![]() |
来源:安全中国 |
技术基础:会编写加壳程序、了解PE结构、X86指令系统。 开发工具:MASM、VC++ 1、我们有时候在改造一个PE的时候,对其中的某些call要做大量的改动,这时 候,往往通过手动增加一个节,在这个节中完成这个call的改造,再返回原来的地方继续执行。此法虽然可行,但工作量实在太大,且调试十分不方便。 2、“通用简单的方法”之实现原理: 使用jmp far address 和 call far address的机器码长度相同,且仅仅第一字节不同的原则,jmp第一字节是0xE9,而call第一字节是0xE8,后面4字节相同。 这种方法就是:通过给PE加个小壳,同时在加壳的时候,将PE中要改造的call 修改成调用外壳中的call,在外壳中的call中调用原来PE中的call,而外壳中的call我们在编写的时候较容易实现功能扩充和改造。 在外壳中的call中,我们有两种扩充功能的方式: (1)在调用原来PE中的call之前扩充功能; (2)在调用原来PE中的call之后扩充功能; 扩充功能可以直接在外壳call中实现,但较难调试和复杂。 简单通用的方法是这个外壳中的新call仅仅完成: 1、加载一个外部补丁DLL; 2、调用这个外部补丁DLL中的函数,完成原来PE中的call的功能扩充和改造。 3、调用原来PE中的call的; 是先调用原来PE中的call还是后调用,视具体需要决定。这样我们就可以很简单的写扩充的功能代码了,因为这个外部补丁DLL可以用高级语言来写,且调试十分简单。 3、外壳中函数的具体实现: (一) 首先,在外壳中设定几个变量: (1) 保存外部DLL的各个函数地址的变量(DWORD): pfnExtPatchFun_1--pfnExtPatchFun_n,n为多大具体看要改造PE中几个函数。 (2) 保存原来PE中的call的“地址差”变量(DWORD): dwOrgCallDifference_1--dwOrgCallDifference_n,n为多大具体看要改造PE中几个函数。 dwOrgCallDifference_x = 原来call的机器码的后4个字节(双字)。 (3) 保存原来PE中的call的RVA地址的变量(DWORD): dwOrgCallRVA_1--dwOrgCallRVA_n,n为多大具体看要改造PE中几个函数。 (4) 保存外部DLL的hModule的变量(DWORD): hExtPatchDll。 ;----------------------------------------------------------------- 举例说明: 如:原来PE的ImageBase=0x00400000,且有下面代码(IDA显示): . . 004010CB 50 push eax 004010CC 57 push edi 004010CD E8 CE 08 00 00 call sub_4019A0 ;需要改造的call,注意机器码后4字节 004010D2 85 C0 test eax, eax . . 则:dwOrgCallRVA_1 = 004010CD - 0x00400000 = 0x000010CD dwOrgCallDifference_1 = 0x000008CE ;------------------------------------------------------------------ ; (二) 在外壳程序中编写这些call,需要改造几个函数,就有几个call,格式如下: ;***************************************************************************** ;---------------------------------------------------; ; 外壳中的call,用于替换原来PE中的call ; ;---------------------------------------------------; align 4 wjq_API_SMC_Label_1: GeneralPEShellCall_1 proc ;{ ;原PE中的call的入口参数,我们通过堆栈传递到Dll中的新call,此时需注意EBP在本call中不要改变。 @nPara1 EQU [ebp+0ch] ;原来PE中的Call的参数,也可以通过esp取得 @nPara2 EQU [ebp+8h] ; pusha call GetPatchDllFunctions ;获取外部DLL中的所有函数地址并保存 call GetAddressDifference ;获取地址差 .if [pfnExtPatchFun_1+eax] != 0 ;如果功能函数1实现了,就调用 push @nPara1 ;原来PE中的Call的参数 push @nPara2 call [pfnExtPatchFun_1+eax] ;调用外部DLL中的补丁call .endif ;------------调用原来的call------------------------- call GetAddressDifference ;获取地址差 lea esi,[TempCall_1+eax] ;现在call的VA sub esi,[Image_Base+eax] ;转换成RVA sub esi,[dwOrgCallRVA_1+eax] ;减去原来PE中的call的RVA = 两个call的地址差 ; mov ebx,[dwOrgCallDifference_1+eax] ;原来call xxxxxxxx 指令的偏移差 .if ebx < 0 ;负数,原来call向上调用 add ebx,esi .else ;正数,原来call向下调用 sub ebx,esi .endif ; mov [TempCallDifference_1+eax],ebx ;修正后的差值 popa ; |