①OEP
②填充IAT地址
③获取API地址
将地址填入脚本,进行测试
一般来说,地址随机又两种情况,一是随机基址,二是代码所在处是在申请的内存空间中,这种情况下解决方法就是找到代码基址,然后计算偏移,根据偏移在代码处下断点。
显然这个地方地址随机是因申请了内存导致的。
所以可以在VirtualAlloc处下断点,经过动态调试,发现在VirtualAlloc处断下的位置有很多出,最开始的一处栈回溯之后,代码地址是程序的模块中,推测这个地方申请的内存空间就是修复IAT代码的基地址,将之前的代码偏移,减去基址之后加上偏移,代码与之前一样,所以这个地方就是获取代码基址的地方。
分析后故修改脚本如下:
先获取以下基地址,在我们已知的填充IAT和获取API的地址下硬件执行断点
程序断下后,发现EAX中保存的是API地址
设置RUN跟踪,让EIP等于填充IAT的时候暂停,Ctrl+F7自动步入
查看RUN跟踪
重点关注7开头的数据,可能就是一个AIP地址
直到这个位置EDX中仍然保存的是API地址
解密思路:将API地址保存到某一个不用的寄存器中,然后在填充IAT的时候,把IAT直接填到EDX指向的内存中
修改代码:
下断点运行
DUMP文件并使用IMPREC修复
将手工操作转为脚本
将程序载入OD,先查看程序入口,发现有标准的push指令(pushad/pushfd),故采用ESP定律寻找OEP
使用ESP定律单步至470A036,就对ESP下断点,然后将程序运行起来
程序断在popfd下面,之后一般单步几下就可以到达OEP
到达OEP,根据经验可以看出这应该是VC6.0或者易语言程序,向下看可以发现第一个调用的函数CALL[XXX]中的函数地址被加密了,即IAT被加密了
而解密IAT的一般方法就是在IAT函数表上下硬件写入断点。
观察OEP附件的函数调用,根据经验来说,VC6.0程序调用的第一个函数应该是GetVersion,但现在能看到的是一个随机的函数地址,像是在申请内存地址
F7步入,继续单步跟踪275039地址中的代码,可以发现,原本IAT的函数地址被存放到了一个地址中,代码通过计算地址,获取到了GetVersion的函数地址,之后调用了GetVersion函数
程序的加密函数,GetVersion
生成加密的IAT函数大概步骤如下:
1.获取原始IAT函数地址,存放在一定位置
使用LoadLibraryA/W,GetProcAddress函数
2.申请空间,构造新的IAT函数
使用VirtualAlloc申请空间,拷贝代码
3.根据原始IAT函数地址计算加密值,隐藏真实地址
计算类似GetVersion函数中的代码中的x值
sub edx,x;
add ebc,x;
4.将新IAT函数地址写入IAT
填充地址到IAT
加密函数
根据推测以及对壳shell部分IAT的操作,我们大致可以推出,无论加密不加密IAT,壳其实都会填充IAT,只是加密IAT会填充加密之后的函数。
所以现在只要能找到加密前IAT函数地址以及填充IAT的地方,并且能够在填充IAT时将加密前的函数地址写入,那IAT就相当于完成了解密。
故我们接下来分析的两个关键点就是写入IAT的地方和加密前IAT函数地址出现的地方,对这两个关键点进行破解和解密即可。
综上所诉,下面就从写入IAT的地方开始分析。
首先,在原始OEP处,GetVersion函数的IAT处下写入断点,重新运行程序。
程序断下,找到了填充IAT的位置
一般来说,写入IAT的地方应该是一个循环,在这个循环中应该包括加载模块、获取函数地址等操作。
所以我们可以在LoadLiibraryA/W和GetProcAddress两个函数上下软件断点,在写入IAT处下一行代码下硬件执行断点。
注:壳中的代码一般都是解压、解密出来的,一般地址不可靠
继续单步跟踪分析,发现代码计算出了一个地址,从这个地址获取了一个4字节的数,且没有规律,一般来说,这种值就是hash值了。
继续单步跟踪分析,发现其获取了Kernel32模块基地址
继续单步跟踪分析,访问了数据目录表
继续单步跟踪分析,又发现获取了导出函数字符串,结合上下文分析,推测代码是在获取导出函数字符串,求字符串的hash值,再与刚才获取的hash值进行对比。
继续跟踪发现程序加载了函数字符串的每一个字节,并且进行了计算
程序求函数字符串的hash函数
当计算完hash值后,会进行比较
直接在1A28的位置下断点,运行至此,即hash相等时情况
继续单步跟踪分析,找到了获取函数地址的地方
继续单步跟踪分析,发现函数地址被处理,使用memcpy拷贝出了一段代码,函数地址被写入到了代码中。而新的函数地址就是memcpy拷贝的首地址,这个地址被写入到了IAT中。
至此,我们已经知道程序在写入一个IAT函数地址时的操作过程,概括为以下步骤。
1.获取预先计算好的hash值
2.循环获取当前正在获取的模块中的导出函数名称,计算hash值,与预存的比较,如果失败继续循环获取
3.如果正确,获取导出函数的地址
4.拷贝预存的代码到缓冲区,将导出函数地址写入到缓冲区中
5.将缓冲区首地址写入IAT处,完成填充IAT的操作
如何解密IAT?此处从函数地址入手,如果当我们获取了原始函数地址,且在写入IAT时,寄存器中还保存的是原始函数地址,那解密IAT就会变得很容易完成。如果代码是线性执行,我们只需改一下跳转应该就可以完成了,但是现在代码混淆度比较高,比较难找到规律,虽然说只要足够耐心,更改跳转应该可以实现,仔细跟踪代码,可以发现其实函数地址最初保存在EAX中,而后保存在EDX中,之后EDX被修改为IAT地址,EAX修改为加密的地址,在这个过程中,只要我们能做到EAX最后是函数地址即可。
经过分析,修改两处代码即可。
第一处代码:
这里的修改是为了将函数地址保存到EBX中,因为EBX看起来没有实际使用用处
第二处代码:
这里的修改为了将函数地址保存到EAX中,因为最后填充IAT的代码使用的是EAX
这里也可以改为脚本,与上文方法3类似,更改地址与部分代码即可
尝试直接修改壳代码
8D642404895401FC
在内存窗口中搜索,定位到地址
计算另一个地址
地址=0047BB70-(002214DC-00220895)
(对0047BB70 和0047AF29 两个地址下硬件执行断点)
修改为
同样可脚本。。。
/
/
1.
定义变量
MOV dwOEP,
0047148B
MOV dwGetAPI,
001E1914
MOV dwWriteIAT,
001E0897
/
/
2.
清除环境
BC
/
/
清除所有软件断点
BPHWC
/
/
清除所有硬件断点
BPMC
/
/
清除内存断点
/
/
3.
设置断点
BPHWS dwOEP,
"x"
/
/
当执行到此地址时产生中断.
BPHWS dwGetAPI,
"x"
/
/
当执行到此地址时产生中断.
BPHWS dwWriteIAT,
"x"
/
/
当执行到此地址时产生中断.
/
/
4.
循环
LOOP0:
RUN
/
/
F9
CMP
dwGetAPI,eip
JNZ CASE1
MOV dwTMP,eax
JMP LOOP0
CASE1:
CMP
dwWriteIAT,eip
JNZ CASE2
MOV [edx],dwTMP
/
/
MOV DWORD PTR DS:[EDX],EAX
JMP LOOP0
CASE2:
CMP
dwOEP,eip
JNZ LOOP0
MSG
"OEP已到达"
/
/
1.
定义变量
MOV dwOEP,
0047148B
MOV dwGetAPI,
001E1914
MOV dwWriteIAT,
001E0897
/
/
2.
清除环境
BC
/
/
清除所有软件断点
BPHWC
/
/
清除所有硬件断点
BPMC
/
/
清除内存断点
/
/
3.
设置断点
BPHWS dwOEP,
"x"
/
/
当执行到此地址时产生中断.
BPHWS dwGetAPI,
"x"
/
/
当执行到此地址时产生中断.
BPHWS dwWriteIAT,
"x"
/
/
当执行到此地址时产生中断.
/
/
4.
循环
LOOP0:
RUN
/
/
F9
CMP
dwGetAPI,eip
JNZ CASE1
MOV dwTMP,eax
JMP LOOP0
CASE1:
CMP
dwWriteIAT,eip
JNZ CASE2
MOV [edx],dwTMP
/
/
MOV DWORD PTR DS:[EDX],EAX
JMP LOOP0
CASE2:
CMP
dwOEP,eip
JNZ LOOP0
MSG
"OEP已到达"
/
/
1.
定义变量
MOV dwOEP,
0047148B
MOV dwGetAPI,
1914
MOV dwWriteIAT,
0897
MOV dwBase,
0047A37F
/
/
2.
清除环境
BC
/
/
清除所有软件断点
BPHWC
/
/
清除所有硬件断点
BPMC
/
/
清除内存断点
/
/
3.
设置断点
BPHWS dwOEP,
"x"
/
/
当执行到此地址时产生中断.
BPHWS dwBase,
"x"
/
/
当执行到此地址时产生中断.
/
/
4.
循环
LOOP0:
RUN
/
/
F9
CMP
dwBase,eip
JNZ CASE0
ADD dwGetAPI,eax
/
/
加上基地址
ADD dwWriteIAT,eax
/
/
加上基地址
BPHWS dwGetAPI,
"x"
/
/
下断点
BPHWS dwWriteIAT,
"x"
/
/
下断点
BPHWC dwBase
/
/
清除硬件断点
JMP LOOP0
CASE0:
CMP
dwGetAPI,eip
JNZ CASE1
MOV dwTMP,eax
JMP LOOP0
CASE1:
CMP
dwWriteIAT,eip
JNZ CASE2
MOV [edx],dwTMP
/
/
MOV DWORD PTR DS:[EDX],EAX
JMP LOOP0
CASE2:
CMP
dwOEP,eip
JNZ LOOP0
MSG
"OEP已到达"
/
/
1.
定义变量
MOV dwOEP,
0047148B
MOV dwGetAPI,
1914
MOV dwWriteIAT,
0897
MOV dwBase,
0047A37F
/
/
2.
清除环境
BC
/
/
清除所有软件断点
BPHWC
/
/
清除所有硬件断点
BPMC
/
/
清除内存断点
/
/
3.
设置断点
BPHWS dwOEP,
"x"
/
/
当执行到此地址时产生中断.
BPHWS dwBase,
"x"
/
/
当执行到此地址时产生中断.
/
/
4.
循环
LOOP0:
RUN
/
/
F9
CMP
dwBase,eip
JNZ CASE0
ADD dwGetAPI,eax
/
/
加上基地址
ADD dwWriteIAT,eax
/
/
加上基地址
BPHWS dwGetAPI,
"x"
/
/
下断点
BPHWS dwWriteIAT,
"x"
/
/
下断点
BPHWC dwBase
/
/
清除硬件断点
JMP LOOP0
CASE0:
CMP
dwGetAPI,eip
JNZ CASE1
MOV dwTMP,eax
JMP LOOP0
CASE1:
CMP
dwWriteIAT,eip
JNZ CASE2
MOV [edx],dwTMP
/
/
MOV DWORD PTR DS:[EDX],EAX
JMP LOOP0
CASE2:
CMP
dwOEP,eip
JNZ LOOP0
MSG
"OEP已到达"
001E14DC
8BDA
MOV EBX,EDX user32.BeginPaint
001E14DE
90
NOP
001E14DF
90
NOP
001E0895
891A
MOV DWORD PTR DS:[EDX],EBX
001E14DC
8BDA
MOV EBX,EDX user32.BeginPaint
001E14DE
90
NOP
001E14DF
90
NOP
001E0895
891A
MOV DWORD PTR DS:[EDX],EBX
/
/
1.
定义变量
MOV dwOEP,
0047148B
MOV dwPatch1,
14DC
MOV dwPatch2,
0895
MOV dwBase,
0047A37F
/
/
2.
清除环境
BC
/
/
清除所有软件断点
BPHWC
/
/
清除所有硬件断点
BPMC
/
/
清除内存断点
/
/
3.
设置断点
BPHWS dwOEP,
"x"
/
/
当执行到此地址时产生中断.
BPHWS dwBase,
"x"
/
/
当执行到此地址时产生中断.
/
/
4.
循环
LOOP0:
RUN
/
/
F9
CMP
dwBase,eip
JNZ CASE0
ADD dwPatch1,eax
/
/
加上基地址
ADD dwPatch2,eax
/
/
加上基地址
BPHWS dwPatch1,
"x"
/
/
下断点
BPHWC dwBase
/
/
清除硬件断点
JMP LOOP0
CASE0:
CMP
dwPatch1,eip
JNZ CASE1
FILL dwPatch1,
4
,
90
/
/
NOP
4
个字节
ASM dwPatch1,
"MOV EBX,EDX"
/
/
将当前指令修改为 MOV DWORD PTR DS:[EDI],EAX
ASM dwPatch2,
"MOV DWORD PTR DS:[EDX],EBX"
BPHWC dwPatch1
JMP LOOP0
CASE1:
CMP
dwOEP,eip
JNZ LOOP0
MSG
"OEP已到达"
/
/
1.
定义变量
MOV dwOEP,
0047148B
MOV dwPatch1,
14DC
MOV dwPatch2,
0895
MOV dwBase,
0047A37F
/
/
2.
清除环境
BC
/
/
清除所有软件断点
BPHWC
/
/
清除所有硬件断点
BPMC
/
/
清除内存断点
/
/
3.
设置断点
BPHWS dwOEP,
"x"
/
/
当执行到此地址时产生中断.
BPHWS dwBase,
"x"
/
/
当执行到此地址时产生中断.
/
/
4.
循环
LOOP0:
RUN
/
/
F9
CMP
dwBase,eip
JNZ CASE0
ADD dwPatch1,eax
/
/
加上基地址
ADD dwPatch2,eax
/
/
加上基地址
BPHWS dwPatch1,
"x"
/
/
下断点
BPHWC dwBase
/
/
清除硬件断点
JMP LOOP0
CASE0:
CMP
dwPatch1,eip
JNZ CASE1
FILL dwPatch1,
4
,
90
/
/
NOP
4
个字节
ASM dwPatch1,
"MOV EBX,EDX"
/
/
将当前指令修改为 MOV DWORD PTR DS:[EDI],EAX
ASM dwPatch2,
"MOV DWORD PTR DS:[EDX],EBX"
BPHWC dwPatch1
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2021-12-29 11:01
被HLuKT编辑
,原因: