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里数据的汇编行
选中字符串后鼠标右键点击交叉引用列表
image.png
2、查看二进制里所有的字符串
视图->子视图->字符串
image.png

2、ida联合gdb进行远程调试
F8单步调试,F9单步进入函数
https://uuzdaisuki.com/2020/02/04/ida%E8%BF%9C%E7%A8%8B%E8%B0...

3、ida远程调试之动态库
windows上加载你要调试的动态库,远程配置如下图所示。应用程序选择加载该动态库的应用程序,输入文件是该动态库。
image.png

4、反汇编报错positive sp value has found
https://blog.csdn.net/u010334666/article/details/82937413
在选项-常规勾选堆栈指针
image.png
可以看到堆栈指针
image.png
把出现负数的上一行改成和负数一样的值
image.png

5、给汇编代码添加描述
image.png

汇编显示机器码
image.png

6、规则
str R0,[sp,#0x428+var_420] 把R0的值存在sp+0x428-0x420所在地址里
var_14 是ida创建的汇编宏,其数值等于-0x14

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
  • 需要使用gdb调试时,在发送数据前输入pause(),然后gdb -p 进程号连接
    image.png

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 以字符串形式查看
    x/5i 0x3333 展示地址处开始汇编指令
    更多格式
    单位:

      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)
      i: 汇编指令
  • 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位计算机常用寄存器
image.png

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、标识寄存器
image.png

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
image.png

7、32位和64位的内存空间
cat /proc/pid/maps查看某进程的实际虚拟内存布局
32位
image.png
32位Linux系统下,从0地址开始的内存区域不直接就是代码段区域,而是一段不可访问的保留区。因为大多数系统里,较小数值的地址不是个合法地址,如在C代码里会将无效指针赋为NULL。因此,这里会出现一段不可访问的内存保留区,防止程序因bug,导致读或写了一些小内存地址的数据,而使程序跑飞。

代码段从0x08048000开始排布(以上地址需gcc编译时不开启pie选项)。代码段、数据段都是从可执行文件映像中装载到内存中;BSS段则根据BSS段所需大小,在加载时生成一段0填充的内存空间。

64位系统
0x0 - 0x00007fffffffffff 将低128T划为用户空间
0xffff800000000000 - 0xffffffffffffffff 高128T划分为内核空间
image.png

用户空间、内核空间之间有个巨大内存空洞,这块空间不可访问,由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中的值,以便在中断返回之后恢复处理器程序状态。
image.png

PC指针(r15)

R15用作程序计数器(PC)对应一个物理寄存器,由于ARM体系结构采用了多级流水线技术,对于ARM指令集而言,PC总是指向当前指令的下两条指令的地址,即PC的值为当前指令的地址值加8个字节程序状态寄存器。

CPSR
image.png
下面介绍其中几个比较重要的位,其他位,大家可以参考官方手册:

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位
image.png
arm64位
image.png

9、不同架构入参总结

  • x86
    PUSH 3rd argument
    PUSH 2nd argument
    PUSH 1st argument
    CALL function
    ; modify stack pointer (if needed)
  • x64(MSVC)
    MOV RCX, 1st argument
    MOV RDX, 2nd argument
    MOV R8, 3rd argument
    MOV R9, 4th argument
    PUSH 5th, 6th argument, etc (if needed)
    CALL function
    ; modify stack pointer (if needed)
  • X64(GCC)
    MOV RDI, 1st argument
    MOV RSI, 2nd argument
    MOV RDX, 3rd argument
    MOV RCX, 4th argument
    MOV R8, 5th argument
    MOV R9, 6th argument
    PUSH 7th, 8th argument, etc (if needed
    CALL function
    ; modify stack pointer (if needed)
  • ARM
    MOV R0, 1st argument
    MOV R1, 2nd argument
    MOV R2, 3rd argument
    MOV R3, 4th argument
    ; pass 5th, 6th argument, etc, in stack (if needed)
    BL function
    ;modify stack pointer (if needed)
  • ARM64
    MOV X0, lst argument
    MOV X1, 2nd argument
    MOV X2, 3rd argument
    MOV X3, 4th argument
    MOV X4, 5th argument
    MOV X5, 6th argument
    MOV X6, 7th argument
    MOV X7, 8th argument
    ; pass 9th, 10th argument, etc, in stack (if needed
    BL CALL function
    ;modify stackpointer (if needed)
  • MIPS(O32)
    LI $4, 1st argument ; AKA $A0
    LI $5, 2nd argument ; AKA $A1
    LI $6, 3rd argument ; AKA $A2
    LI $7, 4th argument ; AKA $A3
    ;pass 5th, 6th argument, etc, in stack (if needed
    LW temp_reg, address of function
    JALR temp_reg

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也不认识的符号


白风之下
10 声望3 粉丝