1 查看壳程序信息
- 使用ExeInfoPe
![]()
脱壳系列_2_IAT加密壳_详细版_解法1_包含脚本
分析:
发现这个壳的类型没有被识别出来,Vc 6.0倒是识别出来了,Vc 6.0的特征是 入口函数先调用GetVersion()
2 用OD找OEP
- 拖进OD
发现 这个壳和我们的正常程序很像。但是并不是我们的真正程序入口![]()
脱壳系列_2_IAT加密壳_详细版_解法1_包含脚本
- 因为vc6.0特征的第一个调用的是GetVersion(),给GetVersion()下 硬件断点
//第一次断下来,但是根据栈回溯,调用者并不是我们的模块//第二次断下来,就应该是了![]()
脱壳系列_2_IAT加密壳_详细版_解法1_包含脚本 //找到入口后 栈上右键 反汇编窗口跟随![]()
脱壳系列_2_IAT加密壳_详细版_解法1_包含脚本 //如下![]()
脱壳系列_2_IAT加密壳_详细版_解法1_包含脚本 ![]()
脱壳系列_2_IAT加密壳_详细版_解法1_包含脚本
- 在OD看内存布局,一般.rdata的最前面是放的IAT,而且OD数据窗口默认就是.rdata的起始位置。
- 也可以点一个call /jmp [];看一下来找IAT表
![]()
脱壳系列_2_IAT加密壳_详细版_解法1_包含脚本
- 对那个地方下一个硬件写入断点 --DWORD,即当前面的壳程序在修改的时候就能段下来找到壳的加密算法的地方
3 对加密部分分析
这个分析的过程,需要自己去啃,是分享不了的。
我是从GetProcAddress 开始分析的,过程中往前观察分析了几次来解答这里的一些寄存器的内容信息、局部变量的信息,以下是我主要分析的部分:
- 通过对IAT表位置下硬件写入断点,我们找到了这个修改IAT的地方
![]()
脱壳系列_2_IAT加密壳_详细版_解法1_包含脚本
断下这个GetProcAddress()这个函数 调用之后EAX保存的是 当前要填入IAT的真实地址
004385B9 FF15 CC924300 CALL DWORD PTR DS:[0x4392CC] ; kernel32.GetProcAddress
//开始怀疑修改进IAT的函数内容--加密代码前面几句--写死的硬编码opcode 到底意欲何为:![]()
脱壳系列_2_IAT加密壳_详细版_解法1_包含脚本
在内存窗口跳到[EBX-0X30]选择反汇编观察一下:![]()
脱壳系列_2_IAT加密壳_详细版_解法1_包含脚本
- 发现 这就是解密IAT代码。
总结:用硬编码 opcode (用于解密IAT)的首地址替代IAT中的函数地址,然后每次IAT调用的时候都会去调用这个代码块使用。![]()
脱壳系列_2_IAT加密壳_详细版_解法1_包含脚本
4 分析了加密算法了流程后
- 这时候我们只需要保存正确的函数地址值到IAT,那么这个程序就能脱掉了。
![]()
脱壳系列_2_IAT加密壳_详细版_解法1_包含脚本
然后和前面几篇一样的流程dump到本地,用impREC修复一下IAT
运行没毛病!!
附:还可以使用OD脚本修正IAT
思路:脚本如下:
- 调用getprocAddress之后,立刻使用一个临时变量保存起来,
- 再待壳修改IAT后,立刻修改回正确的函数地址(前面保存的临时变量)
//1.定义变量 MOV dwGetApiAddr,004385bf MOV dwWriteAddr,004385f0 MOV dwOEP,00409486 //2.初始化环境 BC //清除软件断点 BPHWC //清除硬件断点 BPMC //清除内存断点 //下硬件执行断点 BPHWS dwGetApiAddr,"x" BPHWS dwWriteAddr,"x" BPHWS dwOEP,"x" //3.构建逻辑 /* -- 用一个临时变量来存储 真正的函数地址 -- 在加密逻辑代码执行完,并且写入IAT后,立刻改回 */ LOOP0: RUN //相当于od -- F9 CMP dwGetApiAddr,eip//如果是执行完GetProcAddress后 JNZ CASE1 mov dwTemp,eax JMP LOOP0 CASE1: CMP dwWriteAddr,eip MOV [edi],dwTemp JMP LOOP0 CASE2: CMP dwOEP,eip JNZ LOOP0 MSG "改回来了哈哈"