完美脱壳组装PE的一般步骤(Obsidium1.3.6.4 DEMO 主程序)

最近学习了天草视频。记录一下笔记。

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头中一些基本选项的修改,以及数据目录的修改 。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值