其他分享
首页 > 其他分享> > 第19章:寻找UPack OEP

第19章:寻找UPack OEP

作者:互联网

PE头在进程装载的时候使用格式比较固定,从中找到需要的信息后不必过多关注,只需要找到还原后的节区体即可。

从文件中的EntryPoint找到进程入口:

占用不需要的元素,其在节区如下区域中插入代码:

蓝色框中是可选头所占区域,其余的到16F多余的区域

这条命令将ESI所指的区域中的27个Dword大小的数据移动到EDI所指的地址中(即F0~16F),即将解码代码释放出来,以供后面执行:

红色框中是第一个节区头部分数据(前五个Dword元素),前面是利用SizeOfOptionalHeader增加出来的区域。

而EDI所指的地方恰好是PE文件的最后部分,并且这个区域是第二节区所处的位置:

下一条指令将两个Word元素(NumberOfRelocations/LineNumbers)压入栈。

然后用400H填充后面1C00个Dword,一直填充到1026DC处(已经快把第二节区[102700]填充满了)。

后面的解码中,首先会反复使用一个函数,大概会看到两种解码方式:

1.对几个变量进行计算,通过adc指令,在循环体中通过对AX寄存器进行反复的计算,得到值,写入EDI寄存器中。

2.通过rep movsb 指令,对一块区域进行解码。

 

这一区域的代码是两种解码方式最开始都会用到的,通过对函数返回的Flag值进行判断,从而选择了解码方式。

首先看看Decryption()函数,参数通过EDX传入:

 

 

首先需要明确一点 EBX 在程序的运行过程是一个固定的值,不会改变,即:0101FEC4

[EBX] , [EBX-4] , [EBX+4],这三个变量(分别命名为A,B,C)与解码密切相关。

 

1.将变量 A 与传入的变量EDX相乘,然后除以1000H,放在EAX中。

2.由变量 B (地址,即指针) 寻址找到数据,进行字节转置。

3. [B] - C 的值与变量 A 进行对比

(1)若小于 A则不跳转 , A = EAX,[EDX] += (800 - [EDX]) / 20H) , 通常[EDX] <= 400H 。

(2)跳转后:C += EAX , B -= EAX , [EDX] -=  [EDX] / 20H .然后置CF位为1。

4.对比AL寄存器是否为0,若为0,则 B++, C /= 3000H , A /= 3000H 。

执行返回。

总结一下这个函数的功能:通过对几个变量的计算,解密出值写入内存,并设置了CF位,后面会用到。

 

先看第一种解码方式,即不跳转的方式,代表没有置CF位为1,前面计算出来的值小于变量A。

 

 对EAX寄存器进行一系列的操作,如果ECX != 0 就进入循环,通过adc 指令,实现解码:

 

将AL寄存器写入EDI中,EDI会从第二节区0101Fxxx开始一直写到节区后面[ESI+34]即1014B5A为止。

当然有时候也会写前面的数据。

第二种解码方式是在第一个地方跳转:

 

这当中有一个执行了Decryption()函数,且执行了stc指令都会跳转。

若执行到最后的jmp,则会最后跳转,然后将EAX中的值写入[EDI]:

若没有执行jmp就跳转了则他们经过一系列对EAX的计算,最后都会到:

逐渐填充:

 

 

 

重建IAT

出循环后会进入第一个循环,从01001000H开始比较,一字节一字节的寻找,直到 [EDI] + 18 == 00  or  x1.

第二个循环继续寻找,直到 [EDI] + 18 ==  x1. 第三个循环是循环132次。

总结一下:一共循环133次,每次都要满足  [EDI] + 18 ==  x1 。

然后从栈中获取LoadLibrary() 以及 GetProcAddress()的地址,然后先调用LoadLibrary() ,将需要的库载入。

 

然后通过GetProcAddress()函数依次将获得到的函数地址写入EDI所指向的地址。

ret 返回时就到了正常notepad程序入口。

 

这篇文章写的杂乱没有头绪,是因为没有真正懂得它的解压缩算法是怎么执行的,只是看出了一个大概的流程。

标签:EDI,变量,19,解码,OEP,EAX,跳转,EDX,UPack
来源: https://www.cnblogs.com/Rev-omi/p/13326320.html