好久没怎么脱过壳,最近碰到一个软件是用arm加的壳,用到了arm的输入表乱序和远地址跳转,只有找些资料来补课,略有些心得,还望指正。
下面以某软件为例,这个是arm3.70a加的壳,单进程,用了输入表乱序和远地址跳转。某软件已经是双进程CC壳了,还不敢碰,:(
第1部分:准备工作&抵达OEP
先需要找到一些基本数据
1. OEP
设断点he SetProcessWorkingSetSize断下后再设断点he GetCurrentThreadId
断下后,Ctrl+F9返回,去除这两个断点,向下看,看到那个call edi了没,在按F2那里设个断点
断下后,F7跟入,就来到OEP了
记住: OEP=004E8850
2. magicjump
好几个地方修改跳转都可以放置arm破坏输入表的,都可以称之为magicjump
来看arm处理输入表的流程
先将一些预设的api经过处理
然后在读入输入表时候,比较api名称是否为壳中处理过的api名称
如果相同则将该地址指向壳中的函数地址,程序所得到的地址就指向壳里了,importRec就识别不出该
api了
这个虽然代码比较长,不过流程还算清楚,可以跟上几遍就大概知道是怎么回事了
因为相关文章比较多,这里简单看一下
00DC96FD 8D85 B4FEFFFF LEA EAX,DWORD PTR SS:[EBP-14C]
00DC9703 50 PUSH EAX
00DC9704 FF15 C8B0DE00 CALL DWORD PTR DS:[DEB0C8] ;
kernel32.GetModuleHandleA
00DC970A 8B0D C04CDF00 MOV ECX,DWORD PTR DS:[DF4CC0]
00DC9710 89040E MOV DWORD PTR DS:[ESI+ECX],EAX
00DC9713 A1 C04CDF00 MOV EAX,DWORD PTR DS:[DF4CC0]
00DC9718 393C06 CMP DWORD PTR DS:[ESI+EAX],EDI
00DC971B 75 16 JNZ SHORT 00DC9733
00DC971D 8D85 B4FEFFFF LEA EAX,DWORD PTR SS:[EBP-14C]
00DC9723 50 PUSH EAX
00DC9724 FF15 D0B0DE00 CALL DWORD PTR DS:[DEB0D0] ;
kernel32.LoadLibraryA
00DC972A 8B0D C04CDF00 MOV ECX,DWORD PTR DS:[DF4CC0]
00DC9730 89040E MOV DWORD PTR DS:[ESI+ECX],EAX
00DC9733 A1 C04CDF00 MOV EAX,DWORD PTR DS:[DF4CC0]
00DC9738 393C06 CMP DWORD PTR DS:[ESI+EAX],EDI
00DC973B 0F84 AD000000 JE 00DC97EE //magicjump 记录该地址
值
00DC9741 33C9 XOR ECX,ECX
如果是dll可能还需要更改这个地址,od载入dll基地址为10000000,而输入表在该地址以下
造成在ImportRec设不了IAT RVA地址
00DE489D 8B85 1CE8FFFF MOV EAX,DWORD PTR SS:[EBP-17E4] //保存当前IAT指针
00DE48A3 8B8D 80E2FFFF MOV ECX,DWORD PTR SS:[EBP-1D80]
00DE48A9 8908 MOV DWORD PTR DS:[EAX],ECX //一个api地址写入IAT
00DE48AB 8B85 1CE8FFFF MOV EAX,DWORD PTR SS:[EBP-17E4]
00DE48B1 83C0 04 ADD EAX,4
00DE48B4 8985 1CE8FFFF MOV DWORD PTR SS:[EBP-17E4],EAX
对[EBP-17E4]来设内存写断点,可以找到这里
00DE453B 8B8D F0E6FFFF MOV ECX,DWORD PTR SS:[EBP-1910] //保存IAT的首地址,记录
该地址值00E9A880
00DE4541 8D0481 LEA EAX,DWORD PTR DS:[ECX+EAX*4]
00DE4544 8985 1CE8FFFF MOV DWORD PTR SS:[EBP-17E4],EAX //载入下一个dll的IAT中
首地址
3.antidump
OD把原代码中一些代码,jmp较高的地址了 如jmp 03171000,造成lordpe dump文件时候,这段代码丢
失,
该地址是不是固定的。
可以直接下断VirtualAlloc,查看申请地址值,如果比较大就需要注意了,返回后代码类似下面
00DE2612 6A 40 PUSH 40
00DE2614 68 00200000 PUSH 2000
00DE2619 FFB5 70E6FFFF PUSH DWORD PTR SS:[EBP-1990]
00DE261F FF35 3092DF00 PUSH DWORD PTR DS:[DF9230]
00DE2625 FF15 A0B1DE00 CALL DWORD PTR DS:[DEB1A0] ;
kernel32.VirtualAlloc
00DE262B 8985 78E6FFFF MOV DWORD PTR SS:[EBP-1988],EAX
00DE2631 83BD 78E6FFFF 0>CMP DWORD PTR SS:[EBP-1988],0
00DE2638 74 33 JE SHORT 00DE266D
00DE263A 6A 40 PUSH 40
00DE263C 68 00100000 PUSH 1000
00DE2641 FFB5 70E6FFFF PUSH DWORD PTR SS:[EBP-1990]
00DE2647 FF35 3092DF00 PUSH DWORD PTR DS:[DF9230]
00DE264D FF15 A0B1DE00 CALL DWORD PTR DS:[DEB1A0] ;
kernel32.VirtualAlloc
00DE2653 8985 78E6FFFF MOV DWORD PTR SS:[EBP-1988],EAX //保存codesplit 首地
址 在这里断下修改eax值到一较低地址我选择写到arm的adata节
00DE2659 83BD 78E6FFFF 0>CMP DWORD PTR SS:[EBP-1988],0
00DE2660 74 0B JE SHORT 00DE266D
记录CodeSplit addr=00DE2653
4. 用脚本来走到OEP
因为要修改的地方比较多,而且后面我们调试脚本难免要重复工作,所以还是写个od脚本吧,可以节省点体力 :)
因为要向rdata段写IAT数据,所以先将该段内存设为可写
Memory窗口选中该段,右键set access ->full access
//脚本1,直达OEP,顺便处理magicjump和antidump
var NewIatHead
var NewSplitCodeHead
var SetIatHead
var SetSplitCodeHead
var IatOver
var MagicJmp
var OEP
var bSplitCodeOver
var bIatOver
var pTempAddr
var VirtualAlloc
//需要填入的信息内容
mov NewIatHead, 5CA000
mov NewSplitCodeHead, 674000
mov MagicJmp, 00DC973B
mov SetIatHead, 00DE453B
mov IatOver, 00DE498E
mov SetSplitCodeHead, 00DE2653
mov OEP, 004E8850
//变量初始化
mov bIatOver, 0
mov bSplitCodeOver, 0
//获得VirtualAlloc首地址
gpa "VirtualAlloc", "kernel32.dll"
mov VirtualAlloc, $RESULT
BPHWS VirtualAlloc, "x"
run
BPHWC VirtualAlloc
//此时,壳内存代码已分配
//开始设断点
BPHWS MagicJmp, "x" //magicjump之处
//BPHWS 00994704, "x" 写入内存时
BPHWS SetIatHead, "x" //载入输入表首地址时候
BPHWS IatOver, "x" //处理完所有dll
BPHWS SetSplitCodeHead, "x" //申请高位内存处,需更改返回eax为一段低位内存
eoe _Exception
eob _Break
run
//遇例外继续执行
_Exception:
esto
//处理断点中断
_Break:
cmp eip, SetIatHead
je _SetIatHead
cmp eip, MagicJmp
je _MagicJmp
cmp eip, IatOver
je _IATOver
cmp eip, SetSplitCodeHead
je _SetSplitCodeHead
jmp _InvalidBreak
//设置新的IAT首地址
/*
00DE453B 8B8D F0E6FFFF MOV ECX,DWORD PTR SS:[EBP-1910] //保存IAT的首地址
00DE4541 8D0481 LEA EAX,DWORD PTR DS:[ECX+EAX*4]
00DE4544 8985 1CE8FFFF MOV DWORD PTR SS:[EBP-17E4],EAX //当前IAT指针
*/
_SetIatHead:
mov pTempAddr, ebp
sub pTempAddr, 1910 //看上面数据
mov [pTempAddr], NewIatHead
log NewIatHead
BPHWC SetIatHead
run
//修改magicjump, 来得到原始的IAT
_MagicJmp:
mov !ZF, 1 //修改magicjump
run
//maigcjump处理完毕
_IATOver:
BPHWC MagicJmp
BPHWC IatOver
mov bIatOver, 1
cmp bSplitCodeOver,1
je _FixOver
run
//设置新的保存CodeSplit代码的首地址
/*
00DE263A 6A 40 PUSH 40
00DE263C 68 00100000 PUSH 1000
00DE2641 FFB5 70E6FFFF PUSH DWORD PTR SS:[EBP-1990]
00DE2647 FF35 3092DF00 PUSH DWORD PTR DS:[DF9230]
00DE264D FF15 A0B1DE00 CALL DWORD PTR DS:[DEB1A0] ;
kernel32.VirtualAlloc
00DE2653 8985 78E6FFFF MOV DWORD PTR SS:[EBP-1988],EAX //保存antidump首地址
00DE2659 83BD 78E6FFFF 0>CMP DWORD PTR SS:[EBP-1988],0
00DE2660 74 0B JE SHORT 00DE266D
*/
_SetSplitCodeHead:
mov eax, NewSplitCodeHead
mov bSplitCodeOver, 1
BPHWC SetSplitCodeHead
cmp bIatOver,1
je _FixOver
run
//其它未处理的断点
_InvalidBreak:
log eip
msg "Invalid Break"
ret
//IAT,AntiDump处理完毕
//准备跳往OEP
_FixOver:
eoe _Continue
eob _End
BPHWS OEP, "x"
run
_Continue:
esto
_End:
BPHWC OEP
msg "Success!"
ret
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!