Anti-Attach 与修正镜像大小
在不方便使用调试器启动程序的时候,可以先运行目标程序,再使用调试器附加到目标进程;
Ring3 调试器附加使用 DebugActiveProcess 函数,在附加相关进程时,会先执行 ntdll.dll 下的 ZwContinue 函数,最后停留在 ntdll.dll 的 DgbBreakPoint 处;
事实上,调试器会在此处设置一个 INT3 断点,然后由调试器自己来捕获;
使用工具
- OllyDbg 1.10原版,简称
OD
; OD
汉化
和插件
均来自互联网;- UnPackMe来自互联网,仅供学习使用;
- OEP 检索工具为 PEiD,来自互联网;
- 进程管理工具为Estricnina,来自互联网;
- 反 Anti-Attach 工具为 POKEMON_AntiAttach;
- Dump 工具为 OD 插件 OllyDump,来自互联网;
- IAT 重建工具为 ImportREC,来自互联网;
- PE 重建工具为LoadPE,来自互联网;
- 文中特殊数字均是
HEX
,为了书写方便采用DEC
;
操作流程
运行 CM 后虚拟机变得非常卡,打开任务管理器会发现当前 CM 的 CPU 占用率为 99%;
打开 OD,在文件菜单选择附加 CM 后,会提示无法附加:
不过这个 CM 的 Anti-Attach 好像不够完善,在弹出所有异常弹窗后,还是会附加成功;
同时查看堆栈窗口,可以看到调用来自 DgbBreakPoint:
并且函数的首行指令被替换为 INT3;
F7 单步执行,程序返回到调用的下一行:
这里是 DbgUiRemoteBreakin 函数内部,为了使运行中进程能够即时中断到调试器中,操作系统提供了函数 DbgUiRemoteBreakin,其内部通过调用 DbgBreakPoint 产生一个中断异常从而被调试器捕获;
为了实现及时中断,需要在运行中的进程中创建远程线程,线程回调函数就是 DbgUiRemoteBreakin 函数,最后的 RtlExitUserThread 就是结束远程线程;
使用 LoadPE 的 PE 编辑器打开程序,收集程序的重要信息:
基址是:00400000,镜像大小是:6000,代码段 RVA:10000,数据段 RVA 是:2000,不过 NumberOfRvaAndSize 肯定是不对的,应该是 10(10 进制的 16);
接着使用 PEiD 自带的插件 GenOEP 获取程序的 OEP:
OEP 为 401000,处于 LoadPE 获取的代码段区间,应该是正确的;
信息收集完成,运行 CM,然后使用 Estricnina 挂起 CM 的所有线程:
暂停 CM 的线程只是为了让 CPU 占用率降低,使虚拟机用起来不是那么的卡;
接着,使用 POKEMON_AntiAttach 绕过程序的反附加:
使用 LoadPE 定位到 CM 的进程,修正进程的映像尺寸:
PE 的 IMAGE_OPTIONAL_HEADER 结构体中有个字段叫 SizeOfImage,作用是指出 PE 文件在载入内存后的总尺寸,还有个字段叫 ImageBase,表示 PE 文件在内存中的首选载入地址;
在 Dump 文件时,一些关键参数是通过 MODULEENTRY32 结构的快照获取的,因此可以通过在 modBaseSize 和 modBaseAddr 字段中填入错误的值,让 Dump 软件无法正确读取进程中的数据,但由于修改 modBaseAddr 会使系统出现问题,因此只能修改 modBaseSize 的值;
某些壳在做 Anti-Dump 的时候,会对内存中的 SizeOfImage 大小进行修改,通常是改得很小,这样,Dump 工具读取当前值后获取的就会是一个映像尺寸不对的无用文件;
所以,所谓的修复镜像大小,就是读取磁盘文件的 SizeOfImage 与内存中的 SizeOfImage 进行大小比对,然后利用文件中的值进行修正;
接着,使用 OD 附加当前进程,程序中断在 ntdll.DbgBreakPoint 函数中,然后 goto 到 OEP:
可以看到,由于当前程序处于运行状态,所以代码段已经解码了;
设置 EIP 为 OEP,即让 CPU 即将执行的指令指向 OEP:
然后跟随任意调用,获取 IAT 相关信息:
IAT 起始 RVA 为:2000,Size 为:1C;
使用 OllyDump 将内存数据 Dump 下来:
使用 ImportREC 获取并重建 IAT:
直接运行修复后的程序,弹出错误弹窗:
将修复后的程序拖入 LoadPE,重建 PE:
导入 OD 并运行程序,完美运行: