Introduction
I compiled a simple executable with Visual Studio in x64 Windows. Source code:
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);
}
If you compile the executable with GCC, then you get what you'd expect: one function call in main
(it calls test
) and one function call in test
(it calls printf
).
However, if you compile the executable with Visual Studio on an x64 Windows machine, then in both main
and test
, an additional call is made to some function. Let's call that function mystery
. I'd like to know what the mystery
function is for.
Code
Here is the disassembly of the main
function. I have added a comment to show where the mystery
function is called.
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
The same mystery
function is also called in the test
function:
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
Here are the contents of the mystery
function:
├ 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
Notes
The call to KERNEL32.dll_GetCurrentThreadId in the mystery
function seemed interesting, but I couldn't find any information about functions, which get added by the compiler and call GetCurrentThreadId.
The question arose because I was studying the Microsoft x64 calling convention and I noticed that the mystery
function didn't seem to use "home space" (also known as "shadow space", 32 bytes of space on the stack that's assigned to each function). I wanted to know what that function is and why home space wasn't assigned to it.
So the question is, what is the mystery
function?