介绍
我在 x64 Windows 中使用 Visual Studio 编译了一个简单的可执行文件。源代码:
long test(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) {
printf("%d %d %d", a, b, c);
return 0x0123456789acdef;
}
int main() {
test(1,2,3,4,5,6,7,8,9,10);
}
如果您使用 GCC 编译可执行文件,那么您会得到您所期望的:一个函数调用main
(它调用test
)和一个函数调用test
(它调用printf
)。
但是,如果您在 x64 Windows 机器上使用 Visual Studio 编译可执行文件,则在main
和 中test
,都会对某些函数进行额外调用。让我们调用该函数mystery
。我想知道这个mystery
功能是做什么用的。
代码
下面是main
函数的反汇编。我添加了一条注释来显示mystery
调用函数的位置。
int main (int argc, char **argv, char **envp);
; var int64_t var_c8h @ rbp+0xc8
; var int64_t var_20h @ rsp+0x20
; var int64_t var_28h @ rsp+0x28
; var int64_t var_30h @ rsp+0x30
; var int64_t var_38h @ rsp+0x38
; var int64_t var_40h @ rsp+0x40
; var int64_t var_48h @ rsp+0x48
; var int64_t var_50h @ rsp+0x50
push rbp
push rdi
sub rsp, 0x118
lea rbp, [var_50h]
lea rcx, [0x1400c1003]
call fcn.140034cf1 ; Mystery function here!
mov dword [var_48h], 0xa
mov dword [var_40h], 9
mov dword [var_38h], 8
mov dword [var_30h], 7
mov dword [var_28h], 6
mov dword [var_20h], 5
mov r9d, 4
mov r8d, 3
mov edx, 2
mov ecx, 1
call fcn.140033e73
xor eax, eax
lea rsp, [var_c8h]
pop rdi
pop rbp
ret
mystery
函数中也调用了相同的test
函数:
test (int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4);
; var int64_t var_c8h @ rbp+0xc8
; var int64_t var_20h_2 @ rsp+0x20
; var int64_t var_8h @ rsp+0x100
; var int64_t var_10h @ rsp+0x108
; var int64_t var_18h @ rsp+0x110
; var int64_t var_20h @ rsp+0x118
; arg int64_t arg1 @ rcx
; arg int64_t arg2 @ rdx
; arg int64_t arg3 @ r8
; arg int64_t arg4 @ r9
mov dword [var_20h], r9d ; arg4
mov dword [var_18h], r8d ; arg3
mov dword [var_10h], edx ; arg2
mov dword [var_8h], ecx ; arg1
push rbp
push rdi
sub rsp, 0xe8
lea rbp, [var_20h_2]
lea rcx, [0x1400c1003]
call fcn.140034cf1 ; Mystery function here!!!
mov r9d, dword [var_18h]
mov r8d, dword [var_10h]
mov edx, dword [var_8h]
lea rcx, str._d__d__d ; 0x14009ff98 ; "%d %d %d"
call fcn.1400335b3
mov eax, 0x789acdef
lea rsp, [var_c8h]
pop rdi
pop rbp
ret
下面是mystery
函数的内容:
├ 60: mystery (int64_t arg1);
│ ; var int64_t var_20h @ rsp+0x20
│ ; var int64_t var_8h @ rsp+0x40
│ ; arg int64_t arg1 @ rcx
│ 0x1400387d8 48894c2408 mov qword [var_8h], rcx ; arg1
│ 0x1400387dd 4883ec38 sub rsp, 0x38
│ 0x1400387e1 488b442440 mov rax, qword [var_8h]
│ 0x1400387e6 4889442420 mov qword [var_20h], rax
│ 0x1400387eb 488b442440 mov rax, qword [var_8h]
│ 0x1400387f0 0fb600 movzx eax, byte [rax]
│ 0x1400387f3 85c0 test eax, eax
│ ┌─< 0x1400387f5 7418 je 0x14003880f
│ │ 0x1400387f7 833d06fa0700. cmp dword [0x1400b8204], 0 ; [0x1400b8204:4]=0
│ ┌──< 0x1400387fe 740f je 0x14003880f
│ ││ 0x140038800 ff15fa770800 call qword [sym.imp.KERNEL32.dll_GetCurrentThreadId] ; [0x1400c0000:8]=0xc0738 reloc.KERNEL32.dll_GetCurrentThreadId ; "8\a\f" ; DWORD GetCurrentThreadId(void)
│ ││ 0x140038806 3905f8f90700 cmp dword [0x1400b8204], eax ; [0x1400b8204:4]=0
│ ┌───< 0x14003880c 7501 jne 0x14003880f
│ │││ 0x14003880e 90 nop
│ │││ ; CODE XREFS from mystery @ 0x1400387f5, 0x1400387fe, 0x14003880c
│ └└└─> 0x14003880f 4883c438 add rsp, 0x38
└ 0x140038813 c3 ret
笔记
函数中对KERNEL32.dll_GetCurrentThreadId的mystery
调用似乎很有趣,但我找不到有关函数的任何信息,这些信息是由编译器添加并调用 GetCurrentThreadId 的。
出现这个问题是因为我正在研究 Microsoft x64 调用约定,并且我注意到该mystery
函数似乎没有使用“主空间”(也称为“影子空间”,堆栈上分配给每个函数的 32 字节空间) . 我想知道那个功能是什么以及为什么没有分配家庭空间给它。
那么问题来了,mystery
函数是什么?