出于好奇 MacOS 如何準備其堆疊,我撰寫了一個 (x86_64) 匯編程式,以便在行程啟動時將堆疊頂部列印到標準輸出:
global start
start: ; entry point of the binary, called by the loader
push rsp ; push the stack pointer to stack so that we'll se that too
mov rdi, 1 ; file to write to: file descriptor 1 (STDOUT)
lea rsi, [rsp] ; source of the write: stack
mov rdx, 64 ; number of bytes to write: 64 (8 x 64-bit integers)
mov rax, 0x02000004 ; MacOS syscall number for write
syscall
mov rsi, [rsp 16] ; smoke test: argv contents
mov rdx, 16 ; we expect the argv[0] ("./inspect_stack\0") to be 16 bytes long
mov rax, 0x02000004
syscall
mov rsi, [rsp 32] ; another smoke test: envp???
mov rdx, 11
mov rax, 0x02000004
syscall
mov rax, 0x02000001 ; MacOS syscall number for exit
syscall
運行此程式并檢查輸出:
nasm -f macho64 inspect_stack.asm && ld inspect_stack.o -static -o inspect_stack && ./inspect_stack | xxd -e -g 8 -c 8
我看到這樣的東西:(添加了我自己的一些評論)
00000000: 00007ff7bfeff6b0 ........ # this is the stack pointer we pushed
00000008: 0000000000000001 ........ # argc
00000010: 00007ff7bfeff880 ........ # argv; see the smoke test result
00000018: 0000000000000000 ........ # a null pointer???
00000020: 00007ff7bfeff890 ........ # are these part of envp?
00000028: 00007ff7bfeff89f ........ # ...seems like an array of pointers stored inline?
00000030: 00007ff7bfeff8dc ........ # ...and they seem to point at a continuous buffer
00000038: 00007ff7bfeff8ed ........
00000040: 636570736e692f2e ./inspec # the result of the 1st smoke test. yes, argv[0]!
00000048: 006b636174735f74 t_stack.
00000050: 6573552f3d445750 PWD=/Use # the result of the 2nd smoke test... seems like envp?
00000058: 2f7372 rs/
因此,我了解到在程式開始時將有一個 64 位整數 (argc) 和兩個指標(指向 argv 和指向 envp)存盤到堆疊中。但是,這似乎不是真的,或者由于某種原因 envp 指標為空。但是,我們可以看到,行內存盤的 envp 陣列似乎在 null 之后開始。行程開始時堆疊的實際布局是什么?
uj5u.com熱心網友回復:
進一步檢查并添加更多引數,我注意到我的理解是錯誤的,即在堆疊頂部會有兩個指向 argv 和 envp 的指標。相反, argv 和 envp 存盤為inline,作為指向相關字串的指標陣列。兩個陣列都是空終止的,所以我看到的空值實際上是 argv 的終止符。添加更多引數會使這一點更加清晰:
nasm -f macho64 inspect_stack.asm && ld inspect_stack.o -static -o inspect_stack && ./inspect_stack first second | xxd -e -g 8 -c 8
00000000: 00007ff7bfeff698 ........
00000008: 0000000000000003 ........ # argc
00000010: 00007ff7bfeff878 x....... # argv[0]
00000018: 00007ff7bfeff888 ........ # argv[1]
00000020: 00007ff7bfeff88e ........ # argv[2]
00000028: 0000000000000000 ........ # argv end
00000030: 00007ff7bfeff895 ........ # envp[0]
00000038: 00007ff7bfeff8a4 ........ # envp[1] and so on
00000040: 636570736e692f2e ./inspec
00000048: 006b636174735f74 t_stack.
00000050: 5000646e6f636573 second.P # the second smoke test now sees argv[2]!
00000058: 3d4457 WD= # seems that the envp strings are located right after argc strings
TL;DR:我認為堆疊中的第二個和第三個 64 位值是char **argvand char **envp。相反,他們是argv[0]和argv[1]。現在,為了得到char **argvCmain期望我可以使用[rsp 8](8 bytes for skipping argc),并且為了得到char **envp我可以mov rax, [rsp]然后使用[rsp 8 rax*8 8](8 bytes for skipping argc, 然后跳過 argc 指標數量,最后再使用 8 bytes 用于跳過 null 終止符)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/447170.html
