当前位置: 首页 > news >正文

Win32汇编学习笔记09.SEH和反调试

Win32汇编学习笔记09.SEH和反调试-C/C++基础-断点社区-专业的老牌游戏安全技术交流社区 - BpSend.net
SEH - structed exception handler 结构化异常处理

跟筛选一样都是用来处理异常的,但不同的是 筛选器是整个进程最终处理异常的函数,但无法做到比较精细的去处理异常(例如处理某个函数的异常), 跟 C++ 的 try { } catch { } 的思路一脉相承, SHE 实现的 就是 函数自己 来处理自己的异常,实现方式就是通过回调函数实现的,把回调函数注册给操作系统,当你函数内部出现异常时,系统就会调用你的回调函数,此时就可以处理了,处理完之后可以继续执行代码或者把异常交给筛选器

因此要使用SHE只需要做2件事,1是自己实现异常回调函数,2是吧异常回调函数注册给系统

把函数注册给系统的方式就是 把函数地址 存到 fs:[0] 就可以了

img

可以看到 偏移为0 的位置 是一个异常链 表, , 记录的是一个结构体指针

因此我们需要构造一个结构体 , 把 函数地址 放到 Handler

img

回调函数声明在 msdn 是没有定义,这是微软没有文档化的函数,但是在微软 C 库的 实现用了,可以直接到里面去搜

img

参数: 第一个 异常记录 (异常信息) 第二个 不用管 第3个环境记录 (寄存器环境) 第4个也可以不用管

第2个和第4个是给嵌套异常和展开异常用的

声明

img

第3个和第四个也是给 嵌套异常 和展开异常用的

.586
.model flat,stdcall
option casemap:noneinclude windows.incinclude user32.incinclude kernel32.incincludelib user32.libincludelib kernel32.lib;构造结构体 异常回调函数结构体
EXCEPTION_REGISTRATION_RECORD strucNext dd 0           ;调用这异常回调函数函数结构体指针Handler dd 0        ;当前异常回调函数地址  
EXCEPTION_REGISTRATION_RECORD ends.datag_szF0 db "F0",0g_szF1  db "F1",0.codeassume fs:nothing   ;对fs的类型进行强转;处理 F1 异常的回调函数 
F1Handler proc uses esi pER:ptr EXCEPTION_RECORD, pFrame:dword, pContext:ptr CONTEXT, pDC:dwordinvoke MessageBox, NULL, offset g_szF1, NULL, MB_OK;ExceptionContinueExecution, - 程序继续执行;ExceptionContinueSearch,    - 此异常我不处理,交给其它处理;异常交给F0 的 异常回调函数处理mov eax, ExceptionContinueSearch     ;返回异常处理方式 不然会直接退出ret
F1Handler endp  ;产生异常函数  F1  
F1 procLOCAL @err:EXCEPTION_REGISTRATION_RECORDLOCAL @dwOldSeh:dword    ;原先的过程函数地址;保存调用者的异常回调函数mov eax, fs:[0]mov @dwOldSeh, eax  ;保存调用者回调函数地址 到 next,不然无法找到调用者异常函数处理的地址mov eax, fs:[0]mov @err.Next, eax   ;注册异常回调mov @err.Handler, offset F1Handlerlea eax, @errmov fs:[0], eax;产生异常xor esi, esidiv esi;卸载SEH   还原过程函数(不然 F0 产生的异常会又回来)mov eax, @dwOldSehmov fs:[0], eaxret
F1 endp  ;处理 F0 异常的回调函数
F0Handler proc pER:ptr EXCEPTION_RECORD, pFrame:dword, pContext:ptr CONTEXT, pDC:dwordinvoke MessageBox, NULL, offset g_szF0, NULL, MB_OK;处理 F1 产生的除0异常   assume esi:ptr EXCEPTION_RECORD   ;类型强转mov esi, pER      ;将 异常信息 pER 给  esi  .if [esi].ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO   ;如果是除0异常;处理,跳过产生异常的代码mov esi, pContextassume esi:ptr CONTEXTadd [esi].regEip, 2    ;除0指令是2个字节   regEip 是返回的地址assume esi:nothingmov eax, ExceptionContinueExecution   ;返回异常处理方式 不然会直接退出ret.endifassume esi:nothingret
F0Handler endp;产生异常函数  F0
F0 procLOCAL @err:EXCEPTION_REGISTRATION_RECORDLOCAL @dwOldSeh:dword;保存调用者的异常回调函数mov eax, fs:[0]mov @dwOldSeh, eax;保存调用者回调函数地址 到 next,不然无法找到调用者异常函数处理的地址mov eax, fs:[0]mov @err.Next, eax;注册异常mov @err.Handler, offset F0Handler ;存入异常回调函数地址lea eax, @errmov fs:[0], eaxinvoke F1;产生异常mov eax, 1211hmov [eax], eax;卸载SEHmov eax, @dwOldSehmov fs:[0], eaxret
F0 endpstart:invoke F0xor eax, eaxinvoke ExitProcess,eax
end start

image.png

异常链 : SEH链 尾结点是系统默认的异常函数处理地址

但是我们一般不会像上面写

因为结构体是2成员, 一个是调用者的异常回调函数信息结构体地址 , 一个是自己的异常回调函数地址,都是 4字节

那么我们只需要在栈上 push 2个 dword(2个地址指针) ,就可以了

.586
.model flat,stdcall
option casemap:noneinclude windows.incinclude user32.incinclude kernel32.incincludelib user32.libincludelib kernel32.lib.datag_szF0 db "F0",0g_szF1  db "F1",0.codeassume fs:nothingF1Handler proc uses esi pER:ptr EXCEPTION_RECORD, pFrame:dword, pContext:ptr CONTEXT, pDC:dwordinvoke MessageBox, NULL, offset g_szF1, NULL, MB_OK;ExceptionContinueExecution, - 程序继续执行;ExceptionContinueSearch, - 此异常我不处理,交给其它处理mov eax, ExceptionContinueSearchret
F1Handler endp  F1 proc;注册SEHpush offset F1Handler             ; handler push 自己异常回调函数的地址push fs:[0] ;next                 ;push 调用者异常处理结构体信息地址mov fs:[0], esp                 ;注册回调函数,移位此时esp 存的就是结构体首地址xor esi, esidiv esi;卸载SEHpop fs:[0]          ;把 next 弹回 fs:[0]         add esp, 4          ;平栈,因为自己的异常回调函数地址不需要弹栈,直接丢弃ret
F1 endp  F0Handler proc pER:ptr EXCEPTION_RECORD, pFrame:dword, pContext:ptr CONTEXT, pDC:dwordinvoke MessageBox, NULL, offset g_szF0, NULL, MB_OKassume esi:ptr EXCEPTION_RECORDmov esi, pER.if [esi].ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO;处理,跳过产生异常的代码mov esi, pContextassume esi:ptr CONTEXTadd [esi].regEip, 2assume esi:nothingmov eax, ExceptionContinueExecutionret.endifassume esi:nothingret
F0Handler endpF0 proc;注册异常; | next      | <--esp; | Fohandler | push offset F0Handler ;handler    ;push 自己异常回调函数的地址push fs:[0] ;next                 ;push 调用者异常处理结构体信息地址mov fs:[0], esp                   ;注册回调函数,移位此时esp 存的就是结构体首地址invoke F1;产生异常mov eax, 1211hmov [eax], eax;卸载SEH   pop fs:[0]          ;把 next 弹回 fs:[0]         add esp, 4          ;平栈,因为自己的异常回调函数地址不需要弹栈,直接丢弃ret
F0 endpstart:invoke F0xor eax, eaxinvoke ExitProcess,eax
end start

反调试

一切阻止调试的方法都被称为反调试

在 OD 或者 x32Dbg 中下断点时,他会插入一行代码,但在调试器中看不出来的 那就是把这一行指令在内存的第一个字节改成了 CC (int 3)

img

当 TF 被置位 为 1 时 ,执行一行代码 就会 抛出异常,抛出异常之后就会恢复为 0,因此可以不断通过 改变 TF 位,来判断每一行代码,判断是否被下断点

.586
.model flat,stdcall
option casemap:noneinclude windows.incinclude user32.incinclude kernel32.incincludelib user32.libincludelib kernel32.lib.datag_szCaption db "友情提示",0g_szText  db "你干嘛调试我?",0g_szText2 db "结束了", 0g_ddEnd dd 0        ;函数结束地址.codeassume fs:nothingFuncTest proc;存储mov g_ddEnd, offset ENDTF  ;保存函数结束位置;设置TF标志位  (将值置为1 就会抛异常)pushfdor dword ptr [esp], 100hpopfdxor eax, eaxxor eax, eaxxor eax, eaxxor eax, eaxxor eax, eaxxor eax, eaxxor eax, eaxxor eax, eaxxor eax, eaxxor eax, eaxxor eax, eaxxor eax, eaxxor eax, eaxxor eax, eaxENDTF:retFuncTest endp;异常回调函数,将 TF 置位
F0Handler proc uses esi edi pER:ptr EXCEPTION_RECORD, pFrame:dword, pContext:ptr CONTEXT, pDC:dwordassume esi:ptr EXCEPTION_RECORDmov esi, pERmov edi, pContextassume edi:ptr CONTEXT;判断mov eax, [edi].regEip.if byte ptr [eax] == 0cch   ;指令的第一个字节是CC 说明被调试;被设置断点了invoke MessageBox, NULL, offset g_szText, offset g_szCaption, MB_OKinvoke ExitProcess, 0    ;退出进程.endif;结束mov eax, g_ddEnd.if [edi].regEip == eax    ;程序结束, TF就不需要置位了mov eax, ExceptionContinueExecutionret.endif;继续设置TF标志位or [edi].regFlag, 100hmov eax, ExceptionContinueExecutionassume edi:nothingassume esi:nothingret
F0Handler endpF0 proc;注册异常; | next      | <--esp; | Fohandler | push offset F0Handler ;handlerpush fs:[0] ;nextmov fs:[0], espinvoke FuncTest;卸载SEHpop fs:[0]add esp, 4ret
F0 endpstart:invoke F0invoke MessageBox, NULL , offset g_szText2, NULL, MB_OKxor eax, eaxinvoke ExitProcess,eax
end start

对于部分调试器,他会接收所有异常,这种处理方式就是 把 主要代码放在异常中实现

异常很多时候都被用作反调试

对抗反调试的方法:

把代码分成多块,每块做加密,执行每块代码之前 先进异常还原,还原之后再进异常变成加密状态,因为不解密前面的代码,无法知道后面的代码去哪

处理方法:代码追踪,把执行的每一行代码记录下来

把代码放到堆里面,在堆里面执行完再回到代码区,这样代码追踪就失效了,因为重启地址就变了

相关文章:

Win32汇编学习笔记09.SEH和反调试

Win32汇编学习笔记09.SEH和反调试-C/C基础-断点社区-专业的老牌游戏安全技术交流社区 - BpSend.net SEH - structed exception handler 结构化异常处理 跟筛选一样都是用来处理异常的,但不同的是 筛选器是整个进程最终处理异常的函数,但无法做到比较精细的去处理异常(例如处理…...

[人工智能]CSDN创作助手体验

一、什么是智能体 智能体是一种能够感知环境、学习、推理和行动的实体。它可以是一个计算机程序、机器人或其他类似的系统。智能体的目标是通过与环境的交互来实现特定的任务或目标。 智能体通常由以下几个组件组成&#xff1a; 感知器&#xff1a;感知器是智能体与环境之间的…...

vue3中el-table实现多表头并表格合并行或列

1、el-table中添加事件 :span-method"genderSpanCity" <el-table :span-method"genderSpanCity":data"data.tableData":fit"true" table-layout"fixed" header-align"center" stripestyle"width:100%;he…...

HTML+CSS+JS制作中国传统节日主题网站(内附源码,含5个页面)

一、作品介绍 HTMLCSSJS制作一个中国传统节日主题网站&#xff0c;包含首页、节日介绍页、民俗文化页、节日活动页、联系我们页等5个静态页面。其中每个页面都包含一个导航栏、一个主要区域和一个底部区域。 二、页面结构 1. 顶部横幅区 包含传统中国风格的网站标题中国传统…...

时空笔记:CBEngine(微观交通模拟引擎)

CBEngine 是一个微观交通模拟引擎&#xff0c;可以支持城市规模的道路网络交通模拟。CBEngine 能够快速模拟拥有数千个交叉路口和数十万辆车辆的道路网络交通。 以下内容基本翻译自CBEngine — CBLab 1.0.0 documentation 1 模拟演示 1.0 模拟演示结构 config.cfg 定义了 roa…...

【LeetCode】力扣刷题热题100道(26-30题)附源码 轮转数组 乘积 矩阵 螺旋矩阵 旋转图像(C++)

目录 1.轮转数组 2.除自身以外数组的乘积 3.矩阵置零 4.螺旋矩阵 5.旋转图像 1.轮转数组 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 class Solution { public:void rotate(vector<int>& nums, int k) …...

【C++】字符串的 += 和 + 运算详解

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;1. 字符串的 和 基本用法1.1 的用法1.2 的用法 &#x1f4af;2. 示例代码的剖析与解释代码分析 &#x1f4af;3. 底层实现与性能分析3.1 的实现原理3.2 的实现原理3.…...

多模态大模型部署:结合dify

文章目录 前言minicpm-vDify测试一下总结部署过程回顾集成与测试实验结果分析展望未来 前言 上回说道&#xff0c;我们用ollama部署了一个多模态的大模型&#xff0c;也就是minicpm-v&#xff1a; 但这玩意儿感觉只能打字啊。 怎么给它发图片呢&#xff1f; minicpm-v Mini…...

Matlab Steger提取条纹中心(非极大值抑制)

文章目录 一、简介二、实现代码三、实现效果一、简介 由于在确定条纹的ROI区域之后,会计算出多个条纹中心坐标,因此这里就需要对其进行则优选择,毕竟条纹只有一条,这最简单的方式就是使用非极大值抑制,即选择每一行/列最好的条纹中心。 二、实现代码 Hessian2D.m function…...

springboot + vue+elementUI图片上传流程

1.实现背景 前端上传一张图片&#xff0c;存到后端数据库&#xff0c;并将图片回显到页面上。上传组件使用现成的elementUI的el-upload。、 2.前端页面 <el-uploadclass"upload-demo"action"http://xxxx.xxx.xxx:9090/file/upload" :show-file-list&q…...

LabVIEW 系统诊断

LabVIEW 系统诊断是指通过各种工具和方法检测、评估、分析和解决 LabVIEW 程序和硬件系统中可能存在的故障和性能问题。系统诊断不仅涵盖软件层面的调试与优化&#xff0c;还包括硬件交互、数据传输、实时性能等方面的检查和分析。一个成功的系统诊断能够显著提升LabVIEW应用程…...

韩国机场WebGIS可视化集合Google遥感影像分析

目录 前言 一、相关基础数据介绍 1、韩国的机场信息 2、空间数据准备 二、Leaflet叠加Google地图 1、叠加google地图 2、空间点的标记及展示 3、韩国机场空间分布 三、相关成果展示 1、务安国际机场 2、有同类问题的机场 四、总结 前言 12月29日8时57分左右务安国际机…...

springCloudGateWay使用总结

1、什么是网关 功能: ①身份认证、权限验证 ②服务器路由、负载均衡 ③请求限流 2、gateway搭建 2.1、创建一个空项目 2.2、引入依赖 2.3、加配置 3、断言工厂 4、过滤工厂 5、全局过滤器 6、跨域问题...

使用new Vue创建Vue 实例并使用$mount挂载到元素上(包括el选项和$mount区别)

new Vue({...}) 是创建一个新的 Vue 实例的方式。你可以通过传递一个选项对象来配置这个实例。常见的选项包括&#xff1a; •data&#xff1a;定义组件的数据属性。 •el&#xff1a;指定 Vue 实例应该挂载到哪个 DOM 元素上&#xff08;通常是一个选择器字符串&#xff0c;如…...

GTX750Ti打DP补丁

背景 咸鱼收了一个二手的GTX750Ti,用于4K60Hz显示器,HDMI接口勉强可以4K60Hz,不过色彩和帧率都不是太正常,理论上它的HDMI接口是不支持的,原本也是打算用DP接口接显示器的,但是发现接DP口之后无法通过bios的vga检测最终一直重启,在华硕B760-K的BIOS中使能CSM是可以使用…...

springmvc前端传参,后端接收

RequestMapping注解 Target({ElementType.METHOD, ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) Documented Mapping public interface RequestMapping {String name() default "";AliasFor("path")String[] value() default {};AliasFor(&quo…...

PyTorch 张量的分块处理介绍

分块处理是将大型张量分解成较小的块&#xff0c;以便更高效地进行计算&#xff0c;减少内存占用&#xff0c;特别适用于处理超大张量的场景&#xff08;如深度学习中的大批量数据或大型模型训练&#xff09;。 PyTorch 提供了多种方法来分块张量&#xff0c;包括 chunk、spli…...

在Ubuntu中使用systemd设置后台自启动服务

引言 在Ubuntu系统中&#xff0c;systemd 是一个非常强大的系统和服务管理器。它不仅负责系统的启动和初始化&#xff0c;还可以帮助我们管理各种后台服务。通过使用 systemd&#xff0c;我们可以轻松地设置服务在系统启动时自动运行&#xff0c;并且能够方便地管理服务的启动…...

mongodb清理删除历史数据

批量清理mongodb历史数据 清理程序的原来 目前项目组上很多平台上线历史数据积压&#xff0c;导致入库查询数据缓慢&#xff0c;历史数据有些已经归档&#xff0c;进行历史数据清理删除。 之前临时写shell脚本&#xff0c;太简陋&#xff0c;重新使用Python进行改造&#xff0c…...

C++字体库开发之字体回退策略十六

回退表 { "blocks": [ "UBLOCK_BASIC_LATIN", ], "font": { "family": "Noto Sans SC", "style": [ { "name": "Thin", …...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...