标签:调用函数 断点 信息 情况 button 表示 modified std 用户输入
汇编基础
•寄存器简介:
•80x86中,32位寄存器有16个:
•4个数据寄存器、2个变址寄存器、2个指针寄存器、6个段寄存器、1个指令寄存器和1个标志寄存器
•--数据寄存器
•eax,ebx,ecx,edx
•作用:存放运行中间数据,ecx常用作计数器
•特点:可以分割,比如eax可分成ax16寄存器,16位的ax又可以分成al和ah
•--变址寄存器
•esi源地址 edi目的地址
•作用:存放存储单元段内偏移量,用于寻址
•--指针寄存器
•EBP(栈底)/ESP (栈顶)
•作用: EBP是基地址指针寄存器,可以直接访问栈中数据, ESP指向堆栈栈顶。
• •--段寄存器
•CS/DS/ES/SS/FS/GS
•说明:在32位CPU中,段寄存器的作用根据工作方式不用而不同,在保护模式下,
•段寄存器中存放的不在是段值,而是称为"选择子( Selector )的某个值。
• •--指令寄存器和标志寄存器
•指令寄存器: EIP ,存放下一次要执行的指令的地址。
•标志寄存器: PSW ,存放程序运行状态。
汇编指令
enter等价于:
push ebp
mov ebp, esp
在函数的入口时常用。
leave等价于:
mov esp, ebp
pop ebp
在函数的出 口时常用。
GDB调试器
•GDB是一个由GNU开源组织发布的、UNIX/LINUX操作系统下的、基于命令行的、功能强大的程序调试工具。 对于一名Linux下工作的c/c++程序员,gdb是必不可少的工具
•调试可执行文件
gdb file
注:这里有异常出现但是不影响下面的操作,所以就没改
GDB常用命令
•运行
•run:简记为 r ,其作用是运行程序,当遇到断点后,程序会在断点处停止运行,等待用户输入下一步的命令。
•continue (简写c ):继续执行,到下一个断点处(或运行结束)
•next:(简写 n),单步跟踪程序,当遇到函数调用时,也不进入此函数体;此命令同 step 的主要区别是,step 遇到用户自定义的函数,将步进到函数中去运行,而 next 则直接调用函数,不会进入到函数体内。
•step (简写s):单步调试如果有函数调用,则进入函数;与命令n不同,n是不进入调用的函数的
disas main 查看地址
GDB常用命令
•断点
•break n (简写b n):在第n行处设置断点 •break func(break缩写为b):在函数func()的入口处设置断点,如:break cb_button
•delete 断点号n:删除第n个断点
•disable 断点号n:暂停第n个断点
•enable 断点号n:开启第n个断点
•clear 行号n:清除第n行的断点
•info b (info breakpoints) :显示当前程序的断点设置情况 •delete breakpoints:清除所有断点:
将断点下在 接收字符串的位置
查看断点信息
•查看
•x /nfu <addr>查看内存
•n表示要显示的内存单元的个数
•f表示显示方式, 可取如下值
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
i 指令地址格式
c 按字符格式显示变量。
f 按浮点数格式显示变量。
•u表示一个地址单元的长度
b表示单字节,
h表示双字节,
w表示四字节,
g表示八字节
•Info functions 查看函数
简单的溢出
#include <stdio.h>
int main(int argc, char** argv)
{
int modified;
char buffer[64];
modified = 0;
gets(buffer); // 引发缓冲区溢出
if (modified != 0)
{
printf("Congratulations, you pwned it.\n");
}
else
{
printf("Please try again.\n");
}
return 0;
}
Dump of assembler code for function main:
//压栈
0x0000555555555145 <+0>: push rbp
0x0000555555555146 <+1>: mov rbp,rsp
//rsp=rsp-0x60,即在栈上分配0x60个字节空间
0x0000555555555149 <+4>: sub rsp,0x60
//将edi的值存入[rbp-0x54]
0x000055555555514d <+8>: mov DWORD PTR [rbp-0x54],edi
//将rsi的值存入[rbp-0x60]
0x0000555555555150 <+11>: mov QWORD PTR [rbp-0x60],rsi
//0x0的值存入[rbp-0x4] 这里是modified的值0
0x0000555555555154 <+15>: mov DWORD PTR [rbp-0x4],0x0
//将rbp-0x50的值存入rax
0x000055555555515b <+22>: lea rax,[rbp-0x50]
//将rax的值存入rdi
0x000055555555515f <+26>: mov rdi,rax
//将0x0存入eax
0x0000555555555162 <+29>: mov eax,0x0
/调用gets(buffer)读取输入数据
0x0000555555555167 <+34>: call 0x555555555040 <gets@plt>
//判断modified变量的值是否为0
0x000055555555516c <+39>: cmp DWORD PTR [rbp-0x4],0x0
//如果modified不为0,打印成功提示Congratulations, you pwned it
0x0000555555555170 <+43>: je 0x555555555180 <main+59>
//将rip+0e8b的值存入rdi 成功提示的信息
0x0000555555555172 <+45>: lea rdi,[rip+0xe8b] # 0x555555556004
//调用puts输出数据
0x0000555555555179 <+52>: call 0x555555555030 <puts@plt>
//无条件跳转到0x55555555518c 也就是 0
0x000055555555517e <+57>: jmp 0x55555555518c <main+71>
//将rip+0xe99的值存入rdi 错误的提示信息
0x0000555555555180 <+59>: lea rdi,[rip+0xe99] # 0x555555556020
//调用puts输出数据
0x0000555555555187 <+66>: call 0x555555555030 <puts@plt>
//将0x0的值存入eax
0x000055555555518c <+71>: mov eax,0x0
//弹栈操作,恢复ebp、esp
0x0000555555555191 <+76>: leave
//栈平衡
0x0000555555555192 <+77>: ret
End of assembler dump.
通过对上面的汇编代码进行分析,我们知道buffer位于[rbp-0x50]处,而modified位于[rbp-0x4]处,两个地址的距离为0x50 - 0x4 = 0x4C,即76,刚好为buffer数组的缓冲区大小。因此当我们输入的数据超过76字节时,modified变量就可以被覆盖。
下面在gdb中进行验证,在gdb中执行b *0x000000000000116c命令对gets的下一条指令下一个断点:
gdb-peda$ b *0x000000000000116c
所以接下就是证明我们的推测了,令其一个窗口使用python语句生成77个个字符,python -c "print ‘a‘*77"
运行程序输入构造的字符串,返回结果
连续输入两次ni,如果je没有跳转,说明modified的值不为0
输入c后程序继续执行,看到提示信息
使用quit退出,这次我们直接执行程序
可以看到,在76个字节以内的字符串,会被提示Please try again。下面输入77个字符试试
可以看到已经成功发起了溢出攻击,程序被PWN掉了!
注:
简单的GCC语法:
如果你只有一个文件(或者只有几个文件),那么就可以不写Makefile文件(当然有Makefile更加方便),用gcc直接编译就行了。在这里我们只介绍几个我经常用的几个参数,第一是 “-o”,它后面的参数表示要输出的目标文件,再一个是 “-c”,表示仅编译(Compile),不连接(Make),如果没有”-c”参数,那么就表示连接,如下面的几个命令:
gcc –c test.c,表示只编译test.c文件,成功时输出目标文件test.o
gcc –c test.c –o test.o ,与上一条命令完全相同
gcc –o test test.o,将test.o连接成可执行的二进制文件test
gcc –o test test.c,将test.c编译并连接成可执行的二进制文件test
gcc test.c –o test,与上一条命令相同
gcc –c test1.c,只编译test1.c,成功时输出目标文件test1.o
gcc –c test2.c,只编译test2.c,成功时输出目标文件test2.o
gcc –o test test1.o test2.o,将test1.o和test2.o连接为可执行的二进制文件test
gcc –c test test1.c test2.c,将test1.o和test2.o编译并连接为可执行的二进制文件test
gcc –m64 test1.c -o test.out 编译64位程序
gcc –m32 test1.c -o test.out 编译32程序
如果命令出错:
标签:调用函数 断点 信息 情况 button 表示 modified std 用户输入
原文地址:https://www.cnblogs.com/zi-Chuan/p/13064625.html