您的位置:首页 > 编程语言 > C语言/C++

汇编语言函数调用小结及缓冲区溢出的利用

2016-11-19 21:41 537 查看
本文以C语言中的
write
函数为例,以汇编代码的形式,探讨该函数运行时内存中的情况。

这是我们常见的一个
write()
函数:

write(fd,buf,len);


这个函数有三个参数:文件描述符fd、数组buf的地址、读取数据的长度len。

它的作用是从buf中读取长度len个字节的数据。

举个例子:

write(1,"Hello World\n",13);


这条语句作用是向标准输出中打印
"Hello World"


它的汇编代码如下:



从上面的代码中可以发现,在
write
函数被调用(
call <write@plt>
)之前时,有
3
MOV
指令,先将
write
函数的各个参数存入栈中(C语言中参数以从右向左的顺序入栈)。

参数入栈后,执行
call
指令,
call
相当于
push ip
,
jmp <addr>
,也就是先将
IP
(函数的返回地址,即下一条指令的地址)压入栈中,然后
JMP
至被调用函数的地址。

参数入栈后栈的情况如下:



call <write>
后栈的情况如下:



其中
0x08048457
write
函数执行完后的返回地址。

现在重新梳理一下,调用
write
函数时依次压入栈中的分别是长度
len
、数组
buf
的首地址、文件描述符
fd
write
函数的返回地址,如下图所示:



那么如果程序中存在缓存区溢出漏洞(比如
read
函数读取的数据长度比实际缓存区长时),我们可以通过覆盖函数的返回地址,来控制程序的执行流程。

漏洞函数:

char buffer[100];
read(0,buffer,128);




我们可以将函数的返回地址覆写为
write
函数的地址,然后在栈中构造
write
函数的返回地址和参数,这样我们便可以使用
write
函数来泄露内存中的信息,比如某函数在
libc
中的地址等等。



被修改后的栈是这样的:



当正常的函数调用
ret
指令返回时,
ret
指令相当于
pop ip
,也就是说
write
的地址会赋给
ip
,相当于执行了一条
jmp <write>


现在栈变成了这样:



是不是和上面讲的
write
函数的栈一样啦?

不过也不一样,现在这个栈中的
fd
,
buf
,
len
是我们可以任意指定的,即: 指哪儿就可以打哪儿 :)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐