0x01 PWN
checksec下载安装
git clone https://github.com/slimm609/checksec.sh.git
sudo ln -s /xx/xx/checksec /usr/local/bin/checksec #建立软链接,源地址需要是绝对路径
ida
F5/tab 查看伪代码
空格 查看汇编代码
视图->打开子视图->字符串 :查看所有字符串
编辑->修补程序 :修改程序 修改完后点击修补程序应用到输入文件即可保存修改
一些信息 HKEY_CURRENT_USER\Software\Hex-Rays\IDA\History 通过注册表查看
1、如何找到使用.rodata里数据的汇编行
选中字符串后鼠标右键点击交叉引用列表
2、查看二进制里所有的字符串
视图->子视图->字符串
2、ida联合gdb进行远程调试
F8单步调试,F9单步进入函数
https://uuzdaisuki.com/2020/02/04/ida%E8%BF%9C%E7%A8%8B%E8%B0...
3、ida远程调试之动态库
windows上加载你要调试的动态库,远程配置如下图所示。应用程序选择加载该动态库的应用程序,输入文件是该动态库。
4、反汇编报错positive sp value has found
https://blog.csdn.net/u010334666/article/details/82937413
在选项-常规勾选堆栈指针
可以看到堆栈指针
把出现负数的上一行改成和负数一样的值
5、给汇编代码添加描述
汇编显示机器码
6、规则
str R0,[sp,#0x428+var_420] 把R0的值存在sp+0x428-0x420所在地址里
python pwntools
安装 :pip install pwntools
环境:ubuntu python64位
调试:context(os='linux', arch='amd64', log_level='debug') 32位时改为i386
导入外部lib:from ctypes import *
ex:使用c的rand函数
libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
libc.srand(1)
libc.rand()
- sendlineafter("在什么字符串后面发送数据",要发送的数据)
- ljust(num,b'a') 补全payload,使长度为num字节
- recvlineuntil() 可以截取收到内容中想要的数据
- asm(shellcraft.sh()) 生成shell,使用前需要用context声明环境
- elf=ELF(filepath) 解析elf文件,输出checksec数据
- 使用p32等 from pwnlib.util.packing import p32
pwn
汇编转机器码
from pwn import *
context.arch='i386' #指定架构
print(asm('mov eax, 0'))
gdb
- gdb filename :调试某个文件
- info reg 查看寄存器的值
- info args:打印当前函数参数名机值
- info variables:显示当前可见的所有全局变量和静态变量的信息。
- info frame:打印当前函数的栈信息
- info locals:打印当前函数所有局部变量及值
- info functions: 打印所有函数 info functions nb_dev_ioctl 指定具体函数搜索
- info symbol 0x07dde378 知道地址搜索函数
- info files: 查看加载的可执行文件的信息,包括基地址。
- info proc mappings 查看内存映射
- maintenance info sections 查看程序的段信息
- info sharedlibrary:显示已加载的共享库的信息,包括名称、地址范围和符号表信息。
- info threads 查看线程 thread 3 选择3号线程
- xinfo 0x1232 获取地址所处位置的权限
- c(continue):继续执行/n(next):单步跟踪/s(step):步入
- ni/si都是汇编级别的断点定位。si会进入汇编和C函数内部,ni不会。
- finish 步出当前函数
- start:重新启动程序
- 设置在单步执行时自动显示汇编代码
(gdb) set disassemble-next-line on - x /8gx:以16进制 输出8个64位数
x /4wx:以16进制 展示4个32位数
x /10x $sp:输出当前堆栈栈顶往下10个栈里的值
x/sh $rdi 以字符串形式查看
更多格式
单位:b:字节(byte) h:半字(halfword,2 字节) w:字(word,4 字节) g:双字(giant word,8 字节)
格式:
x:十六进制(hexadecimal) d:有符号十进制(signed decimal) u:无符号十进制(unsigned decimal) o:八进制(octal) t:二进制(binary) a:地址(address) c:字符(character) f:浮点数(floating point)
- set $edx=0x12 修改寄存器的值
- set args in/test.mp3 设置程序的参数
show args 显示设置的参数 - 直接gdb带参数运行
gdb -q --args /opt/kingsoft/wps-office/office6/et -shield test2.xlsx - r < test.txt 从标准输入进行读取,先创建一个文件
r 直接带上参数 ex: run -shield test.xx - p 命令用于评估表达式并打印结果。它根据表达式的类型来确定如何评估和打印结果。
p 变量名 查看变量在内存中的位置及值
p/x $rax 查看rax寄存器里的值
p e->init() 判断是个函数,返回类型为void,所以打印void
p e->init 判断是个变量或指针,会打印变量或指针所指向的内容或者值 - disa main 查看main函数的反汇编代码
- disassemble 0xxxx,0xxxx 查看指定位置的反汇编
- bt 查看调用栈
- gdb ./test core 调试core文件
- catch load libtest.so 在目标程序加载共享库时设置断点
- thread apply all bt 查看所有线程的调用栈
- watch:当内存地址的内容发生变化时停止。
rwatch:当内存地址被读取时停止。
awatch:当内存地址被读取或写入时停止
example:
watch $ecx if $ecx == 0xffff 当ecx寄存器等于某值时停止
watch global_var & 0x00000003 当global_var与0x00003按位与发生变化时停止 - condition 4 $rbx==0x997 断点4条件满足时停止,取消 condition 4
layout asm 展示汇编,layout src 展示源码。退出 layout 先ctrl+x 再按a; ctrl+x 按1/2 单窗口/双窗口模式
layout下使用ctrl+p /ctrl+n查看历史命令,ctrl+b/ctrl+f 光标前后移动
使用fs next 切换窗口/ctrl+x o
ctrl+L 刷新窗口 /refresh-
layout快捷键
当前运行到的行被高亮且前面有一个 > 标记.前面第一个标记的含义
B: 至少被触发过 1 次的断点.
b: 还没有被触发过的断点.
H: 至少被触发过 1 次的硬件断点.
h: 还没有被触发过的硬件断点.
前面第二个标记的含义
+: 被启用的断点.
-: 未被启用的断点.- tui new-layout test {-horizontal asm 1 regs 1} 2 status 0 cmd 1 创建一个布局,使用layout test使用它
https://www.chuxin911.com/GDB_note_2_tui_20220708/ - find 0x400000,0x3b85fff,"Syntax error" 搜索字符串位置,需要注意的是后面的地址通过info proc map 查看得到的0x3b86000不是结束地址。它是没有被映射的第一个字节!在查找命令中使用这个地址会报错,因为它超出了内存映射的范围。所以,需要后退一个字节,到 0x3b85fff
find /b 0x090890f0, 0x0a29c306, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64 搜索十六进制 - set debug remote 1 显示远程调试信息
- set variable *0x40003234=1 设置内存某个位置的值
- detach 脱离gdbserver
- print $sp+0x12 直接在gdb里面进行计算
- gdb 卡住怎么办,attach一下卡住的进程,看看卡在哪 https://blog.csdn.net/Dong_HFUT/article/details/125816152
- rbreak 正则表达匹配函数下断点
- list main.c:109 展示源码,需要有调试信息
until *0x12313 运行到某处,不用下断点
设置断点 1、b *0x111111 : 在0x11111处下断点,记得前面要加* 2、b func_name 3、b /src/codefile.cc:81。gdb将在运行到源码文件/src/codefile.cc的第81行中断 4、b *(function_name + 32) 在具体函数偏移处下断点 5、条件断点: b hw/core/gpio.c:32 if name=0x7fffffffd8b0 6、保存断点 save breakpoints breakpoints.txt 下次使用时加载 source breakpoints.txt 如果想自动加载断点,在.gdbinit中添加source xx.txt这行 7、给断点添加注释 commands 10(断点编号),然后输入注释 删除断点 delete [断点编号] 查看断点 info break 使断点生效/失效 enable/disable 断点编号
pwndbg
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
sudo #./setup.sh
命令
- parseheap 展示堆结构
- fastbins fastbins指针
pwn
- 将汇编转成机器码
from pwn import *
context.arch='i386' #指定架构
print(asm('mov eax, 0'))
questions
- 遇到执行elf文件提示没有那个目录或文件
解决:
Ubuntu 32位库的安装:
ubuntu64位默认没有32位的库,所以32位的ELF文件无法执行,会提示找不到文件或者文件夹,所以需要手动安装32位的库。
sudo apt install libc6-dev-i386
sudo apt-get install lib32z1 - 实验不同版本的glibc,patchelf和glibc-all-in-one
https://blog.csdn.net/qq_41560595/article/details/114597342
0x02 reverse
elf文件upx脱壳 :kali下 upx -d 源文件 -o 目标文件
0x03 汇编知识点
1、32位计算机常用寄存器
2、x86-64位架构中的寄存器
寄存器 描述
RAX 通用寄存器,可用于存储整数数据或指针
RBX 通用寄存器,可用于存储整数数据或指针
RCX 通用寄存器,可用于存储整数数据或指针
RDX 通用寄存器,可用于存储整数数据或指针
RDI 存储函数参数
RSI 存储函数参数
RBP 存储函数局部变量
RSP 存储函数栈指针
R8-R15 存储函数参数或临时数据
RIP 存储下一条要执行的指令的地址
RFLAGS 存储处理器状态信息,例如进位标志、零标志和符号标志等
XMM0-XMM15 扩展多媒体寄存器,可用于存储浮点数和矢量数据
3、 入参规则
在32位系统下,函数参数通常通过栈来传递。而在64位系统下,函数参数则主要通过寄存器进行传递。
在32位系统下,函数参数通常按照从右向左的顺序依次入栈,使用ESP寄存器作为栈指针。常用的参数传递寄存器包括:
EAX:用于存放函数的返回值,同时也可以作为第一个整型参数。
EBX、ECX、EDX:作为普通整型参数的通用寄存器。
ESP、EBP:ESP作为栈指针,EBP作为帧指针,用于访问函数局部变量和参数。
ESI、EDI:作为指针参数或字符串操作的源地址和目的地址。
在64位系统下,函数参数则主要通过寄存器进行传递,寄存器的使用方式如下:
RDI、RSI、RDX、RCX、R8、R9:用于传递前6个整型参数。
XMM0-7:用于传递浮点参数。
RAX:用于存放函数的返回值。
RBX、RSP、RBP、R12-R15:作为通用寄存器和保存上下文的寄存器。
4、标识寄存器
5、syscall命令
syscall 指令用于在 x86-64 架构的 64 位模式下将控制权从用户空间代码转移到内核。当执行 syscall 时,处理器将当前 RIP(指令指针)保存在 RCX 寄存器中,将当前的 RFLAGS 寄存器保存在 R11 寄存器中。系统调用号加载到 RAX 寄存器中,而系统调用参数依次放置在 RDI、RSI、RDX、R10、R8 和 R9 寄存器中。
系统调用完成后,返回值存储在 RAX 寄存器中。R11 寄存器包含系统调用返回时的 RFLAGS 值,RCX 寄存器包含保存的 RIP 值。内核通过执行 iretq 指令将控制权返回给用户空间代码,该指令恢复保存的 RIP 和 RFLAGS 值,并切换回用户模式执行。
6、标志寄存器 https://en.wikipedia.org/wiki/FLAGS_register
7、32位和64位的内存空间
cat /proc/pid/maps查看某进程的实际虚拟内存布局
32位
32位Linux系统下,从0地址开始的内存区域不直接就是代码段区域,而是一段不可访问的保留区。因为大多数系统里,较小数值的地址不是个合法地址,如在C代码里会将无效指针赋为NULL。因此,这里会出现一段不可访问的内存保留区,防止程序因bug,导致读或写了一些小内存地址的数据,而使程序跑飞。
代码段从0x08048000开始排布(以上地址需gcc编译时不开启pie选项)。代码段、数据段都是从可执行文件映像中装载到内存中;BSS段则根据BSS段所需大小,在加载时生成一段0填充的内存空间。
64位系统
0x0 - 0x00007fffffffffff 将低128T划为用户空间
0xffff800000000000 - 0xffffffffffffffff 高128T划分为内核空间
用户空间、内核空间之间有个巨大内存空洞,这块空间不可访问,由CPU保证(这里的地址都不满足Intel 64 Canonical form)。
64位程序,查看/proc/pid/maps会发现代码段跟数据段的中间还有段不可读写的保护段,防止程序在读写数据段的时候越界访问到代码段,这保护段可让越界访问行为直接崩溃,防止它继续往下运行。
8、arm寄存器
ARM 处理器一般共有 37 个寄存器,其中包括:
1) 31 个通用寄存器,包括 PC(程序计数器)在内,都是 32 位的寄存器。
2) 6 个状态寄存器,都是 32 位的寄存器。
ARM 处理器共有 7 种不同的处理器模式:
(1)USR(10000):正常用户模式,程序正常执行模式。
(2)FIQ(10001):快速中断模式,以处理快速情况,支持高速数据传输或通道处理。
(3)IRQ(10010):外部中断模式,普通中断处理
(4)SVC(10011):操作系统保护模式(管理模式),即操作系统使用的特权模式(内核),处理软件中断swi reset
(5)abt(10111):数据访问中止模式,用于 虚拟存储器 和 存储器 保护
(6)und(11011):未定义指令终止模式,用于支持通过软件仿真硬件的协处理器
(7)sys(11111):系统模式,用于运行特权级的操作系统任务( armv4 以上版本才具有)
在所有的寄存器中,有些是各模式共用同一个物理寄存器,有些寄存器是各个模式自己拥有独立的物理寄存器
r0~r3 主要用于子程序间传递参数,r0用来传递返回值
r4~r11 主要用于保存局部变量,但在 Thumb 程序中,通常只能使用 r4~r7 来保存局部变量
r12 用作子程序间scratch 寄存器,即 ip 寄存器
r13 通常用做栈指针,即 sp
r14 寄存器又被称为连接寄存器(lr),用于保存子程序以及中断的返回地址
r15 用作程序计数器(pc),由于 ARM 采用了流水线机制,当正确读取了 PC 的值后,该值为当前指令地址加 8 个字节,即 PC 指向当前指令的下两条指令地址。
CPSR和SPSR都是程序状态寄存器,其中SPSR是用来保存中断前的CPSR中的值,以便在中断返回之后恢复处理器程序状态。
PC指针(r15)
R15用作程序计数器(PC)对应一个物理寄存器,由于ARM体系结构采用了多级流水线技术,对于ARM指令集而言,PC总是指向当前指令的下两条指令的地址,即PC的值为当前指令的地址值加8个字节程序状态寄存器。
CPSR
下面介绍其中几个比较重要的位,其他位,大家可以参考官方手册:
N: 当两个表示的有符号整数运算时,1表示运算结果为负数,0表示结果为正或零。
Z:1表示运算的结果为零,0表示运算的结果不为零。对于CMP指令,1表示进行比较的两个数大小相等。
C:下面分四种情况讨论C的设置方法:
a) 加法运算(包括比较指令CMN):当运算产生了进位时(无符号数溢出),C=1,否则C=0。
b) 减法运算(包括比较指令CMP):当运算时产生了借位(无符号数溢出),C=0,否则C=1。
c) 对于包含移位操作的非加/减运算指令,C为移出值的最后一位。
d) 对于其他的非加/减运算指令,C的值通常不改变。
V:下面分两种情况讨论V的设置方法
a) 对于加/减法运算指令,当操作数和运算结果为二进制的补码表示的带符号数时,V=1表示符号位溢出。
b) 对于其他的非加/减运算指令,C的值通常不改变。
I:1 表示禁止外部(硬件)中断(IRQ)
F:1 表示禁止快速中断(FIQ)
T:1表示为thumb状态0为arm状态
M[4:0]:用来设置处理器的工作模式具体数据见本文开始的介绍。
SPSR
SPSR 除usr、sys外,对应用于异常保护的CPSR的备份,异常时,保存CPSR值,异常退出时,将该值恢复到CPSR,以保证程序的正常运行,每一中异常运行模式(除usr和sys)有各自的物理寄存器。
内存空间分配
Linux内核一般将处理器的虚拟地址空间分成两部分,在32系统上,地址空间在用户进程和内核之间划分的典型比例为3:1,在给出的4GB的虚拟地址空间中,0 ~ 3GB将用于用户空间而3GB ~ 4GB将用于内核空间,内核提供了相关的配置项来修改该比例,也就是说Kernel最多寻址1GB的虚拟地址空间。
arm32位
arm64位
0x04 深度拓展
gdb原理
https://mp.weixin.qq.com/s/teERWh9IRMuO6tieOZNNng
1、gdb ./a.out
以这种方式直接运行时,首先,gdb解析a.out文件的符号。接下来我们输入 run 命令,gdb通过 fork() 一个新进程,然后通过 ptrace(PTRACE_TRACEME, 0, NULL, NULL); 设置traceme模式。最后执行 exec 启动加载要调试的文件。
2、attach pid
在调试PWN题时,通过attach pid来追踪要调试的进程。gdb通过执行 ptrace(PTRACE_ATTACH,pid, 0, 0) 来对目标进程进行追踪。
3、gdb server的target remote
在gdb+qemu调试内核时,经常用到target remote来attach到qemu上对vmlinux进行调试。二者之间有特殊的定义好的数据信息通信的格式,进行通信。
4、汇编转化成机器码 调试测试写好的汇编
https://www.cnblogs.com/ZIKH26/articles/15845766.html
5、符号表缩略含义 cat /proc/kallsyms 含义
Value Descripition Note
A The symbol's value is absolute, and will not be changed by further linking. 符号绝对,链接过程不会改变
B/b The symbol is in the uninitialized data section (known as BSS). 非初始化符号
C The symbol is common. 公有符号,链接时会被同名符号覆盖
D/d The symbol is in the initialized data section. 初始化符号
G/g The symbol is in an initialized data section for small objects. 初始化符号,面向小数据访问优化
I The symbol is an indirect reference to another symbol. 其它符号的间接引用
N The symbol is a debugging symbol. 调试符号
P The symbols is in a stack unwind section. 栈区符号(清空)
R/r The symbol is in a read only data section. 符号只读
S/s The symbol is in an uninitialized data section for small objects. 非初始化符号,面向小数据访问优化
T/t The symbol is in the text (code) section. 代码区符号
U The symbol is undefined. 未定义或在外部定义的符号
u The symbol is a unique global symbol. 全局唯一,GNU保留符
V/v The symbol is a weak object. 弱定义符(详见C++强弱符号定义)
W/w The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. emm...绕口令符号
- The symbol is a stabs symbol in an a.out object file. stabs格式符号
? The symbol type is unknown, or object file format specific. NM也不认识的符号
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。