在这个 Microsoft x64 函数调用中,参数 5 和 6 在哪里?

逆向工程 拆卸 视窗 部件 x86-64
2021-06-12 10:23:14

我正在反汇编一个按预期工作的编译 C 程序(MS/Windows x64,MinGW gcc 10.1.0)。main函数中,我有以下调用将 6 个参数传递给函数DotProduct

...
// void** m3 [rbp-0x50] = DotProduct(ptr, ptr2, 3, 2, 2, 3);
  401b9f:   48 8b 55 b8             mov    rdx,QWORD PTR [rbp-0x48] // arg2 = m2
  401ba3:   48 8b 45 c0             mov    rax,QWORD PTR [rbp-0x40] // arg1 = m1
  401ba7:   c7 44 24 28 03 00 00    mov    DWORD PTR [rsp+0x28],0x3 // arg6 = 3   (+40) <- Δ main rsp
  401bae:   00
  401baf:   c7 44 24 20 02 00 00    mov    DWORD PTR [rsp+0x20],0x2 // arg5 = 2   (+32)
  401bb6:   00
  401bb7:   41 b9 02 00 00 00       mov    r9d,0x2                  // arg4 = 2
  401bbd:   41 b8 03 00 00 00       mov    r8d,0x3                  // arg3 = 3
  401bc3:   48 89 c1                mov    rcx,rax                  // arg1 = m1
  401bc6:   e8 34 fc ff ff          call   4017ff <DotProduct>      // DotProduct(m1, m2, 3, 2, 2, 3);
  401bcb:   48 89 45 b0             mov    QWORD PTR [rbp-0x50],rax // void** m3 [rbp-0x50] = returned
...

请注意,arg5andarg6被放入[rsp+0x20]and 中[rsp+0x28]

看看调用 DotProduct 时会发生什么:

00000000004017ff <DotProduct>:
  4017ff:   55                      push   rbp
  401800:   53                      push   rbx
  401801:   48 83 ec 48             sub    rsp,0x48       // (-72)
  401805:   48 8d ac 24 80 00 00    lea    rbp,[rsp+0x80] // (-72+128) => (+56)

rbp应在-0x48+0x80相对于mainrsp,这简化了对+0x38(56,Δ mainRSP)。堆栈指针减少了 72 个字节,并且基指针放置在其上方 128 个字节(因此在前一个堆栈指针位置上方 56 个字节)。接下来,DotProduct从它们的寄存器加载前四个参数:

  40180d:   48 89 4d e0             mov    QWORD PTR [rbp-0x20],rcx
  401811:   48 89 55 e8             mov    QWORD PTR [rbp-0x18],rdx // (rbp-24) => (+56-24) => (+32) !
  401815:   44 89 45 f0             mov    DWORD PTR [rbp-0x10],r8d // (rbp-16) => (+56-16) => (+40) !
  401819:   44 89 4d f8             mov    DWORD PTR [rbp-0x8],r9d

等待!如何rdxr8d加载到这些存储单元?他们不是被论据 5 和 6 所占据吗?发生了什么?

在那之后,我们有这个:

  40181d:   8b 45 00                mov    eax,DWORD PTR [rbp+0x0]

这里发生了什么?[rbp+0x0]还没有在这个函数中初始化,那么这个内存位置是什么?第 5 条和第 6 条论点到哪里去了?

2个回答

当你在这里时4017ff: 55 push rbp

您的第 5 个参数将在 [rsp+28] 处可用
(返回地址为 8 个字节,HOMEPARAMS 为 20 个字节(用于保存通过寄存器传递的 4 个参数的空间)

两次推动和一次减法将使您的论点没有 5 可用在 0x28 + 0x8 +0x8 +0x48 = 0x80

所以 rbp+0 将保存 LEA 操作后第 5 个参数的地址

我花了很多时间(太多我无法承认)试图弄清楚这一点,我刚刚意识到它是什么,就在点击提交之后。push指令隐式减少堆栈指针。对函数的DotProduct调用调用了push3 次;一次在call指令本身中,返回地址被隐式推送,两次在函数的序言中,其中两个四字寄存器被push指令显式推送这一切都会自动减少rsp24 个字节,将 arg5 和 arg6 留在[rbp]和 处[rbp+0x8]参数不会被覆盖。