【问题标题】:Assembly x86 code thought process mistake in stack push/pop in a recursive function递归函数中堆栈推送/弹出中的汇编 x86 代码思考过程错误
【发布时间】:2022-07-22 01:34:41
【问题描述】:

在下图中,有一个汇编代码。无论我尝试了多少,我都无法完全理解它是如何工作的。这是我的思考过程: 我假设我输入了 3。

  1. mov bp, sp //使bp指针等于sp

  2. mov ax, [bp+2]// ax 等于 3。3 存储在 ax bp 上方 2 个地址和 return 上方 1 个地址。

  3. cmp a, 1 //3不等于1

  4. jz exit // 代码不跳转退出原因3不等于1。

  5. push ax // 3 现在在 bp-1

  6. 十进制 ax // ax=2

  7. push ax // ax=2 被推到 bp-2

    所以现在我们有了堆栈

    return address
    bp
    3
    2
    
  8. call func // func 被再次调用

  9. add sp, 2 //函数结束后返回地址,sp+2删除变量

代码继续,直到堆栈看起来像这样:

return address
bp
3
2

返回添加sp,2

bp
2
1

返回添加sp,2

bp

现在 ax=1,它会跳到退出。
ret 现在会做什么?进展如何?
我怎么知道是_cdecl还是_stdcall

【问题讨论】:

  • push axsp 递减 2 而不是 1。ret 将返回给调用者。如果递归将在call func 之后并继续add sp, 2。对于顶层,它将返回给原始调用者。该函数是_cdecl,因为调用者删除了参数并且代码没有使用ret immediate
  • 你可以说它不是__stdcall,因为它没有使用ret 2。 arg-passing 是__cdecl,因为它们在堆栈上并被调用者弹出。但是这个函数破坏了 BP 和 BX,所以它也没有完全遵循__cdecl;这些寄存器通常是调用保留的。 en.wikipedia.org/wiki/… 没有在表中提到保留调用和调用破坏的寄存器,并且在 16 位内容中,这与在 32 位中的一致程度较低。之前那篇文章en.wikipedia.org/wiki/X86_calling_conventions#cdecl 提到了regs。
  • 你们能否向我解释一下代码是如何工作的。真的会救我。

标签: assembly reverse-engineering x86-16


【解决方案1】:

这是您的汇编程序逐行翻译成等效的 C 程序的过程。这可能更容易推理,因为这些操作在 C 中更明显。

#include <inttypes.h>

#define SIZE 0x200
intptr_t virtual_stack[SIZE];
intptr_t *sp = virtual_stack + SIZE - 1;

intptr_t func(void) {
  intptr_t *bp = sp; // mov bp, sp
  intptr_t ax = bp[1]; // mov ax, [bp + 2]
  if (ax == 1) // cmp ax, 1
    goto exit; // jz exit
  *--sp = ax; // push ax
  --ax; // dec ax
  *--sp = ax; // push ax
  *--sp = (intptr_t)func; ax = func(); // call func
  ++sp; // add sp, 2
  intptr_t bx = *sp++; // pop bx
  ax = ax * bx; // mul bx
exit:
  ++sp; return ax; // ret
}

如果还是没有头绪,可以直接调用函数看看输出结果。

#include <stdio.h>

int main(void) {
  for (int i = 1; i <= 8; ++i) {
    intptr_t r;
    *sp = x; *--sp = (intptr_t)main; r = func();
    printf("%"PRIdPTR"\n", r);
  }
  return 0;
}

输出是,

1
2
6
24
120
720
5040
40320

这应该是一个非常熟悉的数字序列。如果不是,请搜索OEIS中的序列。

【讨论】:

    猜你喜欢
    • 2014-08-04
    • 2017-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-17
    • 2020-09-23
    • 1970-01-01
    相关资源
    最近更新 更多