脱壳0.upx.exe
① 找OEP
ESP定律
跟脱aspack.exe方法差不多。
脱壳0.exe(FSG 2.0)
① 找oep
单步跟踪
跳转到OEP代码:
观察寄存器,看是否有在获取导入表的代码附近代表快到OEP了。
就能找到 JMP DWORD PTR DS:[EBX+0xC]
② dump内存
。。
③ 修复IAT
FSG 2.0对内存中的IAT 进行了一些空隙填充,需要将其改为0 ,
否则会修复不完全。
进到oep里面,机器码为FF15或者FF25就是跟导入表有关,
右键数据窗口跟随
脱壳2.exe(15PB pack)
① 找OEP
单步跟踪
跟到有个jmp到
GetVersion函数特征
FF15或者FF25后面跟着一般都是导入表
对着004094AC这行右键-数据窗口跟随
然后下硬件写入断点,当程序在往里写数据的时候就会断下,就能找到加密的地方。
② IAT加密,解密IAT
解密IAT需要先找到加密IAT的地方,然后将其删除或是能还原。
找加密IAT代码的方式
① 对IAT设置硬件写入断点
对GetVersion所在的iat设置硬件写入断点,最终可以找到填充IAT的地方
004385EE 8907 MOV DWORD PTR DS:[EDI],EAX ; 填充IAT 函数地址
修改附近代码
修改前
修改后
dump的时候,入口应该用9486.
修复导入表的时候大小可以弄大点。然后再把无效的剪切掉。
② 对IAT相关的API设置断点
LoadLibraryA
GetProcAddress
GetModuleHandleA
VirtualProtect
脱壳中的一些问题
-
拿到程序怎么办?
以脱壳为目的,尽可能完成脱壳
-
技巧的使用?
① API下断注意的问题
API下断之后,第一应该观察的是堆栈,确定返回地址是我们要分析的模块
②注意当前分析的模块是不是主模块
脱壳4.exe(未知壳)
① 找OEP
ESP定律
② IAT加密
这个壳使用一些加密技术:
-
IAT函数地址是不规则的
-
填充IAT时有混淆代码
-
自己实现了一个GetProcAddress
-
字符串使用Hash值来存储
-
壳代码填充IAT的代码是在申请的内存中执行
混淆规则
-
jmp 地址
等价于 call 地址
LEA ESP,DWORD PTR SS:[ESP+0x4]
哈希算法
edx=0, 保存计算之后的结果
LODS BYTE PTR DS:[ESI] TEST AL,AL
JE SHORT 002F1CCB ROL EDX,0x3
XOR DL,AL
手动修改填充IAT代码解密IAT
-
修改代码
① 保存API函数地址到EBX
001D14DC mov dword ptr ds:[ecx+eax-0x4],edx
改为
001D14DC mov ecx,edx
这里的修改为了能将函数地址保存到ecx中,经过测试ecx中的值没有什么用
001D0EE2 mov eax,dword ptr ss:[ebp-0x58]
改为
001D0EE2 mov eax,ecx
这里的修改为了将函数地址保存到eax中,因为最后填充IAT的代码使用的是eax
由于上面代码是在申请出来的地址, 所以有可能是从原程序中拷贝的, 经过搜索,可以在原代码修改
特征:FF 75 D8 E9 AD 00 00 00 C2 04 00 E9 F3 F1 FF FF 8D 64 24 04
② 将原CALL指令改为jmp,增加代码空间
在原代码中修改
③ 在原混淆指令上,添加填充IAT代码
在原代码中修改
寻址OEP解密IAT脚本
// 1. 定义一些必要的变量 VAR vOldOEP VAR vAllocAddr VAR vWriteIATAddr VAR vGetAPIAddr VAR vTmp MOV vOldOEP,0047148B MOV vAllocAddr, 0047A37F MOV vGetAPIAddr, 14E0 MOV vWriteIATAddr,0897 // 2. 对申请空间的地方下断,取出基址 // 3. 设置其他的断点,让程序跑起来,对每一个断点进行处理 BPHWC // 清除硬件断点 BC //清除所有断点 BPHWS vOldOEP, "x" //当执行到此地址时产生中断. BPHWS vAllocAddr, "x" //当执行到此地址时产生中断. LOOP1: RUN CMP vAllocAddr,eip JNZ CASE1 ADD vGetAPIAddr, eax ADD vWriteIATAddr,eax BPHWS vGetAPIAddr, "x" //当执行到此地址时产生中断. BPHWS vWriteIATAddr, "x" //当执行到此地址时产生中断. JMP LOOP1 CASE1: CMP vGetAPIAddr,eip JNZ CASE2 MOV vTmp,edx JMP LOOP1 CASE2: CMP vWriteIATAddr,eip JNZ CASE3 MOV [edx],vTmp JMP LOOP1 CASE3: MSG "到达OEP!"
寻找OEP脚本
// 1. 定义变量 VAR dwWriteIATAddr // 填充IAT地址的下一行地址 VAR dwGetAPIAddr // 获取到了API地址 VAR dwOEP // 原始OEP VAR dwGetBaseAddr // 申请的内存 VAR dwTmp // 临时变量 // 2. 初始化变了 MOV dwOEP, 0047148B MOV dwGetBaseAddr,0047A37F // 0047A37F 0BC>OR EAX,EAX ; eax=申请 的基地址 MOV dwWriteIATAddr,0897 // 001D0895 890>MOV DWORD PTR DS:[EDX],EAX ; 填充IAT // 001D0897 E8 >CALL 001D08D5 //00300895 890>MOV DWORD PTR DS:[EDX],EAX //00300897 E8 >CALL 003008D5 MOV dwGetAPIAddr, 0828 //00300828 ^\E9 >JMP 00300474 ; eax=函数地址 // 3. 清除所有断点 BPHWC BPMC BC // 4. 设置断点 BPHWS dwGetBaseAddr, "x" BPHWS dwOEP, "x" // 5. 循环判断 LOOP0: RUN CMP eip,dwGetBaseAddr JNZ NEXT1 ADD dwWriteIATAddr,eax ADD dwGetAPIAddr,eax BPHWS dwWriteIATAddr, "x" BPHWS dwGetAPIAddr, "x" JMP LOOP0 NEXT1: CMP eip,dwGetAPIAddr JNZ NEXT2 MOV dwTmp,eax JMP LOOP0 NEXT2: CMP eip,dwWriteIATAddr JNZ NEXT3 MOV [edx],dwTmp JMP LOOP0 NEXT3: CMP eip,dwOEP JNZ LOOP0 MSG "到达OEP!"
脚本3
// 1. 定义变量 MOV dwGetAPIAddr, 14DC MOV dwWriteIATAddr, 0895 MOV dwOEP, 0047148B MOV dwGetBase,0047A37F // 2. 初始化环境 BC // 清除软件断点 BPHWC // 清除硬件断点 BPMC // 清除内存断点 BPHWS dwGetBase, "x" BPHWS dwOEP, "x" // 3. 构建循环判断 LOOP0: RUN // 相当于在OllyDbg中按 F9 CMP dwGetBase, eip JNZ case0 ADD dwGetAPIAddr, eax ADD dwWriteIATAddr, eax BPHWS dwGetAPIAddr, "x" BPHWS dwWriteIATAddr, "x" JMP LOOP0 case0: CMP dwGetAPIAddr, eip JNZ case1 // 修改指令 fill dwGetAPIAddr,4,90 asm dwGetAPIAddr, "mov ebx, edx" JMP LOOP0 case1: CMP dwWriteIATAddr, eip JNZ LOOP0 asm dwWriteIATAddr, "mov dword ptr [edx], ebx" // 清除环境 BPHWC dwGetAPIAddr BPHWC dwWriteIATAddr RUN