复用程序代码段技术

有两种方法可以复用上面这段代码,第一种方法是把这段代码复制到一个汇编编译器中编译成object,然后在VC中链接这个外部函数,但是这种方法需要额外的编译器,使用起来比较复杂。本文主要介绍第二种方法,就是在VC编译器中复用这段汇编代码,然后直接编译调试。
在VC编译器中使用内联汇编,有两种方式:一种是在每一条指令前面加上__asm,另外一种方式比较适合大段汇编代码,就是使用__asm { } 包含汇编代码块。这个不改变变量作用域,只是一个指示符号,注意大部分伪汇编代码VC是不支持的。将以上汇编代码拷贝到VC的函数int __stdcall RegistryCode(int a1,int a2,const char * key)函数内部,这样一个简单函数初步实现了,但是还需要作一些调整。
VC内联外部汇编代码需要注意以下几个事项:
A.   代码的修正
拷贝出来的汇编代码需要进行初步处理才能移植到VC的函数中,上面的汇编代码中地址头需要全部删除掉(这个可以在UltraEdit的列模式中复制),对于代码中的常数全部要加上0x,因为Ollyice拷贝出来的都是16进制数,对应跳转指令可以用原来的标示符号,如jnz      short   100022F5,改成jnz      short   A100022F5,将地址前加上一个字符,否则VC对于这样的跳转会将数字理解成数字而认为是错误代码,而不会根据目标标签自动计算偏移量。
B.   堆栈的平衡
由于VC默认为函数生成了保护寄存器的代码,也就是进行了寄存器压栈操作,这样栈顶就可能改变,因此拷贝过来的汇编代码中基于栈顶取数据的地址就不对了,因为堆栈栈顶已经被VC默认保护堆栈的PUSH指令改变了。最简单的做法就是先平衡堆栈,也就是先弹出压栈的寄存器,这样拷贝过来的汇编代码在函数内部运行的环境就是干净的,无污染的,而VC在DEBUG模式和RELEASE模式为函数生成的保护寄存器的代码是不同的,需要分别处理来平衡堆栈。(注:如果不想VC生成保护寄存器的代码,可以加上关键字:__declspec( naked ),这样就不需要平衡堆栈了,可以连这一步都省了,但是了解编译器的一些行为对内联汇编的成功与否很重要)
C.   地址的处理
对于汇编代码中出现的地址,有些是字符或者整数数组的首地址,需要在全局或者局部建立字符串数组,然后替换成字符数组的地址。对于VC而言,指针类型的变量本身就是地址,因此在汇编代码中直接使用指针名称,而不需要LEA(或者OFFSET伪指令)来计算地址。
D.   函数调用的处理
对于汇编中使用到的函数需要加入其相应的Lib文件,对于Windows Api只需要加入Windows.h文件即可,这样VC才会为使用到的函数加入导入表和函数符号。对于外部DLL中的函数调用需要注意的是,必须使用间接寻址方式调用函数,而不能是立即数地址,也就是使用FirstThunk+RVA的方式来寻址,由PE加载器动态填充的地址代替实际地址,而FirstThunk+RVA的地址在编译过程中就可以惟一确定。对于静态连接的函数如printf等这些函数,则使用直接调用方式,调用地址是立即数。而编译器对于有些外部导出函数,采用一个call(立即数)+ jmp(间接寻址)方式实现。
具体实现过程:
添加需要使用的全局字符串,定义如下:
char *key1="Software\\abc\\def";
char *key2="InstallCode";
由于DEBUG模式VC默认为函数生成了保护寄存器的代码,总共对4个寄存器进行了保护,因此我们在函数的入口处平衡堆栈,代码如下:
__asm
     {
       pop edi
       pop esi
       pop ebx
       pop ebp
     }
如果是RELEASE模式,则只对3个寄存器进行了压栈保护,因此我们在函数的入口处平衡堆栈,两种模式可以合成写成如下的方式:
//还原堆栈,VC为函数默认生成了保护寄存器的代码
   #ifdef _DEBUG    //DEBUG模式和RELEASE模式生成的保护寄存器的代码不一样
     __asm
     {
       pop edi
       pop esi
       pop ebx
       pop ebp
     }
   #else
     __asm
     {
       pop edi
       pop esi
       pop ebp
     }
   #endif
上面的一段代码,可以使用在函数前面加上__declspec( naked )的方法来替代。
移植的部分代码加到上面的代码后面即可,具体修改如下:
__asm
   {
     sub      esp, 0x10
     lea      eax, dword ptr [esp]
     push     esi
     push     edi
     xor      edi, edi
     push     edi
     push     eax
     push     edi
     push     0x0F003F
     push     edi
     push     edi
     push     edi
     push     key1    //如果是指针直接使用指针名称,否则使用lea eax, key1;   push eax方式
     push     0x80000002
     call dword ptr [RegCreateKeyExA] //此处不能使用call RegCreateKeyExA,只能进行间接寻址调用
     test     eax, eax
     jnz      short A10002300 //使用标签让VC自动计算偏移地址,不能使用纯数组标签
     mov      esi, dword ptr [esp+0x24]
     mov      eax, key2
     mov      ecx, [eax]
     mov      edx, [eax+4]
     mov      eax, [eax+8]
     test     esi, esi
     mov      dword ptr [esp+0x0C], ecx
     mov      dword ptr [esp+0x10], edx
     mov      dword ptr [esp+0x14], eax
     je       short A10002300 //使用标签让VC自动计算偏移地址
     push     esi
     call dword ptr [lstrlenA] //此处不能使用call lstrlenA,只能进行间接寻址调用
     inc      eax
     lea      ecx, dword ptr [esp+0x0C]
     mov      edx, dword ptr [esp+0x08]
     push     eax
     push     esi
     push     1
     push     edi
     push     ecx
     push     edx
     call dword ptr [RegSetValueExA]   //此处不能使用call RegSetValueExA,只能进行间接寻址调用
     test     eax, eax
     jnz      short   A100022F5
     mov      edi, 1
A100022F5:
     mov      eax, dword ptr [esp+0x08]
     push     eax
     call dword ptr [RegCloseKey]     //此处不能使用call RegCloseKey,只能进行间接寻址调用
A10002300:
     mov      eax, edi
     pop      edi
     pop      esi
     add      esp, 0x10
     retn     0x0c
   }
全部源代码如下:
// PCInfoAPI.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
#define PCInfoAPI_EXPORTS
#ifdef PCInfoAPI_EXPORTS
#define PCInfoAPI_API extern "C" __declspec(dllexport)
#else
#define PCInfoAPI_API __declspec(dllimport)
#endif
BOOL APIENTRY DllMain( HANDLE hModule,
                        DWORD   ul_reason_for_call,
                        LPVOID lpReserved
            )
{
     return TRUE;
}
char *key1="Software\\abc\\def";
char *key2="InstallCode";
PCInfoAPI_API   int __stdcall RegistryCode(int a1,int a2,const char * key)
{
   //还原堆栈,VC为函数默认生成了保护寄存器的代码
   #ifdef _DEBUG //DEBUG模式和RELEASE模式生成的保护寄存器的代码不一样
     __asm
     {
       pop edi
       pop esi
       pop ebx
       pop ebp
     }
   #else
     __asm
     {
       pop edi
       pop esi
       pop ebp
     }
   #endif
   __asm
   {
     sub      esp, 0x10
     lea      eax, dword ptr [esp]
     push     esi
     push     edi
     xor      edi, edi
     push     edi
     push     eax
     push     edi
     push     0x0F003F
     push     edi
     push     edi
     push     edi
     push     key1        
     push     0x80000002
     call dword ptr [RegCreateKeyExA]
     test     eax, eax
     jnz      short A10002300
     mov      esi, dword ptr [esp+0x24]
     //mov      esi,   key
     mov      eax, key2
     mov      ecx, [eax]
     mov      edx, [eax+4]
     mov      eax, [eax+8]
     test     esi, esi
     mov      dword ptr [esp+0x0C], ecx
     mov      dword ptr [esp+0x10], edx
     mov      dword ptr [esp+0x14], eax
     je       short A10002300
     push     esi
     call dword ptr [lstrlenA]
     inc      eax
     lea      ecx, dword ptr [esp+0x0C]
     mov      edx, dword ptr [esp+0x08]
     push     eax
     push     esi
     push     1
     push     edi
     push     ecx
     push     edx
     call dword ptr [RegSetValueExA]    
     test     eax, eax
     jnz      short A100022F5
     mov      edi, 1
A100022F5:
     mov      eax, dword ptr [esp+0x08]
     push     eax
     call dword ptr [RegCloseKey]    
A10002300:
     mov      eax, edi
     pop      edi
     pop      esi
     add      esp, 0x10
     retn     0x0c
   }
}

posted @ 2010-04-11 11:49  Yan Zhe  阅读(420)  评论(1编辑  收藏  举报