【问题标题】:What is the first variables of my stack program?我的堆栈程序的第一个变量是什么?
【发布时间】:2019-12-17 12:20:13
【问题描述】:

我已经开始组装了。

我不明白为什么我在 argc 之前有两个变量。

0000 和 0008 是什么?

global _main

section .text
_main:
    ; write
    mov rax, 0x2000004
    mov rdi, 0x1
    mov rsi, [rsp+24]
    mov rdx, 3
    syscall

    ; return (0)
    mov rax, 0x2000001
    mov rdi, 0x0
    syscall

我在 macOSX Mojave 上编译:

nasm -f macho64 ex01.s && ld -macosx_version_min 10.14 -lSystem ex01.o

【问题讨论】:

  • 0000和0008是什么?您的 argv 是一个指针数组,在最后一个参数之后带有一个标记 NULLargv 始终包含可执行文件名称 (argv[0]),由于没有传递其他参数,argv[1] 将是 NULL
  • en.wikipedia.org/wiki/Crt0,它说明了_start设置一些参数和调用_main。您可能会尝试查找在您的环境中使用的特定 _start,以查看如何调用 _main。我的猜测是 +0 是 _start 的返回地址。
  • 不要发布终端输出的图片。相反,将此输出作为文本发布。

标签: macos assembly x86-64 callstack


【解决方案1】:

您的目标是现代 MacOS,因此 ld 将发出 dyld 辅助 LC_MAIN 加载命令用于入口点处理。 [rsp] 是 libdyld 的返回地址 _start 函数结尾:

mov        edi, eax ; pass your process return code as 1st argument under System V 64bit ABI
call       exit ;from libSystem
hlt

这意味着您不需要像在中那样通过系统调用退出进程:

; return (0)
mov rax, 0x2000001
mov rdi, 0x0
syscall

改为:

xor eax,eax
ret

就足够了(这就是编译器会发出 btw 的内容)。

您的缓冲区也将在 ret / libdyld 方法中被刷新。这与您正在执行的系统写入调用无关,但可能与 printf 例如。

这是一个很棒的article,它描述了很多细节。

【讨论】:

    【解决方案2】:

    我不明白为什么我在 argc 之前有两个变量。

    你写的是main,而不是_start你的返回地址上面的堆栈空间是“不是你的”; CRT 启动代码在调用 main 之前使用了多少堆栈空间,或者它在 argc/argv/env 和对 main 的调用之间留在堆栈上的空间没有标准。

    main(int argc, char **argv, char **envp) 中,您会在EDI 中找到argc,在RSI 中找到指向argv[] 的指针,在RDX 中找到指向envp[] 的指针。

    但我们可以看看有什么逆向工程main的调用者:


    0000 开头的数字是相对于 RSP 的字节偏移量。无论生成什么图像,都将 8 字节堆栈“槽”作为整数进行转储和分析,如果它们指向有效内存,则作为指针进行分析。

    堆栈上的所有这些东西都是通过调用main_start 代码把它放在那里的,或者内核在进入用户空间之前把它放在那里。

    • [rsp + 0]main 的返回地址,所以它指向代码。大概_startcall main / mov edi, eax / call exit 之类的代码调用你的主函数,以将你的返回值传递给exit(),如果main 返回(你的没有返回)。因此,您的返回地址指向 mov edi, eax 是有道理的。
    • 0 可能是一个帧指针标记,以便使用 -fno-omit-frame-pointer 编译的代码能够回溯保存的 RBP 值链。在_start 中推送0 会终止该链表,如果调用者随后执行mov rbp, rsp,那么其被调用者中的push rbp 将推送指向该终止符的指针。 x86-64 System V ABI 文档建议这样做。

    其余条目看起来与_start堆栈的条目到用户空间状态完全相同

    • 1 = argc 表示您在没有 args 的情况下运行程序,因此 shell 传递了 1 个隐式第一个 arg(程序名称,argv[0])。
    • 然后是一个以 NULL 结尾的 argv[](不是指向 argv 的 指针,实际的数组就在堆栈上)。第一个元素是指向包含可执行文件路径的字符串的指针,因为您的调用者选择照常将其传递给execve()
    • 然后是一个以 NULL 结尾的 envp[] 数组。同样不是char **envp,而是按值排列的实际数组。每个条目都是环境中条目的char*

    同样,x86-64 System V ABI 记录了这种堆栈布局。 MacOS 遵循 x86-64 System V ABI。 https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI


    (不过,我对堆栈对齐感到惊讶。在 Linux 上,RSP 在进入用户空间时是 16 字节对齐的;它不是函数,也不是 called,因此堆栈上没有返回值。所以argc 是 16 字节对齐的。但是在这里,您的代码似乎表明 main 中的 rspargc 具有相同的对齐方式。这意味着 main 的调用者与堆栈相距 8 个字节call 之前的 16 字节对齐。也许这就是 OS X 一直在做的事情?)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-12
      • 2021-10-13
      • 2019-02-22
      • 2019-08-28
      • 1970-01-01
      • 2015-01-09
      • 2022-09-28
      • 1970-01-01
      相关资源
      最近更新 更多