03_Canary保护
gdb基本命令复习
基本调试控制
s / step:单步执行,进入函数内部。
si:单步执行一条汇编指令,进入函数内部。
n / next:单步执行,不进入函数,直接执行下一条语句。
ni:单步执行一条汇编指令,不进入函数。
c / continue:继续执行程序,直到下一个断点或程序结束。
r / run:从头执行程序,运行到断点或终止。
finish:执行当前函数直到返回,并停在调用该函数的地方。
until/u:执行程序直到退出当前循环或到达指定的行。
jump 行号 / j 行号:跳转执行指定行号的代码,适合在函数内部使用。
return:强制返回当前函数,跳过剩余的未执行代码。可以指定返回值,如:
return 0
。
断点管理
b 地址 / *b 地址:在指定的地址设置断点。
b 函数名:在指定的函数入口处设置断点。
info b / info breakpoints:查看所有断点的信息。
delete:删除所有断点。
clear 地址/函数名:清除指定位置的断点。
x 系列命令
x
系列命令用于查看内存内容,格式为:x/<n/f/u> <地址>
,其中:
n:需要显示的内存单元的个数。
f:显示的格式,可以是:
x
:十六进制。d
:十进制。u
:无符号十进制。o
:八进制。t
:二进制。a
:地址格式。i
:反汇编指令。c
:字符格式。f
:浮点数格式。u:表示从当前地址往后请求的字节数,可以是:
b
:单字节。h
:双字节。w
:四字节(默认值)。g
:八字节。
常用 x
命令示例:
x/xw addr:显示某个地址处开始的16进制内容,单位为四字节。
x/x $esp:查看
esp
寄存器中的值。x/s addr:查看
addr
处的字符串。x/b addr:查看
addr
处的单字节字符。x/i addr:查看
addr
处的反汇编指令。
info 系列命令
info
系列命令用于查看程序或寄存器的各种信息,常用命令包括:
info registers / i r:查看所有寄存器的值。
info register $ebp / i r ebp:查看
ebp
寄存器的值。i r eflags:查看状态寄存器
eflags
的内容。i r ss:查看段寄存器
ss
的内容。info b:查看当前断点的信息。
info functions / i functions:查看程序中所有的函数。
disas addr / disassemble addr:查看指定地址的反汇编代码。
stack n:查看栈中前
n
个值(例如stack 20
查看栈中前20个值)。show args:查看函数调用时的参数。
GDB 插件 (PEDA) 扩展命令
vmmap:查看当前进程的内存映射(PEDA 扩展支持)。
readelf:查看 ELF 文件的各个段的起始地址(PEDA 扩展支持)。
parseheap:显示堆的状况(PEDA 扩展支持)。
调用栈与函数跳转
backtrace / bt:查看调用栈。
frame n:切换到调用栈的第
n
帧。
查看和修改寄存器与变量
p $寄存器 / print $寄存器:查看指定寄存器的值。
p 变量名 / print 变量名:显示变量的值。
p &变量名:查看变量的内存地址。
*p 地址:查看指定地址的值。
p/x 表达式:按十六进制显示结果。
set var 变量=值:修改变量的值。
退出调试
quit / q:退出GDB调试。
16位标志寄存器共用了9个标志位,它们主要用来反映CPU的状态和运算结果的特征,标志位的分布如下表所示。32 位 CPU 也把标志寄存器扩展到 32 位,记为 EFLAGS。它新增加了四个控制标志位,它们是:IOPL、NT、RF 和 VM,这些标志位在实方式下不起作用。其它标志位的位数和作用与先前完全相同。(结尾有详细介绍)
canary保护的位置
#include<stdio.h>
int main()
{
char *name = "Roger";
printf("My name is %s");
return 0;
}
编译
没有保护gcc -no-pie -fno-stack-protector -m32 -o canary printf1.c
有保护 gcc -no-pie -fstack-protector-all -m32 -o canary canary.c
disass main
and esp 0Xfffffff0 这就是堆栈对齐
主流编译器的编译规则规定 “ 程序访问的地址必须向16字节对齐(被16整除)” 内存对齐之后可以提高访问效率。
有canary保护
很像crc校验
破解canary需要精确填充:在填充过程中,确保将canary值(在内存中的实际值)填入正确位置。这通常需要计算偏移量,确保填充的顺序和长度正确。
标志寄存器
标志寄存器(Flags Register)用于反映CPU的状态和运算结果的特征。在不同的CPU架构中,标志寄存器的结构和功能可能会有所不同。以下是详细介绍的标志寄存器,主要针对16位和32位的x86架构。
16位标志寄存器(FLAGS)
在16位CPU中,标志寄存器通常被称为 FLAGS
,主要由以下9个标志位构成:
CF (Carry Flag):
功能:指示运算中是否发生进位或借位。
置1:发生进位。
置0:未发生进位。
PF (Parity Flag):
功能:指示运算结果中1的个数是否为偶数。
置1:结果中1的个数为偶数。
置0:结果中1的个数为奇数。
AF (Auxiliary Carry Flag):
功能:用于BCD(Binary-Coded Decimal)运算,指示低四位是否发生进位。
置1:低四位发生进位。
置0:低四位无进位。
ZF (Zero Flag):
功能:指示运算结果是否为零。
置1:结果为零。
置0:结果不为零。
SF (Sign Flag):
功能:指示运算结果的符号(正负)。
置1:结果为负。
置0:结果为正。
TF (Trap Flag):
功能:用于单步调试,允许在每条指令后产生一个中断。
置1:启用单步调试。
置0:禁用单步调试。
IF (Interrupt Flag):
功能:控制中断的响应。
置1:允许中断。
置0:禁止中断。
DF (Direction Flag):
功能:控制字符串操作的方向。
置1:从高地址到低地址(逆序)。
置0:从低地址到高地址(顺序)。
OF (Overflow Flag):
功能:指示有符号数运算的结果是否发生溢出。
置1:发生溢出。
置0:未发生溢出。
32位标志寄存器(EFLAGS)
在32位架构中,标志寄存器扩展为 EFLAGS
,除了包含16位的标志位外,还新增了四个控制标志位:
IOPL (I/O Privilege Level):
功能:设置当前特权级别,用于控制I/O操作的权限。
置1或0:指示I/O操作的权限级别。
NT (Nested Task Flag):
功能:指示是否允许嵌套任务。
置1:允许嵌套任务。
置0:不允许嵌套任务。
RF (Resume Flag):
功能:指示是否在中断处理后恢复。
置1:在中断处理后恢复。
置0:正常处理。
VM (Virtual 8086 Mode Flag):
功能:指示CPU是否在虚拟8086模式下运行。
置1:处于虚拟8086模式。
置0:不在虚拟8086模式。
其他标志位
Nested Task Flag (NT):用于支持嵌套任务管理。
Resume Flag (RF):用于在处理中断时恢复原来的状态。
Virtual 8086 Mode Flag (VM):允许CPU进入兼容旧的8086模式。
标志寄存器是CPU中非常重要的一部分,它通过状态标志的设置和清除反映运算结果和程序状态。根据这些标志位,CPU可以实现条件跳转、中断管理、错误处理和调试等功能。了解这些标志位的功能和使用方式对于进行底层编程和调试至关重要。
条件跳转指令
对应上面的标志寄存器
在x86架构中,有许多其他的条件跳转指令,它们根据标志寄存器的状态决定是否进行跳转。以下是一些常见的条件跳转指令:
条件跳转指令
jne (Jump if Not Equal):
功能:如果ZF(Zero Flag)为0,则跳转。即上一次比较结果不相等时跳转。
jg (Jump if Greater):
功能:如果ZF为0且SF(Sign Flag)和OF(Overflow Flag)相同,则跳转。即对于有符号数,判断是否大于。
jge (Jump if Greater or Equal):
功能:如果SF和OF相同,则跳转。即对于有符号数,判断是否大于或等于。
jl (Jump if Less):
功能:如果SF与OF不相同,则跳转。即对于有符号数,判断是否小于。
jle (Jump if Less or Equal):
功能:如果ZF为1或SF与OF不同,则跳转。即对于有符号数,判断是否小于或等于。
ja (Jump if Above):
功能:如果CF为0且ZF为0,则跳转。即对于无符号数,判断是否大于。
jae (Jump if Above or Equal):
功能:如果CF为0,则跳转。即对于无符号数,判断是否大于或等于。
jb (Jump if Below):
功能:如果CF为1,则跳转。即对于无符号数,判断是否小于。
jbe (Jump if Below or Equal):
功能:如果CF为1或ZF为1,则跳转。即对于无符号数,判断是否小于或等于。
jz (Jump if Zero):
功能:如果ZF为1,则跳转。即上一次运算结果为零时跳转。
jnz (Jump if Not Zero):
功能:如果ZF为0,则跳转。即上一次运算结果不为零时跳转。
jp (Jump if Parity):
功能:如果PF(Parity Flag)为1,则跳转。即判断运算结果中1的个数为偶数。
jnp (Jump if Not Parity):
功能:如果PF为0,则跳转。即判断运算结果中1的个数为奇数。
这些条件跳转指令可以用于控制程序的流向,基于不同的运算结果和标志位的状态,帮助实现复杂的逻辑和控制结构。在编写汇编语言程序时,了解这些指令的功能和适用场景是非常重要的。