好友
阅读权限 10
听众
最后登录 1970-1-1
耶稣
发表于 2021-8-12 20:59
背景
最近在研究壳,去试了试最新的Enigma脱壳 ,虽然也脱了但是还是有一些勉强,修复IAT并不完美需要手工处理掉一些不同语言所必带的函数才能完全修复掉IAT,有完美方法的朋友可以回帖告诉我。
平台:windows XP 32位工具:OD、REC加壳对象:VC6.0程序寻找OEP
OEP的寻找比较简单,我没有让入口虚拟化,水平有限,只勾选了输入表的保护,其他的默认。
寻找OEP我使用的是语言特有的特征码定位的方法,这种方法很快啊,单步跟OEP比较难这个壳对断点的检测也很严,对于F8单步跟踪也有对应的检测手段,例如步过某个CALL计算出来的返回地址会变成错误的值无法正常返回上一层。我们也要尽量避免使用CC断点和内存断点,只用硬件断点来调试程序。
我们可以自己在网上找一个VC++6.0无壳程序找到OEP头部的CALL(前几个都行,别太远,也别太近),各种语言例子程序我也会打包给大家一份供参考。
我寻找的特征码是33 C0 6A 00 39 44 24 08 68 00 10 00 00 0F 94 C0 50 FF 15 ?? ?? ?? ?? 85 C0
我们先让程序跑起来,毕竟几乎所以的加密壳都会填充原始代码段数据,运行程序会让程序把代码段解码方便我们用特征码定位OEP
在这里下硬件执行断点即可,重新载入程序F9让程序执行过去会断在我们的执行断点处,我们看堆栈窗口直接返回到上一层的调用就行OEP
OEP的寻找到这里就结束了,不难,这种方法除了入口直接虚拟化效果不好以外其他的一些处理OEP头部的方法都能定位出OEP,例如stolen code也可以有效处理找回头部代码或者可以定位接近头部的代码来进行下一步分析找回头部代码。
修复IAT
我先来介绍下EG壳处理IAT有几种手法吧模拟函数执行 单纯加密 加密+模拟函数 还有一种特殊情况下才会有,但也是一种单纯的加密,难度属于最低的那种,我下面也会简单说一下
第一种:模拟函数执行
模拟函数执行的IAT函数就是上面截图0111开头的IAT函数就全部是模拟类型的IAT,我们随便选一个右键查找参考找到调用的call看看
我也随便选一个参考00421678这个call,我们在这个call重建EIP执行进去看看变成了什么样
代码被加花指令分段执行了。我们在这个IAT函数地址下硬件写入断点看看哪里写入这个地址的我们可以看到EDX寄存器的内容就是壳申请的内存段的地址0111开头,我们可以往上看edx的来源,往上跟看看0111地址到底是在哪里生成产生的
005C3F0B 8B55 F0 MOV EDX,DWORD PTR SS:[EBP-0x10]
我们一直跟踪EBP堆栈里面的模拟地址最后跟到
005C3D67 8B00 MOV EAX,DWORD PTR DS:[EAX]我们可以看到eax的地址也是一个0111地址我们数据窗口跟随看下,眼睛犀利的同学有没有发现这段内存排布很整齐?就想一个表一样呢?
我可以尝试下VirtualAlloc断点重新载入程序,当程序刚刚申请0111段内存时我们去看下这个内存段里面到底有什么。我们随便选一个模拟函数地址然后在0111段查找这个值我们会发现能找到这个地址,我们记住这个地址然后重新载入程序VirtualAlloc到刚刚申请完时候再去这个地址看看我们会发现这个地址里不再是0111值而是一个API函数地址,已经闻到真相的味道了。。
我们在这里下个硬件写入断点取消掉申请内存的断点,然后F9运行起来,我们会发现这里会断在一个填充的地方,好了我们尝试不让他成功将原始API地址覆盖来看看,结果是IAT表的模拟函数全部变成了正常的函数地址,模拟函数就这样解决了。要写脚本处理,我会把我的脚本给附上,写得不是很好。
最近家里电路有问题总是跳闸,拖拖拉拉就到了今天才写。修复IAT
第二种单纯加密
这种处理IAT的就算单纯的进行运算解密出对应新的FF25表对应的函数地址跳转,每个加密过的IAT都有对应不同的push xxxxxxxx一个值,EG壳这种处理手法和老版本几乎是一样的,没有太大的变动。
如果大家想详细跟下他的解密可以进入这个CALL005C84B8 E8 3FC0FFFF CALL PE提取_p.005C44FC
EAX就是返回的地址,就是壳自己创建的FF25跳转表对应的函数跳转,如果我们写脚本处理就很简单了,这个解密CALL是公用的所有函数的解密都是调用这一个解密CALL
我简单的来说下脚本的实现思路吧
由于这个样本是VC++编译的所有的函数调用都是以FF15 CALL的形式简接调用我们只需从00401000开始往下找FF15开头的汇编语句即可 当我们找到FF15汇编语句是我们可以+4取间接调用地址里的值是否小于5FF00000(系统函数地址都是高地址,壳申请的都是低的地址) 我们可以在壳创建的FF25地址表选择地址表所处的全部区域下内存访问断点,再按照2的方法获取对应的函数地址填充会1的地址里即可
EG壳对于这种方法还有一个线程干扰,怎么回事呢?就是当你用脚本跑修复IAT时他会时不时调用sleep函数来暂停线程干扰修复,根据我的观察可以发现加壳程序对比没加壳的程序多了2条线程,当我终止这2条线程后程序会退出,就是一个简单的ExitProcess函数的调用退出我们直接在函数内部ret掉程序就不会退出,正常运行了,也不再会调用sleep函数干扰我们修复了。
脚本代码如下:
var addr
var taddr
var addriat
var taddriat
bc
bpmc
bphwcall
bprm 6ccca5,f2a
mov addr,401000
start:
findop addr,#ff15????#,ffff
mov addr,$RESULT + 4
cmp $RESULT,0
je exit
mov taddr,[$RESULT + 2],4
mov taddriat,taddr
mov taddr,[taddr],4
cmp taddr,4f000000
ja start
cmp taddr,500000
jb start
mov eip,$RESULT
eob fix
run
fix:
mov addriat,[eip + 2],4
mov addriat,[addriat],4
mov [taddriat],addriat,4
jmp start
exit:
ret
第三种模拟+加密
最后一种加密也是最难还原的,我把EG壳翻了底朝天我还是无法找到这种IAT处理出现对应原函数的地方,我猜测EG壳在SDK处理的时候就将这一小部分函数给按字节获取重写进壳程序保存起来了,现场就不会再二次获取函数出现暴露的风险。EG壳这种模拟虽然力度比较大但是也是有前提条件的不是任何函数都可以模拟的,更具对比发现他主要是挑一些编译语言所必带的函数来进行加密,不仅如此在这部分函数里再挑选整个函数没有call的调用没有跳转的函数进行重写。
大概有这些函数:
00425148 >7C810C6D kernel32.GetCommandLineA
00425248 >7C80CD37 kernel32.SetHandleCount
00425198 >7C80DE95 kernel32.GetCurrentProcess
00425220 >7C811752 kernel32.GetVersion
004502C0 >7C82511A kernel32.FreeResource
004502B4 >7C8099C0 kernel32.GetCurrentProcessId
00425290 7C80B741 kernel32.GetModuleHandleA
004250F8 7C80CD37 kernel32.SetHandleCount
​
还有几个比较特殊的函数又是另外独特的重写手法,用公用解密call解密后的返回地址是另外一种,我总结了下就这几个,语言不同可能会有变动
kernel32.GetModuleHandleA
kernel32.LoadLibraryExA
ntdll.RtlGetLastWin32Error
我们可以用DIE64来查加壳程序的编译器然后去找到对应无壳的程序来比照填写函数地址即可。数量不会很多的,如果碰上未知的函数我们可以看call的所需要的push参数个数来大致判断是哪个函数。
好了,EG6.8的旅程就到这里结束了。随着我的继续探索,我还会给大家带来VMP TMD SE OB等壳的脱文,感谢大家的支持。
免费评分
参与人数 1 威望 +1
吾爱币 +20
热心值 +1
收起
理由
Hmily
+ 1
+ 20
+ 1
感谢发布原创作品,吾爱破解论坛因你更精彩!
查看全部评分
发帖前要善用【论坛搜索 】 功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。