编译器向用户定义的函数添加函数调用。函数有什么作用?(x64 Windows 可执行文件)

逆向工程 拆卸
2021-06-11 06:21:21

介绍

我在 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_GetCurrentThreadIdmystery调用似乎很有趣,但我找不到有关函数的任何信息,这些信息是由编译器添加并调用 GetCurrentThreadId 的。

出现这个问题是因为我正在研究 Microsoft x64 调用约定,并且我注意到该mystery函数似乎没有使用“主空间”(也称为“影子空间”,堆栈上分配给每个函数的 32 字节空间) . 我想知道那个功能是什么以及为什么没有分配家庭空间给它。

那么问题来了,mystery函数是什么

2个回答

__CheckForDebuggerJustMyCode/JMC(仅我的代码调试)选项启用时,您所指的“神秘”函数被调用并由 MSVC 插入文档

/JMC(只是我的代码调试)

在 Visual Studio 调试器中指定对本机 Just My Code 调试的编译器支持。此选项支持允许 Visual Studio 跳过系统、框架、库和其他非用户调用并在调用堆栈窗口中折叠这些调用的用户设置。

[...]

Visual Studio 仅我的代码设置指定 Visual Studio 调试器是否跳过系统、框架、库和其他非用户调用。/JMC 编译器选项支持在您的本机 C++ 代码中进行“仅我的代码”调试。启用 /JMC 后,编译器会在函数序言中插入对辅助函数 __CheckForDebuggerJustMyCode 的调用。helper 函数提供了支持 Visual Studio 调试器 Just My Code 步骤操作的钩子。

为了验证“神秘”的功能确实对应__CheckForDebuggerJustMyCode,你可以把断点在Visual Studio中main的第一行,按下F5开始调试,然后ctrl+ alt+d向大会由编译器生成的显示窗口。你会得到这样的东西:

主要的拆卸

提供完整的代码编译器版本和编译器/链接器参数

在这里,我使用 msvc 2017 x64 进行了检查,但没有发现任何神秘功能

我也在编译器资源管理器上使用多个版本的 msvc x64运行它,他们也不吐出它

:\>cl 2>&1 | head -n 1
Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27045 for x64

:\>cat myst.cpp
#include <stdio.h>
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);
}

:\>cl /Zi /W4 /analyze /Od /EHsc /nologo myst.cpp /link /release
myst.cpp
myst.cpp(4): warning C4305: 'return': truncation from '__int64' to 'long'
myst.cpp(4): warning C4309: 'return': truncation of constant value
myst.cpp(2): warning C4100: 'j': unreferenced formal parameter
myst.cpp(2): warning C4100: 'i': unreferenced formal parameter
myst.cpp(2): warning C4100: 'h': unreferenced formal parameter
myst.cpp(2): warning C4100: 'g': unreferenced formal parameter
myst.cpp(2): warning C4100: 'f': unreferenced formal parameter
myst.cpp(2): warning C4100: 'e': unreferenced formal parameter
myst.cpp(2): warning C4100: 'd': unreferenced formal parameter

:\>cdb -c "uf myst!main;q" myst.exe | awk /Reading/,/quit/
0:000> cdb: Reading initial command 'uf myst!main;q'
myst!main:
00007ff6`538a1040 4883ec58        sub     rsp,58h
00007ff6`538a1044 c74424480a000000 mov     dword ptr [rsp+48h],0Ah
00007ff6`538a104c c744244009000000 mov     dword ptr [rsp+40h],9
00007ff6`538a1054 c744243808000000 mov     dword ptr [rsp+38h],8
00007ff6`538a105c c744243007000000 mov     dword ptr [rsp+30h],7
00007ff6`538a1064 c744242806000000 mov     dword ptr [rsp+28h],6
00007ff6`538a106c c744242005000000 mov     dword ptr [rsp+20h],5
00007ff6`538a1074 41b904000000    mov     r9d,4
00007ff6`538a107a 41b803000000    mov     r8d,3
00007ff6`538a1080 ba02000000      mov     edx,2
00007ff6`538a1085 b901000000      mov     ecx,1
00007ff6`538a108a e871ffffff      call    myst!test (00007ff6`538a1000)
00007ff6`538a108f 33c0            xor     eax,eax
00007ff6`538a1091 4883c458        add     rsp,58h
00007ff6`538a1095 c3              ret
quit:

:\>cdb -c "uf myst!test;q" myst.exe | awk /Reading/,/quit/
0:000> cdb: Reading initial command 'uf myst!test;q'
myst!test:
00007ff6`538a1000 44894c2420      mov     dword ptr [rsp+20h],r9d
00007ff6`538a1005 4489442418      mov     dword ptr [rsp+18h],r8d
00007ff6`538a100a 89542410        mov     dword ptr [rsp+10h],edx
00007ff6`538a100e 894c2408        mov     dword ptr [rsp+8],ecx
00007ff6`538a1012 4883ec28        sub     rsp,28h
00007ff6`538a1016 448b4c2440      mov     r9d,dword ptr [rsp+40h]
00007ff6`538a101b 448b442438      mov     r8d,dword ptr [rsp+38h]
00007ff6`538a1020 8b542430        mov     edx,dword ptr [rsp+30h]
00007ff6`538a1024 488d0d15c30400  lea     rcx,[myst!__xt_z+0x8 (00007ff6`538ed340)]
00007ff6`538a102b e8d0000000      call    myst!printf (00007ff6`538a1100)
00007ff6`538a1030 b8efcd9a78      mov     eax,789ACDEFh
00007ff6`538a1035 4883c428        add     rsp,28h
00007ff6`538a1039 c3              ret
quit:

正如 bart1e 在他的回答中指出的那样编辑它是 debug_just_my_code 参数 /jmc 没有记录在 cl /? 与 2017 年相比,但已实施

:\>cl /? >args.txt
Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27045 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.


:\>grep -ir jmc args.txt

:\>grep -ir jm args.txt

:\>grep -ir j args.txt
/Fm[file] name map file                 /Fo<file> name object file
/Fm: <file> name map file               /Fo: <file> name object file
/P preprocess to file                   /Fx merge injected code to file
    c++latest - latest draft standard (feature set subject to change)
/permissive[-] enable some nonconforming code to compile (feature set subject to change) (on by default)
  alignedNew[-]         enable C++17 alignment of dynamically allocated objects (on by default)
/Zp[n] pack structs on n-byte boundary  /Zl omit default library name in .OBJ
/bigobj generate extended object format /c compile only, no link
/J default char type is unsigned
/Yd put debug info in every .OBJ        /Yl[sym] inject .PCH ref for debug lib

:\> 

传递 jmc 并导致我的代码函数调用添加

:\>cdb -c "uf myst!main;uf myst!__CheckForDebuggerJustMyCode;q" myst.exe | awk /Reading/,/quit/
0:000> cdb: Reading initial command 'uf myst!main;uf myst!__CheckForDebuggerJustMyCode;q'

myst!main:
00007ff7`3c031050 4883ec58        sub     rsp,58h
00007ff7`3c031054 488d0da84f0600  lea     rcx,[myst!_DebuggerCurrentSteppingThreadId <PERF> (myst+0x66003) (00007ff7`3c096003)]
00007ff7`3c03105b e844010000      call    myst!__CheckForDebuggerJustMyCode (00007ff7`3c0311a4)
00007ff7`3c031060 c74424480a000000 mov     dword ptr [rsp+48h],0Ah
00007ff7`3c031068 c744244009000000 mov     dword ptr [rsp+40h],9
00007ff7`3c031070 c744243808000000 mov     dword ptr [rsp+38h],8
00007ff7`3c031078 c744243007000000 mov     dword ptr [rsp+30h],7
00007ff7`3c031080 c744242806000000 mov     dword ptr [rsp+28h],6
00007ff7`3c031088 c744242005000000 mov     dword ptr [rsp+20h],5
00007ff7`3c031090 41b904000000    mov     r9d,4
00007ff7`3c031096 41b803000000    mov     r8d,3
00007ff7`3c03109c ba02000000      mov     edx,2
00007ff7`3c0310a1 b901000000      mov     ecx,1
00007ff7`3c0310a6 e855ffffff      call    myst!test (00007ff7`3c031000)
00007ff7`3c0310ab 33c0            xor     eax,eax
00007ff7`3c0310ad 4883c458        add     rsp,58h
00007ff7`3c0310b1 c3              ret


myst!__CheckForDebuggerJustMyCode:
00007ff7`3c0311a4 48894c2408      mov     qword ptr [rsp+8],rcx
00007ff7`3c0311a9 4883ec38        sub     rsp,38h
00007ff7`3c0311ad 488b442440      mov     rax,qword ptr [rsp+40h]
00007ff7`3c0311b2 4889442420      mov     qword ptr [rsp+20h],rax
00007ff7`3c0311b7 488b442440      mov     rax,qword ptr [rsp+40h]
00007ff7`3c0311bc 0fb600          movzx   eax,byte ptr [rax]
00007ff7`3c0311bf 85c0            test    eax,eax
00007ff7`3c0311c1 7418            je      myst!__CheckForDebuggerJustMyCode+0x37 (00007ff7`3c0311db)

myst!__CheckForDebuggerJustMyCode+0x1f:
00007ff7`3c0311c3 833d7ef4050000  cmp     dword ptr [myst!__DebuggerCurrentSteppingThreadId (00007ff7`3c090648)],0
00007ff7`3c0311ca 740f            je      myst!__CheckForDebuggerJustMyCode+0x37 (00007ff7`3c0311db)

myst!__CheckForDebuggerJustMyCode+0x28:
00007ff7`3c0311cc ff152ebe0400    call    qword ptr [myst!_imp_GetCurrentThreadId (00007ff7`3c07d000)]
00007ff7`3c0311d2 390570f40500    cmp     dword ptr [myst!__DebuggerCurrentSteppingThreadId (00007ff7`3c090648)],eax
00007ff7`3c0311d8 7501            jne     myst!__CheckForDebuggerJustMyCode+0x37 (00007ff7`3c0311db)

myst!__CheckForDebuggerJustMyCode+0x36:
00007ff7`3c0311da 90              nop

myst!__CheckForDebuggerJustMyCode+0x37:
00007ff7`3c0311db 4883c438        add     rsp,38h
00007ff7`3c0311df c3              ret
quit: