Featured image of post PWN! PWN! PANG! Part3 --- 栈溢出基础

PWN! PWN! PANG! Part3 --- 栈溢出基础

子函数诱拐指南

PWN! PWN! PANG! Part3

这是一篇黑暗童话故事 确信)

至于简介嘛= =,好奇的话就把文章看完吧qaq

C语言函数调用栈

了解函数调用栈

  • 函数调用栈是指程序运行时内存一段连续的区域

  • 用来保存函数运行时的状态信息,包括函数参数与局部变量等

  • 称之为“栈”是因为发生函数调用时,调用函数(caller)的状态被保存在栈内,被调用函数(callee)的状态被压入调用栈的栈顶

  • 在函数调用结束时,栈顶的函数(callee)状态被弹出,栈顶恢复到调用函数(caller)的状态

  • 函数调用栈在内存中从高地址向低地址生长,所以栈顶对应的内存地址在压栈时变小,退栈时变大

配图

关于栈帧

咳咳,上面很官方的解释是不是不容易理解.qwq.

实际上上面所说的"函数的状态"在栈中是由一个一个栈帧(Stack Frame)表示的,基本上 (特殊情况的话,可能是某些在main函数前调用的函数) 每一个函数调用时在栈中都有自己的栈帧

栈帧结构预览


在这里,我们需要了解一下几点

  1. 三个指针(寄存器):

    • ebp指向当前栈帧底部(保存当前栈帧底部地址)

      至于为什么在上面,是因为栈从高地址向低地址增长

    • esp指向栈顶(保存栈顶地址)

    • eip指向要执行的下一指令(保存下一条指令地址)

  2. 参数(arguments)是保存在父函数栈帧中的(类似于父亲给儿子的"启动资金")

    注意, 参数是逆序压栈滴

    参数的保存

  3. 返回地址(Return Address)保存了调用子函数前eip值,这个值在我们栈溢出攻击中至关重要,控制这个值之后就可以劫持程序执行流,让程序下一步执行你想要的指令

    返回地址的位置

  4. (Caller's ebp / Previous ebp保存这父函数ebp的值,长度为一个字长(x32系统4Byte, x64系统8Byte)

    prev ebp

  5. 局部变量(Local Variables)一般是发生栈溢出攻击的地方,主要是因为程序在局部变量中读入了超长的数据,造成溢出覆盖了其他内容(比如说返回地址)

    Local Var

函数调用栈整个过程

这一部分前面的知识点已经概括了一些了,再详细将下来篇幅过长,大家可以参考PPT

栈溢出攻击:子函数"找不着家"了

咳咳,为了便于理解,这里我们生动形象一些

前提

有时候程序猿不知道 “不要相信用户输入的数据总是安全的” 的道理,就在读入函数局部变量的过程中用了gets()等不限制数据长度的函数,或者写了其他可以导致读入过长数据的BUG代码

攻击

如果我们在局部变量中写入的过长的数据,就会覆盖高地址处的 (数据从地址向高地址读入嘛)的数据,例如 prev ebp或者是ret addr(主要关注)

如果我们写入的是垃圾数据,程序会返回到一个奇怪的地址,自然会崩溃(Crash)

而如果我们写入精心构造的恶意数据,我们就可以劫持程序,让它干我们想要的事情;)

比如说,调用system("/bin/sh")


这里举个栗子

如果我们把父函数比喻成爸爸的话,那么子函数就是儿子

儿子要自己去闯荡了,

父亲给儿子一些财产让儿子能更好的为未来做准备(父函数栈帧中参数 args),

然后父亲告诫自己的儿子一定要好好奋斗,回来后为家乡搞建设(返回地址ret addr),

后来,儿子有了自己的生活,财产(局部变量local var)

但是呢,有攻击者这样一个角色。

咳咳,他利用各种手段(我们实战的时候可以用pwndbgpwntools等工具进行攻击)诱骗儿子,最终让他晕头转向,失去理智, 连自己家在哪里都找不到了;) (栈溢出用恶意数据覆盖返回地址)

后来。。由于儿子以为攻击者给他的地址就是自己家的地址,就乖乖的跟着攻击者行事,帮助攻击者达成控制世界邪恶目的

全剧终 逃)


下一次开始实战哦.qaq.

To Be Continued…

Licensed under CC BY-NC-SA 4.0
For a better open source community!
Built with Hugo
主题 StackJimmy 设计