好友
阅读权限10
听众
最后登录1970-1-1
|
我们主要来看看IAT是如何修复的,首先看看修复的脚本.
以下代码引用自Vmprotect 2.0x Unpacker脚本
首先看看视频里的那5个变量mov first,0044CA98
mov write,004B5B72
mov getapi,004B372E
mov begin,0012F7FC
mov end,0012FB98
first : 是OEP,在这个脚本里OEP就是代表一个出口,代表在VM里执行解码IAT的函数已经执行完了.
write :004B5B72 8910 MOV DWORD PTR DS:[EAX],EDX
这个其实是VMP的一个数据输出指令,相当于汇编里的mov dword ptr ds:[xxxxxxxx],reg32
IAT是一组全局变量,而这个指令就是负责写入全局变量的.我们可以知道最终函数保存的地址,视频里下的写入断点就是为了快速地定位这条输出指令,这种方法在其它虚拟机里应该也是通用的.
在VM里追踪数据,hook住数据的输入和输出指令是很常见的方法,以我个人的理解,这个和hook函数的思想是一样的.
hook住这个指令后,可以得到些什么东西呢,答案就是函数的地址和key,在虚拟机里,数据的流动是很频繁的,那么如何确定是输出到IAT呢,就要靠下面的几个变量了.
getapi :004B372E 36:8B00 MOV EAX,DWORD PTR SS:[EAX]
这个又是什么东东呢,看到这个指令使用SS前缀想到了吧,没错,这个就是VMP里的堆栈输入指令,这里可以把它看成汇编的mov reg32,dword ptr ss:[ebp],这条指令用来读取局部变量.
在我上次的扫盲贴里已经说过了,VMP使用了随机指令表,每次的handle和指令的关系都是不一样的,因此每次分析时都需要修正指令表,而在视频里并没有分析指令表,而是直接定位指令,定位这个指令
的过程非常有趣,我们来看一下定位的过程.
通常的情况下,解码IAT都是在一个循环里进行的,"读取key-->解密-->填充IAT"这样的流程,通过内存断点,我们找到了循环的尾部填充部分,只要不是最后一个函数,那么就会在下一轮再次命中输出断
点的,视频里的run trace就是追踪了一个循环的过程.因为这个循环解密了一个函数,所以必定要通过输入指令来读取key,在追踪的数据中查找输入指令的特征码就可以定位到指令了.
光hook住输入指令和输出指令还不够,这两个指令会很频繁地调用,因此还要去除垃圾数据,用什么方法来去除垃圾数据呢,就靠下面的那两个变量了.
begin和end光看名字就知道是IAT数据的界限了,VM掉的函数应该是VMP对IAT进行二次加密的函数,这时真正的数据是通过堆栈来传递的,用的是局部变量的数组,如何准确地查找这个表估计就只有凭经
验了.
好了,修正完这些变量后,我们来看看脚本是如何工作的,脚本前面的部分是用来获取当前环境各段的基址和大小,用处在后面可以看到.
接下来断VirtualProtect跳过解码.sub end,4
bphws end,"w"
esto
bphwc
bphws first
add end,4
在指令表尾部下一个写入断点,跳过解码过程,直接到二次加密.loopfix:
eval "eax>{begin}&&eax<{end}"
bpcnd getapi, $RESULT //筛选无效的数据,只要不是引用指令表的数据都跳过
esto
cmp eip,first //停下来的地方是OEP,解码过程已经结束了
je exit
bc eip
sti
mov rapi,eax
gn eax //获取地址所对应的函数名
mov dllname,$RESULT_1
mov apiname,$RESULT_2
bp write //断输出指令,记录加密后的地址
esto
cmp eip,first
je exit
bc eip //取消输出断点,因为其他的指令也会调用输出指令,这里不取消的话就有可能杯具了
mov addr,eax //记录IAT的地址
mov dword,rapi
sub dword,edx //整个脚本最重要的地方,先记下来吧
wrta logfile,addr
wrta logfile,","
wrta logfile,dword
wrta logfile,","
wrta logfile,dllname
wrta logfile,","
wrta logfile,apiname
wrta logfile,"\r\n"
jmp loopfix
好了,脚本到这里就完了,下面我们来看生成的LOG文件
LOG文件的开头这样的B //段数,这里估计是用来确定IAT数据的起始
401000 //下面的都是基址和大小,估计是用来进行重定位,这里是带基址的 ,loader检查PE头??
4BAE0
44D000
1124
44F000
BD9
450000
1F2A
452000
10
453000
18
454000
5520
45A000
5400
460000
7190
468000
4DDBB
4B6000
5254
下面就是与IAT相关的数据了,这里举一个函数为例子,来理解戏法是怎么变的~
和视频里一样,这里用的是456ED4,8ED28660,kernel32,GetModuleHandleA
456ED4是程序存放key的地址,8ED28660是脚本上面用sub计算出来的值,kernel32是DLL文件名,GetModuleHandleA是函数名.
456ED4是需要重定位的,如果程序不是载入到00400000,那么就会出错,上面的那些数据估计就是用来对这个地址进行重定位了.
其它都很好理解,关键就是8ED28660这个数值,要明白这个数值,首先要搞明白VMP的二次加密对函数地址做了些什么东西,和视频里一样,我们来看看关键的几条指令.00466171 BD 92E94300 MOV
EBP,delphi7?0043E992
00466180 8BAD 42850100 MOV EBP,DWORD PTR SS:[EBP+18542]
00462BE1 8DAD 6086D28E LEA EBP,DWORD PTR SS:[EBP+8ED28660]
关键就这三条指令,0043E992+18542=456ED4,就是存放key的地址,看到了吗"8ED28660"这个就是脚本保存的数值,VMP对IAT的二次加密就是减去一个随机数,因为这些代码是分散在程序里,因此脚本直接模
拟这个减法运算,计算出这个随机值.接下来就是对函数进行重定位了,这个就是loader的作用了,loader可以通过GetProcAddress获得当前函数的地址,而随机数8ED28660加上456ED4保存的EDAE3041就可以
得到dump时的函数地址,然后用地址差值来修正456ED4保存的数据就可以了,如果你开始不明白的话,参考一下经典的自定位. call @f
@@:
pop reg32
sub reg32,@b |
|