🗒️理解栈溢出并使用 gdb 来调试 C 语言程序
2023-5-2
| 2023-5-27
0  |  0 分钟
type
Post
status
Published
date
May 2, 2023
slug
gdb-debug
summary
由于想学习一下逆向,所以摸索一下gdb的用法
tags
学习
category
学习思考
icon
password

编写并编译

编写一个简单的C语言程序,比如
#include <stdio.h> int main() { printf("hello world!"); }
保存为 main.c ,并执行 gcc -g main.c main
然后使用 gdb main 开始执行

使用asm布局调试

执行 layout asm 开始调试,layout split能看到代码

设置 intel 语法

使用 show disassembly-flavor 可以看到使用的是att语法,可以通过 set disassembly-flavor intel 切换为intel语法

断点调试

使用 b 11 来给第11行代码设置断点,使用ni,si来对每条指令进行断点,使用n,s来对每行代码进行断点
ni和si差别就是,si会进入函数,ni不会

起始代码解析

先写一个简单的程序
#include <stdio.h> int main() { volatile int t = 1; if (t == 2) printf("hello world!"); else printf("test2"); return 1; }
 
push rbp ; 将当前的栈底指针存到栈中 mov rbp, rsp ; 将栈底指针置位栈顶指针(相当于新建一个栈) sub rsp, 0x10 ; 将栈顶指针往上移,相当于给局部变量开拓空间 ; 这里实际上 int t = 1,t变量占用0x4(32bit),实际上存四个int变量才会存满,因为内存对齐才要0x10 mov DWORD PTR [rbp-0x4],0x1 ; 将内容1存入栈的第一个位置 mov eax,DWORD PTR [rn] ; 将内容读入寄存器eax (对应t = 1) cmp eax, 0x2 ; 将eax的内容和2进行对比 (对应 t == 2),可通过 p $eflags 看到ZF不在,ZF为0 jne 0x5555555555517a <main+49> ; 如果结果不相等,就跳转到 main 函数往下49条指令的地址 lea rax,[rip+0xe90] ; 计算 "test2" 的地址 mov rdi,rax mov eax, 0x0 ; 做传参的准备工作 call 0x5555555555050 <printf@plt> ; 调用打印方法 mov eax, 0x1 ; 最后return 1,系统会从eax中取得返回值 leave ; 结束进程

缓冲区溢出利用原理

看了上面的解释,其实溢出原理就很好理解了
因为一开始,会将栈底指针存到栈中,然后再开始下一轮的操作,而存储栈数据的时候,会从栈顶不断往下存,所以一旦覆盖掉了之前存的栈底指针,就会导致,最终这个函数结束的时候,通过leave想要返回,就会返回到被覆盖的栈底指针,最终访问到某个本来不属于这个函数的内容。
好比如说,我给栈开拓的空间是0x10,但是我手贱,我偏偏存到0x14,把之前的栈底指针地址给覆盖了,然后整个函数执行完毕了,函数要返回带它来的故乡了。其实也不一定要等到整个函数执行完毕,比如说想修改不属于这个栈的外面的内容,就可以通过这种方式修改。亦或者是修改属于同一个函数的其他变量的内容,也可以这样修改

例子

下面用一个简单的例子来说明这个问题
#include <stdio.h> int main() { volatile int isAdmin = 0; volatile char test[4] = "a"; scanf("%s", &test); if (isAdmin != 0) printf("You are Admin!"); else printf("You are not Admin."); return 0; }
其他汇编语句基本相同,关键在于,给int分配的空间为四个字节,给char分配的也是4个字节,一开始它是这样的
notion image
我们输入abcde,栈变成这样了
notion image
完蛋了呀,int被修改了!这时候,对前面的int进行cmp,发现ZF是0了,所以进入了第一条语句,You are Admin被输出。
注意这里比较容易混淆的是,ebp是栈底,但是它的地址比较高。esp是栈顶,地址较低,int先存,它比较靠栈底(栈是从底部往上存的)。char后存,它挨在int后面。而当char分配的空间存满,它就会往后面覆盖,就把int的内容给覆盖了。

参考文章

致谢:
💡
U2F sdGVkX1
 
 
学习思考
  • 学习
  • 从CTF.Show签到题入手尝试逆向题[Klee-CTF] VulnStack 靶场实战 一 (1)
    目录