3、应用实例
如上图有一个函数PcInfo.dll,里面导出了两个函数,RegistryCode 和IsValidCheckCode。通过OllyICE我们很容易找到这两个函数的实现过程。
第一个函数RegistryCode有三个参数,第一个压栈的是一个字符串的地址,第二个和第三个是一个整数,通过函数返回后对eax寄存器进行了检查可以断定函数带一个int型的返回值。
函数调用后没有进行堆栈的平衡说明这个函数是一个__stdcall方式调用的,堆栈平衡由被调用者平衡。调用方式确定后就可以还原函数原型如下:
int __stdcall RegistryCode(int a1,int a2,const char * key)

函数内部代码实现如上图,我们可以将汇编实现代码拷贝出来如下:
10002280 > 83EC 10 sub esp, 10
10002283 8D4424 00 lea eax, [esp]
10002287 56 push esi
10002288 57 push edi
10002289 33FF xor edi, edi
1000228B 57 push edi
1000228C 50 push eax
1000228D 57 push edi
1000228E 68 3F000F00 push 0F003F
10002293 57 push edi
10002294 57 push edi
10002295 57 push edi
10002296 68 44910010 push 10009144 ; ASCII "Software\abc\def"
1000229B 68 02000080 push 80000002
100022A0 FF15 00800010 call [<&ADVAPI32.RegCreateKeyExA>] ; ADVAPI32.RegCreateKeyExA
100022A6 85C0 test eax, eax
100022A8 75 56 jnz short 10002300
100022AA 8B7424 24 mov esi, [esp+24]
100022AE 8B0D 88910010 mov ecx, [10009188]
100022B4 8B15 8C910010 mov edx, [1000918C]
100022BA A1 90910010 mov eax, [10009190]
100022BF 85F6 test esi, esi
100022C1 894C24 0C mov [esp+C], ecx
100022C5 895424 10 mov [esp+10], edx
100022C9 894424 14 mov [esp+14], eax
100022CD 74 31 je short 10002300
100022CF 56 push esi
100022D0 FF15 24800010 call [<&KERNEL32.lstrlenA>] ; kernel32.lstrlenA
100022D6 40 inc eax
100022D7 8D4C24 0C lea ecx, [esp+C]
100022DB 8B5424 08 mov edx, [esp+8]
100022DF 50 push eax
100022E0 56 push esi
100022E1 6A 01 push 1
100022E3 57 push edi
100022E4 51 push ecx
100022E5 52 push edx
100022E6 FF15 04800010 call [<&ADVAPI32.RegSetValueExA>] ; ADVAPI32.RegSetValueExA
100022EC 85C0 test eax, eax
100022EE 75 05 jnz short 100022F5
100022F0 BF 01000000 mov edi, 1
100022F5 8B4424 08 mov eax, [esp+8]
100022F9 50 push eax
100022FA FF15 08800010 call [<&ADVAPI32.RegCloseKey>] ; ADVAPI32.RegCloseKey
10002300 8BC7 mov eax, edi
10002302 5F pop edi
10002303 5E pop esi
10002304 83C4 10 add esp, 10
10002307 C2 0C00 retn 0C
有两种方法可以复用上面这段代码,第一种方法是把这段代码复制到一个汇编编译器中编译成object,然后在VC中链接这个外部函数,但是这种方法需要额外的编译器,使用起来比较复杂。本文主要介绍第二种方法,就是在VC编译器中复用这段汇编代码,然后直接编译调试。
在VC编译器中使用内联汇编,有两种方式:一种是在每一条指令前面加上__asm,另外一种方式比较适合大段汇编代码,就是使用__asm { } 包含汇编代码块。这个不改变变量作用域,只是一个指示符号,注意大部分伪汇编代码VC是不支持的。将以上汇编代码拷贝到VC的函数int __stdcall RegistryCode(int a1,int a2,const char * key)函数内部,这样一个简单函数初步实现了,但是还需要作一些调整。