{"title": "\u7a0b\u5e8f\u7684\u5806\u548c\u6808", "update_time": "2021-01-31 20:56:01", "tags": "stack", "pid": "346", "icon": "default.png"}
## 堆和栈 ##  proc ```shell script /proc/[pid]/mem This file can be used to access the pages of a process's memory through open(2), read(2), and lseek(2). Permission to access this file is governed by a ptrace access mode PTRACE_MODE_ATTACH_FSCREDS check; see ptrace(2). /proc/[pid]/maps address perms offset dev inode pathname A file containing the currently mapped memory regions and their access permissions. See mmap(2) for some further information about memory mappings. Permission to access this file is governed by a ptrace access mode PTRACE_MODE_READ_FSCREDS check; see ptrace(2). The address field is the address space in the process that the mapping occupies. The perms field is a set of permissions: r = read w = write x = execute s = shared p = private (copy on write) The offset field is the offset into the file/whatever; dev is the device (major:minor); inode is the inode on that device. 0 indicates that no inode is associated with the memory region, as would be the case with BSS (uninitialized data). The pathname field will usually be the file that is backing the mapping. For ELF files, you can easily coordinate with the offset field by looking at the Offset field in the ELF program headers (readelf -l). There are additional helpful pseudo-paths: [stack] The initial process's (also known as the main thread's) stack. [stack:
] (since Linux 3.4) A thread's stack (where the
is a thread ID). It corresponds to the /proc/[pid]/task/[tid]/ path. [vdso] The virtual dynamically linked shared object. See vdso(7). [heap] The process's heap. If the pathname field is blank, this is an anonymous mapping as obtained via mmap(2). There is no easy way to coordinate this back to a process's source, short of running it through gdb(1), strace(1), or similar. ``` ## Stack - 函数调用过程 ## ```shell script 函数调用 函数调用分为如下几步:数据结构 参数入栈: 将参数按照调用约定(C 是从右向左)依次压入系统栈中; 返回地址入栈: 将当前代码区调用指令的下一条指令地址压入栈中,供函数返回时继续执行; 代码跳转: 处理器将代码区跳转到被调用函数的入口处; 栈帧调整: 1.将调用者的ebp压栈处理,保存指向栈底的ebp的地址(方便函数返回以后的现场恢复),此时esp指向新的栈顶位置; push ebp 2.将当前栈帧切换到新栈帧(将eps值装入ebp,更新栈帧底部), 这时ebp指向栈顶,而此时栈顶就是old ebp mov ebp, esp 3.给新栈帧分配空间 sub esp, XXX 函数返回 函数返回分为如下几步:函数 保存被调用函数的返回值到 eax 寄存器中 mov eax, xxx 恢复 esp 同时回收局部变量空间 mov ebp, esp 将上一个栈帧底部位置恢复到 ebp pop ebp 弹出当前栈顶元素,从栈中取到返回地址,并跳转到该位置 ret ``` 结构 ```shell script high address +---------------+ | | | Old | | Stack | | Frame | | | -------------- +---------------+ ^ ^ | Parameters | | caller +---------------+ | v | Return addr | --- +---------------+ Stack Frame ^ | Old ebp | <------- ebp Frame Pointer | +---------------+ | callee | Registers | | | +---------------+ v v | Local vars | -------------- +---------------+ <------- esp Stack Pointer low address ``` 特殊寄存器 ```shell script ESP (points to the top of the stack) EBP (is used as a reference when accessing local variables and arguments of the function) EIP (points to the address of the next instruction) ``` ## GDB Command ## ```shell script (gdb) bt #0 0x00007f7e74107480 in __nanosleep_nocancel () from /lib64/libc.so.6 #1 0x00007f7e74107334 in sleep () from /lib64/libc.so.6 #2 0x0000000000400554 in t (ts=86400) at t.c:5 #3 0x0000000000400569 in main () at t.c:10``` ```shell script (gdb) f 2 #2 0x0000000000400554 in t (ts=86400) at t.c:5 5 sleep(ts); ``` ```shell script (gdb) info f Stack level 2, frame at 0x7ffe1e837180: rip = 0x400554 in t (t.c:5); saved rip 0x400569 called by frame at 0x7ffe1e837190, caller of frame at 0x7ffe1e837160 source language c. Arglist at 0x7ffe1e837170, args: ts=86400 Locals at 0x7ffe1e837170, Previous frame's sp is 0x7ffe1e837180 Saved registers: rbp at 0x7ffe1e837170, rip at 0x7ffe1e837178 ``` 参数值就在`caller of frame` 和 `Arglist at` 指向的地址间 ```shell script (gdb) x/1xg 0x7ffe1e837160 0x7ffe1e837160: 0x0000000000400570 (gdb) 0x7ffe1e837168: 0x0001518000400440 (gdb) 0x7ffe1e837170: 0x00007ffe1e837180 (gdb) 0x7ffe1e837178: 0x0000000000400569 (gdb) 0x7ffe1e837180: 0x0000000000000000 (gdb) 0x7ffe1e837188: 0x00007f7e7406bb15 (gdb) 0x7ffe1e837190: 0x0000002000000000 (gdb) 0x7ffe1e837198: 0x00007ffe1e837268 (gdb) 0x7ffe1e8371a0: 0x0000000100000000 (gdb) 0x7ffe1e8371a8: 0x000000000040055b (gdb) 0x7ffe1e8371b0: 0x0000000000000000 ``` ```shell script 0x7ffe1e8371b0: 0x0000000000000000 0x7ffe1e8371a8: 0x000000000040055b 0x7ffe1e8371a0: 0x0000000100000000 0x7ffe1e837198: 0x00007ffe1e837268 ... 上一个frame,这个应该也是返回地址 0x7ffe1e837190: 0x0000002000000000 <- [frame 3] main函数 0x7ffe1e837188: 0x00007f7e7406bb15 程序返回地址 <__libc_start_main+245> 0x7ffe1e837180: 0x0000000000000000 <- [frame 2] t函数 0x7ffe1e837178: 0x0000000000400569 <- [ Return addr] 0x7ffe1e837170: 0x00007ffe1e837180 <- [old ebp] 0x7ffe1e837168: 0x0001518000400440 <- [Local vars] locals=0x00015180 函数? 00400440 0x7ffe1e837160: 0x0000000000400570 ``` 0x7ffe1e837190 ```shell script 0x7ffe1e837160: 0x00400570 (gdb) 0x7ffe1e837164: 0x00000000 (gdb) 0x7ffe1e837168: 0x00400440 (gdb) 0x7ffe1e83716c: 0x00015180 (gdb) 0x7ffe1e837170: 0x1e837180 (gdb) 0x7ffe1e837174: 0x00007ffe (gdb) 0x7ffe1e837178: 0x00400569 (gdb) 0x7ffe1e83717c: 0x00000000 (gdb) 0x7ffe1e837180: 0x00000000 (gdb) 0x7ffe1e837184: 0x00000000 (gdb) 0x7ffe1e837188: 0x7406bb15 (gdb) 0x7ffe1e83718c: 0x00007f7e (gdb) 0x7ffe1e837190: 0x00000000 ``` 倒过来看 ```shell script 0x7ffe1e837190: 0x00000000 [frame 3 start ] t.c main函数 0x7ffe1e83718c: 0x00007f7e 0x7ffe1e837188: 0x7406bb15 0x7ffe1e837184: 0x00000000 0x7ffe1e837184: 0x00000000 0x7ffe1e837180: 0x00000000 [frame 2 start ] t.c t函数 <- Arglist|Locals@[frame3] at 0x7ffe1e83717c: 0x00000000 0x7ffe1e837178: 0x00400569 <- saved reg rip 0x7ffe1e837174: 0x00007ffe 0x7ffe1e837170: 0x1e837180 <- Arglist|Locals@[frame2] at 0x7ffe1e837170 0x7ffe1e83716c: 0x00015180 <- 86400 也就是我们调用的参数 0x7ffe1e837168: 0x00400440 <- 代码段 <_start>函数地址 -> 这个应该是调用的函数的地址 0x7ffe1e837164: 0x00000000 0x7ffe1e837160: 0x00400570 [frame 1 start] <- 代码段 0x400570 <__libc_csu_init>: # 下一个frame ``` ```shell script (gdb) info frame 3 Stack frame at 0x7ffd1ea565f0: rip = 0x40057d in c (t.c:11); saved rip 0x400592 called by frame at 0x7ffd1ea56600, caller of frame at 0x7ffd1ea565c0 source language c. Arglist at 0x7ffd1ea565e0, args: t1=900, t2=4 Locals at 0x7ffd1ea565e0, Previous frame's sp is 0x7ffd1ea565f0 Saved registers: rbp at 0x7ffd1ea565e0, rip at 0x7ffd1ea565e8 (gdb) x/1xg 0x7ffd1ea565a0 ``` ```shell script (gdb) x/1xg 0x7ffd1ea565a0 0x7ffd1ea565a0: 0x0000000000000076 (gdb) 0x7ffd1ea565a8: 0x0001518000000001 (gdb) 0x7ffd1ea565b0: 0x00007ffd1ea565e0 (gdb) 0x7ffd1ea565b8: 0x000000000040057d (gdb) 0x7ffd1ea565c0: 0x0000000000000000 (gdb) 0x7ffd1ea565c8: 0x0000038400000004 (gdb) 0x7ffd1ea565d0: 0x00000000004005a0 (gdb) 0x7ffd1ea565d8: 0x00000e1000400440 (gdb) 0x7ffd1ea565e0: 0x00007ffd1ea565f0 (gdb) 0x7ffd1ea565e8: 0x0000000000400592 (gdb) 0x7ffd1ea565f0: 0x0000000000000000 ``` ```shell script 0x7ffd1ea565f0: 0x0000000000000000 <- previours frame 0x7ffd1ea565e8: 0x0000000000400592 <- function main 调用c函数后的下一个指令 0x7ffd1ea565e0: 0x00007ffd1ea565f0 <- ebp地址?下面是args start #push %rbp 0x7ffd1ea565d8: 0x00000e1000400440 <- 参数 0x00000e10(3600) 0x00400440 (返回地址) function _start 0x7ffd1ea565d0: 0x00000000004005a0 <- 代码地址 function __libc_csu_init 0x7ffd1ea565c8: 0x0000038400000004 <- 参数 0x00000384(900) 0x00000004(4) 0x7ffd1ea565c0: 0x0000000000000000 0x7ffd1ea565b8: 0x000000000040057d <- function c 返回地址 0x7ffd1ea565b0: 0x00007ffd1ea565e0 <- ebp地址? 0x7ffd1ea565a8: 0x0001518000000001 <- 看起来是参数 86400 是给function 他的参数 0x7ffd1ea565a0: 0x0000000000000076 ``` ## 相关资料 ## 1. CSAPP 351 https://courses.cs.washington.edu/course00400554s/cse351/17wi/videos.html 2. https://introspelliam.github.io/2017/09/10/pwn/Linux%E5%A0%86%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E6%B7%B1%E5%85%A5%E5%88%86%E6%9E%90%E4%B8%8A/ 3. https://paper.seebug.org/papers/scz/unix/201811271208.txt 4. https://www.shangmayuan.com/a/7192bfdf80404f7e9b0bea19.html 5. http://wsfdl.com/linux/2015/05/16/%E7%90%86%E8%A7%A3stack-func.html# 6. https://en.wikipedia.org/wiki/Call_stack 7. https://resources.infosecinstitute.com/stack-analysis-with-gdb/#gref