最近学习了天草视频。记录一下笔记。
Obsidium1.3.6.4DEMO版本主程序下载地址:http://download.csdn.net/detail/whatday/5244425
前期准备工作:
这里主要记录的完美脱壳的PE组装的一些步骤,脱壳部分用脚本带过。
一般脱壳的OD脚本如下:(跳过脚本)
/* repair Obsidium 1.3.6.4 main program function: 1.Go to OEP 2.Fix IAT 3.Fix stolen OEP 4.Fix two type the encrypted code 5.Fix SDK API */ var iatStart var iatAddr var iatKey var iatValue var resApiBk var resApiBk1 var resApiBk2 var sysApiBk var number var oepAddr var dataBase var encrypteAddr mov iatStart, 00581B96 ; The code here because of different computer // This variable is used for the reduction treatment the third type special API xor number,number // Set VirtualProtect breakpoint gpa "VirtualProtect", "kernel32" find $RESULT, #E9??????# gci $RESULT, DESTINATION gci $RESULT, DESTINATION find $RESULT, #E8??????# cmp $RESULT, 0 jz err5 bp $RESULT mov sysApiBk,$RESULT // Set the code section attributes loop0: esto cmp [esp + c], 1 jnz loop0 mov [esp + c], 40 esto findmem #66623A432B2B484F4F4B# cmp $RESULT,0 jz err6 find $RESULT, #E8??????# cmp $RESULT,0 jz err7 bprm $RESULT,1 mov oepAddr,$RESULT loop0_1: esto cmp eip,$RESULT jnz loop0_1 //Set noraml api breakpoint findmem #0FB746026A01506A00FF7604FF37FF535485C0# cmp $RESULT, 0 jz err1 mov resApiBk,$RESULT add resApiBk,e bp resApiBk // 0f 84 je set breakpoint mov resApiBk1,resApiBk sub resApiBk1,60 find resApiBk1, #0f84????# cmp $RESULT,0 jz err3 cmp $RESULT,resApiBk jae err3 gci $RESULT,DESTINATION bp $RESULT // 75 jnz set breakpoint mov resApiBk1,resApiBk sub resApiBk1,40 find resApiBk1, #75??????# cmp $RESULT,0 jz err2 cmp $RESULT,resApiBk jae err2 gci $RESULT,DESTINATION find $RESULT,#85c0????# cmp $RESULT, 0 jz err4 bp $RESULT mov resApiBk2, $RESULT // 0F 85 jnz set breakpoint mov resApiBk1,resApiBk sub resApiBk1,40 loop1: find resApiBk1, #0F85????# cmp $RESULT,0 jz next1 cmp $RESULT,resApiBk jae next1 mov resApiBk1, $RESULT gci resApiBk1,DESTINATION bp $RESULT add resApiBk1,2 jmp loop1 next1: // Set last 0f 85 breakpoint, becase it is a special breakpoint sub resApiBk1,2 gci resApiBk1,DESTINATION bc $RESULT gci $RESULT, DESTINATION mov resApiBk1,$RESULT add resApiBk1,3 bp resApiBk1 // Cycle to restore an API mov iatAddr, iatStart loop2: add iatAddr,6 cmp [iatAddr], 0 jz fun3 cmp [iatAddr], #FF25#, 2 jz next3 sub iatAddr, 5 jmp loop2 next3: mov iatKey, [iatAddr+2] gci iatAddr,DESTINATION mov iatValue, $RESULT //Fix noraml api mov eip,iatValue cmp [eip], #60#, 1 jnz next5 esto cmp eip,resApiBk jnz next2 sto mov [iatKey],eax jmp loop2 //Fix first type special API next2: cmp eip,resApiBk2 jnz next4 mov [iatKey],eax jmp loop2 // Fix second type special API next4: cmp eip,resApiBk1 jnz loop2 cmp eax,0 jnz case2 gpa "VirtualQuery", "kernel32.dll" jmp default case2: cmp eax,2 jnz case3 gpa "FreeResource", "kernel32.dll" jmp default case3: cmp eax,3 jnz case4 gpa "ExitThread", "kernel32.dll" jmp default case4: cmp eax,4 jnz loop2 gpa "ExitProcess", "kernel32.dll" default: mov [iatKey], $RESULT jmp loop2 // Fix third type special API next5: cmp [eip], #B8#, 1 jnz test4 inc number cmp number,1 jnz test1 gpa "GetCommandLineA", "kernel32.dll" jmp default2 test1: cmp number,2 jnz test2 gpa "GetCurrentProcessId", "kernel32.dll" jmp default2 test2: cmp number,3 jnz test4 gpa "GetVersion", "kernel32.dll" jmp default2 test4: cmp [eip], #55#, 1 jnz loop2 gpa "lstrlenA", "kernel32.dll" default2: mov [iatKey], $RESULT jmp loop2 // Repair stolen OEP fun3: sub oepAddr,10 mov [oepAddr], #A18B904700C1E002A38F904700526A00# findmem #426F726C616E6420432B2B# cmp $RESULT,0 jz ret8 mov dataBase,$RESULT mov [oepAddr+1],dataBase+8b mov [oepAddr+9],dataBase+8f mov eip,oepAddr + 10 sub oepAddr, 13 mov [oepAddr], #90EB10#, 3 add oepAddr, 1 //clear breakpoints for the next run clean: bpmc bc // Decrypting the encrypted code mov encrypteAddr,401000 loop: inc encrypteAddr find encrypteAddr,#68????????FF15F2006500# cmp $RESULT,0 je next7 mov encrypteAddr,$RESULT mov eip,encrypteAddr bphws eip+B, "x" esto fill encrypteAddr,B,90 bphwc jmp loop next7: mov encrypteAddr,401000 loop4: inc encrypteAddr find encrypteAddr,#68????????FF15FA006500# cmp $RESULT,0 je next8 mov encrypteAddr,$RESULT fill encrypteAddr,B,90 jmp loop4 // Fix the second type for encrypted code next8: mov encrypteAddr,401000 loop5: inc encrypteAddr find encrypteAddr, #68????????E8# cmp $RESULT,0 je next9 mov encrypteAddr,$RESULT mov number, [encrypteAddr+1] add number,a cmp number,1000 ja loop5 add number,encrypteAddr cmp [number],#EB08#,2 jnz loop5 mov number, [encrypteAddr+1] add number, 5 mov [encrypteAddr], #E9#, 1 mov [encrypteAddr+1],number jmp loop5 // Fix SDK API next9: add iatStart,6 /* jmp 005829B8 nop mov eax, 0x1 retn retn nop nop nop nop nop */ mov [iatStart], #E9170E000090B801000000C3C39090909090# gci iatStart, DESTINATION /* cmp dword ptr [esp], 00403063 jnz L007 mov dword ptr [esp+0x38], 0x7373694B mov dword ptr [esp+0x3C], 0x70555B79 mov dword ptr [esp+0x40], 0x5D4B mov edx, 00583034 retn 0x8 L007: mov dword ptr [esp+0x18], 0x2E777777 mov dword ptr [esp+0x1C], 0x61706E75 mov dword ptr [esp+0x20], 0x632E6B63 mov word ptr [esp+0x24], 0x6E mov edx, 00583034 retn 0x8 */ mov [$RESULT], #813C24633040007520C74424384B697373C744243C795B5570C74424404B5D0000BA34305800C20800C74424187777772EC744241C756E7061C7442420636B2E6366C74424246E00BA34305800C20800# // Output log and positioning OEP eval "OEP地址:{oepAddr}" log $RESULT mov eip, oepAddr jmp ret1 err1: msg "没有找到模拟api的代码" jmp ret1 err2: msg "模拟api的代码已经改变" jmp ret1 err3: msg "模拟api的代码已经改变2" jmp ret1 err4: msg "模拟api的代码已经改变3" jmp ret1 err5: msg "定位VirtualProtect错误" jmp ret1 err6: msg "没有找到BC的特征码" jmp ret1 err7: msg "没有找到BC的入口的CALL" jmp ret1 err8: msg "没有找到数据段" jmp ret1 err9: msg "修复加密代码错误" jmp ret1 ret1: ret
运行此脚本后 OD停在OEP处
第一步:
此时用LordPE Deluxe dump 得到dumped.exe文件 在dump之前先要修复一下镜像大小,这也是anti dump的一种方法。
修复大小
dump
这里查看一下区段信息
发现资源段还存在这里可以备份一份文件,后边修复资源使用。
如果是一般脱壳到这里只需要修复IAT就完成了。接下来是找一个相同编译器的生成的无壳程序,把它的PE头覆盖复制到dump文件上 ,这里是BC程序所以找一个BC程序
第二步:
用C32Asm打开dump文件和无壳的BC程序 复制无壳程序PE头道dump文件
点击按钮是 然后保存
这个时候发现dump程序的图标不显示了
这是因为资源段找不到,本质就是PE头和PE节不匹配造成的,因为要完美脱壳,所以必须在原本的PE头基础上来拼装各个节表,这样才算完美。
这个时候用LordPE查看一下节表信息
可以看出各个节表都有了,这点和无壳程序完全一样了,但是这只是PE头,具体对应的节表还需要修复。
第三步:
接下来通过跟踪 VirtualProtect函数 可以得到原始的节区段信息,这个是OB的一个特点,其他壳不知道。
重新载入DEMO程序,bp VirtualProtect 循环shift+F9 得出原始区段信息
可以看出 第一张和最后一张不在范围了 所以实际上得到4个区段 转换为RVA分别是
001000 - 181A00 183000 - 31200 1B9000 - 200 1BB000 - 3C00
原本的头文件区段应该有
.text .data .tls .rdata .idata .edata .rsrc .reloc
通过无壳的BC文件发现 .tls .rdata段一般都为1000 在回想下前边修复IAT的过程 IAT比较零散比较大,可以得到结论,3C00不是.rdata段的大小,应该是.idata段的大小 .rdata段和.edata没有出现 所以我们自己添加剩下的段,只有资源段和重定位段 资源段需要专门修复 重定位段可以随便添加。又由于PE头文件中一般设定的内存块对齐是1000h。这里得出的区段信息如下:
001000 - 182000 .text 183000 - 36000 .data 1B9000 - 1000 .tls 1BA000 - 1000 .rdata 1BB000 - 4000 .idata 1BF000 - 4000 .edata 1C3000 - 未知 .rsrc 未知 .reloc
第四步:
通过LordPE修改dump各区段信息
需要注意2点,第一点各个段的属性 可以改成E0000060(可读 可写 可执行 包含执行代码 包含初始化数据) 这样排除因为权限造成的错误 在脱壳最后还需要根据无壳程序改回来。第二点 修改地址大小的时候 一定要把VirtualAddress VirtualSize RwaOffset RwaSize都修改了,因为每个区段在磁盘文件中和虚拟内存上都需要正确的信息,这点我先前没有修改RwaOffset 后来造成数据错位,后来想想,既然是全部自己拼装PE,当然内存映射的 和 磁盘的都需要考虑到。
由于资源段和重定位段要自己修复添加 所以这里先删除
第五步:
导出表,导入表,资源表,TLS都需要我们自己修复 所以先清空数据目录
PE头清空以后 对应的区段也需要清空 这里用的是PE Tools
把.tls .rdata .idata .edata 全部00填充 在相应的节鼠标右键Fill section 00填充
到现在清空了数据,后边修复数据就不会被其他数据影响了
第六步:
修复IAT 需要注意的是 平时都是选了Add new section这个选项的 但是这里的IAT都在的区段我们已经准备好了 而且清空了所以这个不能勾选此选项
RVA填入1BB000(1BB000 - 4000 .idata)
修复后用LordPE查看一下 输入表信息 看是否正确
第七步:
修复资源表 使用DT FixResource 把前边的dump备份文件拖进去
NewRVA填入1C3000(1C3000 - 未知 .rsrc)由于我们的PE文件头设置的文件块对齐是200 所以这里也填200 然后 Dump Resource
在LordPE中load section from disk 选中刚才保存的资源段
修改PE数据目录的资源段信息 并验证
资源段的大小可以通过导入资源段后查看得到
返回PE头信息窗口 看看还有什么需要修改的 发现BaseOfData还有问题 修改成我们先前得到的00183000
保存后 刷新一下可以看到有图标了
这个时候在win7运行可以正常了 但是在XP有可能还需要修复TLS 现在继续修复TLS
第八步:
TLS的修复需要参照无壳程序和加壳的源程序来看 以OD本身为例 通过TLS和区段目录可以发现
DataBlockStartVA就是tls段的起始地址 DataBlockEndVA=DataBlockStartVA+9C
注意点1:这里的9C不是固定的 是编译器根据程序的不同而生成的 所以这里需要参考加壳的源程序
显然加壳程序中的大小比9C大 如果这里填入9C 程序也可以正常运行但是就会出现 退出时错误,
原因跟踪发现,就是 线程局部存储 块比程序中设计的小了,原程序中的各个程序设计都是按照CC来做的
比如memcpy设定的长度等等,如果是9C 这样一来就会造成数据的错误,从而在退出程序清理内存时出现错误。
继续来定位TLS各项
IndexVariableVA是OEP指向的指令的内存寻址值
这里是0x58308B
CallBackTableVA=下一个区段起始地址+10=5BA000+10=5BA010
总结一下tls的值为
tls 5B9000 5B909c 58308B 5BA010修改dump文件的tls
运行下程序 可以运行了 PEID查看一下
发现有附加数据段 用File Format Identifier去除附加数据段
在用PEID看一下
第九步:
到目前为止还需要添加一个重定位段和修改各个段的属性 以及 块对齐 这样可以减少PE文件大小
重定位可以随便添加一个 但是需要注意用Import REConstructor添加段 只是在PE头中添加 不会有相应的磁盘段对应 可以利用上一个段的剩余 也可以用其他工具修改下
各个段属性的修改就是比对无壳程序进行的 。
块对齐使用的是 PE Optimizer 工具 选择我们的文件 点process
可以看出来 大小缩小了2%
对比下先前和现在文件的大小
的确有变化
这个时候运行发生错误 通过OD跟踪发现 call 49af5c 造成的 但是49af5c为00 初步怀疑是PE Optimizer优化造成的,
实验发现是 kill Relocation造成的。LordPE加载未优化的文件
把重定位RVA+400000=496000 再观察节表
显然这个重定位地址是在代码段,PE Optimizer的优化恰好把代码清0了,
回想下重定位信息是先前我们复制PE头自带的 我们没有修改它 也没有清除它
这里把重定位信息清除掉 在优化可以运行了。
以上步骤完成后 在关闭脱壳文件任然要报错,还需要修复。通过 注意点1 解决了错误。
总结一下:PE的拼装关键就是要把PE头和各个区段分开看,PE头和区段一一对应,然后还有PE头中一些基本选项的修改,以及数据目录的修改 。