首页
学习
活动
专区
圈层
工具
发布
30 篇文章
1
《coredump问题原理探究》Linux x86版6.4节虚函数
2
《coredump问题原理探究》Linux x86版7.3节List对象
3
《coredump问题原理探究》Linux x86版4.4节函数的逆向之循环结构
4
《coredump问题原理探究》windows版8.5节堆布局空闲堆块遍历
5
《coredump问题原理探究》windows版9.1节栈溢出
6
《coredump问题原理探究》Linux x86版3.2节栈布局之函数桢
7
《coredump问题原理探究》Linux x86版3.4节栈布局之函数参数
8
《coredump问题原理探究》Linux x86版4.1节函数的逆向之序言
9
《coredump问题原理探究》Linux x86版4.2节函数的逆向之顺序结构
10
《coredump问题原理探究》Linux x86版4.3节函数的逆向之条件结构
11
《coredump问题原理探究》windows版3.4节coredump例子
12
《coredump问题原理探究》windows版第四章函数的逆向
13
《coredump问题原理探究》windows版5.1节基本数据类型
14
《coredump问题原理探究》windows版5.2节数组
15
《coredump问题原理探究》windows版5.3节结构体
16
《coredump问题原理探究》windows版5.4节联合体
17
《coredump问题原理探究》windows版6.1节无成员变量的类
18
《coredump问题原理探究》windows版6.2节有成员变量的类
19
《coredump问题原理探究》windows版6.3节虚函数
20
《coredump问题原理探究》windows版6.4节单继承
21
《coredump问题原理探究》windows版6.5节多继承
22
《coredump问题原理探究》windows版7.1节vector
23
《coredump问题原理探究》windows版7.2节list
24
《coredump问题原理探究》windows版7.3节map
25
《coredump问题原理探究》windows版7.4节set
26
《coredump问题原理探究》windows版7.5节iterator
27
《coredump问题原理探究》windows版7.6节string
28
《coredump问题原理探究》windows版8.1节堆布局背景
29
《coredump问题原理探究》windows版8.2节堆布局堆块结构
30
《coredump问题原理探究》windows版3.1节函数桢

《coredump问题原理探究》Linux x86版3.4节栈布局之函数参数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1344462

上面已经讨论当有局部变量空参数的函数时栈的布局,那么当函数具有局部变量和参数,那么栈布局又会怎样?

先看一个例子:

代码语言:javascript
复制
int func( int c, char* s, int off )
{
     int a = 0x12345678;
     int *p = &a;
     int res = c + *( s + off );

     return *p + res;
}
 
int main()
{
     int b = 0x87654321;
 
     return b + func( 0x100, "hello", 3 );
}

看一下main函数的汇编:

代码语言:javascript
复制
(gdb) disassemble main
Dump of assembler code for function main:
   0x08048405 <+0>:     push   %ebp
   0x08048406 <+1>:     mov    %esp,%ebp
   0x08048408 <+3>:     sub    $0x1c,%esp
   0x0804840b <+6>:     movl   $0x87654321,-0x4(%ebp)
   0x08048412 <+13>:    movl   $0x3,0x8(%esp)
   0x0804841a <+21>:    movl   $0x80484d4,0x4(%esp)
   0x08048422 <+29>:    movl   $0x100,(%esp)
   0x08048429 <+36>:    call   0x80483d0 <_Z4funciPci>
   0x0804842e <+41>:    mov    -0x4(%ebp),%edx
   0x08048431 <+44>:    add    %edx,%eax
   0x08048433 <+46>:    leave  
   0x08048434 <+47>:    ret    
End of assembler dump.

从0x08048412 到0x08048422 的汇编语句可以看到,esp, esp+4,esp+8这三个单元依次存放着0x100,0x80484d4,3。而0x80484d4存放的内容可以如下获得:

代码语言:javascript
复制
(gdb) x /s 0x80484d4
0x80484d4 <__dso_handle+4>:      "hello"

可见,刚好和依次传入func函数的参数0x100,”hello”, 3一样。可知正是那三个参数。

当执行

代码语言:javascript
复制
  0x08048429 <+36>:    call   0x80483d0 <_Z4funciPci>

进入func函数时,会把main函数的返回地址压入栈,这时,栈的布局应该如下:

代码语言:javascript
复制
esp:返回地址
esp+4:0x100
esp+8:”hello”的地址
esp+0xC:3

那么当func函数执行完它的开头特征指令

代码语言:javascript
复制
push   %ebp
mov    %esp,%ebp

栈的布局应该如下:

代码语言:javascript
复制
esp:main函数桢指针
esp+4:返回地址
esp+8:0x100
esp+0xC:“hello”的地址
esp+0x10:3

由于这时ebp和esp相等,而esp会用于push、pop、sub、add等操作来申请和释放局部变量空间。所以栈布局应该是ebp为基准。所以上面栈布局应该表述如下:

代码语言:javascript
复制
ebp:main函数桢指针
ebp+4:返回地址
ebp+8:0x100
ebp+0xC:“hello”的地址
ebp+0x10:3

在func打断点来看一下,是不是如此:

代码语言:javascript
复制
(gdb) tbreak func                  
Temporary breakpoint 1 at 0x80483d6
(gdb) r
Starting program: /home/buckxu/work/3/3/xuzhina_dump_c3_s3 

Temporary breakpoint 1, 0x080483d6 in func(int, char*, int) ()
(gdb) x /8x $ebp
0xbffff4b4:     0xbffff4d8      0x0804842e      0x00000100      0x080484d4
0xbffff4c4:     0x00000003      0x0804844b      0xb7fbeff4      0x08048440
(gdb) info symbol 0x0804842e
main + 41 in section .text of /home/buckxu/work/3/3/xuzhina_dump_c3_s3
(gdb) x /s 0x080484d4
0x80484d4 <__dso_handle+4>:      "hello"

果然如此。

由上面的探讨可知,函数的参数有如下规则:

1.      参数在栈上的排列和它的声明顺序是一致的。

2.      第一个参数是在ebp+8开始,第二个参数是放在ebp+0xC,其它依此类推。

到了这里,可以知道完整的栈布局应该如下图:

从图上还可以得到这样的规律:

如果两个函数桢指针fp1,fp2,fp1正好指向fp2的单元。那么它们满足这样的关系:

1.      esp < fp1 < fp2

2.      fp1的下一个单元的内容ret1肯定是返回地址,fp2的下一个单元的内容ret2也肯定是返回地址。即ret1,ret2都可以用info symbol命令来获取它们位于哪些函数里。

探讨完了函数参数的知识,在这里探究一下main函数的参数。虽然main函数在写的时候,可以是

代码语言:javascript
复制
int main()

也可以是

代码语言:javascript
复制
int main( int argc, char* argv[] )

实际上,main函数的原型是这样的:

代码语言:javascript
复制
int main( int argc, char* argv[], char* envp[ ] )

从上面的

代码语言:javascript
复制
(gdb) tbreak func                  
Temporary breakpoint 1 at 0x80483d6
(gdb) r
Starting program: /home/buckxu/work/3/3/xuzhina_dump_c3_s3 

Temporary breakpoint 1, 0x080483d6 in func(int, char*, int) ()
(gdb) x /8x $ebp
0xbffff4b4:     0xbffff4d8      0x0804842e      0x00000100      0x080484d4
0xbffff4c4:     0x00000003      0x0804844b      0xb7fbeff4      0x08048440

可知,main函数桢指针是0xbffff4d8。现在看一下0xbffff4d8 +8,0xbffff4d8+0xC, 0xbffff4d8+0x10的内容是什么?

代码语言:javascript
复制
(gdb) x /4x 0xbffff4d8+8
0xbffff4e0:     0x00000001      0xbffff574      0xbffff57c      0xb7fdd6b0

由于程序运行没有参数,所以argc为1,而argv的值为0xbffff574,envp的值为0xbffff57c。

先看一下argv的内容:

代码语言:javascript
复制
(gdb) x /4x 0xbffff574
0xbffff574:     0xbffff6b9      0x00000000      0xbffff6e2      0xbffff6f3
(gdb) x /s 0xbffff6b9
0xbffff6b9:      "/home/buckxu/work/3/3/xuzhina_dump_c3_s3"

正好和程序的路径一样。

再看一下envp的内容:

代码语言:javascript
复制
(gdb) x /4x 0xbffff57c   
0xbffff57c:     0xbffff6e2      0xbffff6f3      0xbffff704      0xbffff714
(gdb) x /s 0xbffff6e2
0xbffff6e2:      "XDG_SESSION_ID=2"
(gdb) x /s 0xbffff6f3
0xbffff6f3:      "HOSTNAME=xuzhina"
(gdb) x /s 0xbffff704
0xbffff704:      "SHELL=/bin/bash"
(gdb) x /s 0xbffff714
0xbffff714:      "TERM=linux"

正好和

代码语言:javascript
复制
(gdb) shell set
COLUMNS=80
DIRSTACK=()
EUID=1000
GROUPS=()
HISTCONTROL=ignoredups
HISTSIZE=1000
HOME=/home/buckxu
HOSTNAME=xuzhina
HOSTTYPE=i686
PWD=/home/buckxu/work/3/3
QT_GRAPHICSSYSTEM_CHECKED=1
SHELL=/bin/bash 
SHELLOPTS=braceexpand:hashall:interactive-comments
SHLVL=2
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
SSH_AUTH_SOCK=/tmp/ssh-gORyjvO849/agent.849
SSH_TTY=/dev/pts/0
TERM=linux
UID=1000
USER=buckxu
XDG_RUNTIME_DIR=/run/user/buckxu
XDG_SESSION_ID=2
_=/bin/gdb

一样。

下一篇
举报
领券