建设谷歌公司网站费用,淘宝客网站设计,苏州园区邮编,华为官方手机商城目录 课程教学C语言函数调用栈ret2textPWN工具 课程教学
课程链接#xff1a;https://www.bilibili.com/video/BV1854y1y7Ro/?vd_source7b06bd7a9dd90c45c5c9c44d12e7b4e6 课程附件#xff1a; https://pan.baidu.com/s/1vRCd4bMkqnqqY1nT2uhSYw 提取码: 5rx6
C语言函数调… 目录 课程教学C语言函数调用栈ret2textPWN工具 课程教学
课程链接https://www.bilibili.com/video/BV1854y1y7Ro/?vd_source7b06bd7a9dd90c45c5c9c44d12e7b4e6 课程附件 https://pan.baidu.com/s/1vRCd4bMkqnqqY1nT2uhSYw 提取码: 5rx6
C语言函数调用栈 一个栈帧保存的是一个函数的状态信息父函数每调用一个子函数就会在函数调用栈中新增一个栈帧 32 x86 esp 64 x86 rsp ebp 栈底 esp 栈顶 stack frame pointer记录上一个父函数的栈顶指针的值便于恢复父函数 发生栈溢出的地方在local variables【上图是32位情况】 上图是32位情况在32位传参时子函数所用到的参数保存在父函数栈帧的末尾并不是保存在自己的栈帧中这里的arguments是子函数所用到的形参 父函数最末尾的字长保存父函数自己栈顶的值如上面红色的箭头指向previous stack frame pointer同理父函数的父函数也是一样的
父函数main先把所要调用的子函数(sum)中的参数(1,2)逆序压栈压入自己的栈帧此后压入return address【即子函数下一条指令的地址return 0 的地址】在子函数执行后回到下一条指令的地址(执行return 0)在子函数结束后要返回父函数的栈帧这意味在调用子函数时不能把父函数的栈帧丢弃由此需要加入父函数的栈底指针加入 丢弃某块数据不用并不需要把这块数据抹除只需要标记成不是我所使用的范围即可这也是磁盘数据恢复的原理除非有新来的数据复写 主调函数 caller 被调函数 callee 遵循C语言函数调用规范一般在开头用push ebp以及mov ebp, esp需要保存父函数栈底的状态结束会执行leave恢复父函数的栈底以及 ret返回到父函数的下一条指令
首先主调函数也是有自己的父函数将它的父函数的ebp压入 接着把esp抬高到和ebp相同的位置 下一条指令为新的栈帧开辟局部变量的空间这里是sub 0x10 , esp即esp-16为什么是减去因为栈是反向增长高到低 将被调函数所用到的参数123反向压入栈即先压入3再压入2最后压入1 call 这条指令不等于jumpjump是一个跳转指令call 不仅会将eip移动到目标代码的位置还会在栈中自动保存下一条指令的地址【此时的return address就是23的位置】 此时进入被调函数同理首先是push ebp和mov sep , ebp先把主调函数的栈帧保存注意此时父函数的ebp重新增加到栈里了将esp的值赋给ebp让ebp抬高到新的栈帧的栈顶 执行实际操作最后的运算结果保存在eax的寄存器中【默认情况下保存函数的返回值】 由于esp并没有开辟局部变量的空间为什么不是leaveleave就是 mov ebp,esp再pop ebp这里只有pop ebp是因为子函数没有任何局部变量所以ebp在调用返回时已经在相应的位置了pop这条指令总是把esp当前指向的位置对应的一个字长的数据抬入到目标位置所以pop ebp就会把esp向上抬一个字长把esp本来指向父函数的值抬入ebp中 ret 相当于pop eipeip回到父函数的位置 主调函数情况其局部变量以及被调函数的相关参数 使用add这条指令清空数据最后保存结果到eax中 值得注意的是此时返回时esp和ebp并不在相同位置所以需要leave首先将esp的值变成ebp 返回父函数的父函数 以上过程需要非常熟练栈还有很多其他工作的规则以上是最基本的
ret2text 关注eip寄存器其中return address存在eip中当eip中写入我们目标代码的地址程序的控制流便被劫持了 栈溢出是缓冲区溢出的一种 向局部变量str[8]中写入24字节数据溢出到了关键结构 当我们拿到一个CTF PWN的题先通过checksec 看这个程序有什么保护措施 【x86架构小端序的可执行ELF】 通过IDA看c语言代码 显然漏洞明显读入的数据长度不受限向上溢出 值得注意的是此时虽然开辟了8字节但是与ebp的距离是16字节10h 最好的方法还是动态调试因为出题人可能以esp来寻址 通过gdb动态调试直接run是没什么意义的先打断点例如b main【b 表示 break】再r【r 表示run】此时我们可以看到具体的信息 在程序执行到绿色箭头位置时所有寄存器的值 eip此时main函数偏移26字节的指令当前指针也是0x804856b esp和ebp此时对应的值是一个很大的值这是栈的地址用户空间的最高地址 这是反汇编窗口 最上面的00:0000是栈顶低地址最下面的07:001c是栈底高地址gdb是反着显示的 函数调用栈的关系 按n一直步过到漏洞位置 按s步进入这个函数 按n开始输入按照正常输入8个A看什么情况 这里我们看栈里面的情况输入stack 看多少项24项栈值 esp和ebp之间就是当前执行的函数的栈帧esp表示栈顶ebp是栈底 ebp是前一个函数被保存在栈里的ebp的值ebp再往高一个字长就是返回地址我们的目标就是攻击这个返回地址 我们此时可控制的区域是esp和ebp之间的位置即buffer这个变量 这意味着只要我们一直写覆盖返回地址即可达到攻击效果 回到IDA我们可找到这个后门函数执行系统命令直接获得shell的控制权相当于在shell中打开shell 这意味着我们先写20个字节Aebp还有4个字节再写4个字节制定的地址就会把原本的地址覆盖掉 我们要找到getshell的开始地址通过双击IDA中getshell在汇编代码中找到起始地址8048522 我们这里就可以写脚本来获得shell了值得注意的是payload中不能直接加0x8048522【前面是字符串这里是整型】所以需要p32来转换 最终发送payload并且与之交互io.interactive()即可获得shell 打远程只要用remote即可
但是实际情况下不一定有后门函数 一般系统调用这样的代码需要我们自己输入 只要不是代码的地方都不可执行 随机化栈中的地址 所以放在Bss中居多Bss用于存放全局变量的如果这个全局变量是开辟缓存区可以输入系统调用即可 比如上面这个情况 利用工具直接生成shellcode的机械码再io.send直接发送
PWN工具 IDA pro f5 进入c程序 esc 返回上一个程序 白色的函数为已写死的函数 粉色的函数要用的时候再去调 在Options的General中可调整一些设置例如加入机械码 这样C语言代码被拷贝到汇编代码中 shiftf12 或者 shiftfnf12打开一个字符串界面【在不知道mian函数位置可通过程序所表示的字符串找程序的主函数】 \r把之前的文本在显示时清空io.recv()把所有的数据完全还原【这里我用的时io.recvline()一行一行接收】 这里的ZmxhZ3tuMHRfZjRzdGVyX3Q2YW5feTB1fQo显然是Base64我们需要解码 将其解码