PWN! PWN! PANG! Part3
这是一篇黑暗童话故事 确信)
至于简介嘛= =,好奇的话就把文章看完吧qaq
C语言函数调用栈
了解函数调用栈
-
函数调用栈是指程序运行时内存一段连续的区域
-
用来保存函数运行时的状态信息,包括函数参数与局部变量等
-
称之为“栈”是因为发生函数调用时,调用函数
(caller)
的状态被保存在栈内,被调用函数(callee)
的状态被压入调用栈的栈顶 -
在函数调用结束时,栈顶的函数
(callee)
状态被弹出,栈顶恢复到调用函数(caller)
的状态 -
函数调用栈在内存中从高地址向低地址生长,所以栈顶对应的内存地址在压栈时变小,退栈时变大
关于栈帧
咳咳,上面很官方的解释是不是不容易理解.qwq.
实际上上面所说的"函数的状态"在栈中是由一个一个栈帧(Stack Frame)
表示的,基本上 (特殊情况的话,可能是某些在main函数前调用的函数) 每一个函数调用时在栈中都有自己的栈帧
在这里,我们需要了解一下几点
-
三个指针(寄存器):
-
ebp
指向当前栈帧底部(保存当前栈帧底部地址)至于为什么在上面,是因为栈从高地址向低地址增长
-
esp
指向栈顶(保存栈顶地址) -
eip
指向要执行的下一指令(保存下一条指令地址)
-
-
参数
(arguments)
是保存在父函数栈帧中的(类似于父亲给儿子的"启动资金")注意, 参数是逆序压栈滴
-
返回地址
(Return Address)
保存了调用子函数前eip
值,这个值在我们栈溢出攻击中至关重要,控制这个值之后就可以劫持程序执行流,让程序下一步执行你想要的指令 -
(Caller's ebp / Previous ebp
保存这父函数ebp
的值,长度为一个字长(x32系统4Byte, x64系统8Byte) -
局部变量
(Local Variables)
一般是发生栈溢出攻击的地方,主要是因为程序在局部变量中读入了超长的数据,造成溢出覆盖了其他内容(比如说返回地址)
函数调用栈整个过程
这一部分前面的知识点已经概括了一些了,再详细将下来篇幅过长,大家可以参考PPT
栈溢出攻击:子函数"找不着家"了
咳咳,为了便于理解,这里我们生动形象一些
前提
有时候程序猿不知道 “不要相信用户输入的数据总是安全的” 的道理,就在读入函数局部变量的过程中用了gets()
等不限制数据长度的函数,或者写了其他可以导致读入过长数据的BUG代码
攻击
如果我们在局部变量中写入的过长的数据,就会覆盖高地址处的 (数据从地址向高地址读入嘛)的数据,例如 prev ebp
或者是ret addr
(主要关注)
如果我们写入的是垃圾数据,程序会返回到一个奇怪的地址,自然会崩溃(Crash)
而如果我们写入精心构造的恶意数据,我们就可以劫持程序,让它干我们想要的事情;)
比如说,调用system("/bin/sh")
这里举个栗子
如果我们把父函数比喻成爸爸的话,那么子函数就是儿子
儿子要自己去闯荡了,
父亲给儿子一些财产让儿子能更好的为未来做准备(父函数栈帧中参数 args
),
然后父亲告诫自己的儿子一定要好好奋斗,回来后为家乡搞建设(返回地址ret addr
),
后来,儿子有了自己的生活,财产(局部变量local var
)
但是呢,有攻击者这样一个角色。
咳咳,他利用各种手段(我们实战的时候可以用pwndbg
和pwntools
等工具进行攻击)诱骗儿子,最终让他晕头转向,失去理智, 连自己家在哪里都找不到了;) (栈溢出用恶意数据覆盖返回地址)
后来。。由于儿子以为攻击者给他的地址就是自己家的地址,就乖乖的跟着攻击者行事,帮助攻击者达成控制世界邪恶目的
全剧终 逃)
下一次开始实战哦.qaq.
To Be Continued…