被函数调用修改后使用ECX

逆向工程 x86 汇编
2021-06-21 04:49:40

我有一段特殊的代码片段,我无法理解:

push 0xC           ; arg1 for call
mov ecx,edi        ; set the this pointer for call
call sdk.100039F0  ; make the call (internally calls DeviceIOControl)
push ecx           ; ECX now points to a function within kernelbase.dll
mov ecx,edi        ; set the this pointer for call
call sdk.10003BD0  ; make the call

要在通话DeviceIoControl的第一个函数调用中修改ECX。为什么将其作为参数推送到第二次调用?第二个调用本身不接受参数,它根本不引用[ebp + n],但它仍然以ret 4.

紧随其后的是第三次调用,它也在内部使用DeviceIOControlthen 函数返回。这第三次调用没有前面的神秘推动。所有这些功能,包括容器,都使用thiscall.

以防万一我错过了什么,这是第二个电话的正文:

push ebp
mov ebp,esp
sub esp,0x30
push ebx
push esi
mov eax,ecx
xor esi,esi
push edi
mov edi,dword ptr ds:[<&DeviceIoControl>]
mov ecx,sdk.10002690
mov dword ptr ss:[ebp-0x8],eax
mov edx,0x8000
test ecx,ecx
je aura_sdk.10003C35
mov ax,word ptr ds:[eax+0x4]
push 0x0
mov word ptr ss:[ebp-0x20],ax
lea eax,dword ptr ss:[ebp-0x14]
push eax
push 0x4
lea eax,dword ptr ss:[ebp-0x4]
mov dword ptr ss:[ebp-0x4],0x0
push eax
push 0x7
lea eax,dword ptr ss:[ebp-0x20]
mov byte ptr ss:[ebp-0x1A],0x1
push eax
push 0x80102050
push dword ptr ds:[0x100375C0]
call edi
mov bl,byte ptr ss:[ebp-0x4]
mov ecx,sdk.10002690
mov edx,0x8000
jmp sdk.10003C37
xor bl,bl
test bl,0x9E
jne sdk.10003C8A
test ecx,ecx
je sdk.10003C7D
push 0x0
mov eax,0xED
mov dword ptr ss:[ebp-0xC],0x0
mov word ptr ss:[ebp-0x28],ax
lea eax,dword ptr ss:[ebp-0x18]
push eax
push 0x4
lea eax,dword ptr ss:[ebp-0xC]
mov byte ptr ss:[ebp-0x22],0x1
push eax
push 0x7
lea eax,dword ptr ss:[ebp-0x28]
push eax
push 0x80102050
push dword ptr ds:[0x100375C0]
call edi
mov ecx,sdk.10002690
mov edx,0x8000
mov eax,dword ptr ss:[ebp-0x8]
inc esi
cmp si,dx
jb sdk.10003BF0
test bl,0x82
je sdk.10003CA3
pop edi
test bl,0x1C
mov eax,0x0
pop esi
sete al
pop ebx
mov esp,ebp
pop ebp
ret 0x4

这是编译器自行纠正的情况还是有目的?

1个回答

这看起来像是编译器优化的结果。第二个被调用者(一个fastcall函数)接受一个它显然不使用的参数。编译器处于无法修改第二次调用的调用约定的情况,所以它仍然需要取一个参数并将其从堆栈中移除。最有可能的是,编译器无法证明该函数未被它无法调整的外部代码调用。但是编译器可以使用该函数在编译调用者时不使用其参数的知识,方法是删除计算参数的代码并推送一个虚拟参数。push ecx是一个单字节指令,因此是提供所需虚拟参数的一种非常有效的方式。

对于处于发布模式的 Visual C++,您应该知道默认情况下启用链接时代码生成如果应用程序和 SDK 都使用链接时代码生成进行编译,则“目标文件”实际上包含代码的一些抽象中间表示,并且当所有具有链接时代码的库都在链接时实际转换为二进制代码生成启用和应用程序相结合。即使目标文件或静态库是相互独立生成的,这也可以实现参数省略。