debug tips
前言
更新时间 | 更新内容 |
---|---|
2021-02-02 | 初稿 |
记录一些关于调试的小技巧.
正文
传统pwn调试
介绍传统glibc pwn题调试的一些技巧:
关闭地址随机化
地址随机是一种针对攻击的缓解措施, 此处讨论的代码段的随机化. 我们使用checksec
检查可执行文件保护措施的时候可以看到PIE
这一项:
$ checksec /bin/ls
[*] '/bin/ls'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
PIE(Position Independent Executables)表示这个程序是位置无关的, 即它可以被加载到内存任意地址处都可以正常执行. 默认情况下这类程序每次执行的时候在内存中的位置都是随机的(按页对齐). 因此极其不利于我们调试的时候进行查看内存, 下断点等操作. 因此我们需要想办法将随机化关掉.
负责将可执行文件加载到内存中的操作(应该)是内核中某块代码负责完成的, 好消息是, 内核给我们提供了一个选项: 用于控制将可执行文件加载到内存中时的随机化程度, 该选项的值可以为 0, 1, 2. 为0时内核在进行加载时就不会进行随机化操作, 为2时则会进行随机化操作. (1 和 2的区别我也不太清楚). 因此我们将该选项置为即可禁用加载时的随机化操作. 此时即使可执行文件支持随机化加载也无济于事了.
修改命令如下:
sudo bash -c "echo 0 > /proc/sys/kernel/randomize_va_space"
注意: 关闭随机化会降低系统的安全性, 所以不建议在服务器上进行该操作.
我们在使用IDA分析这类PIE程序的时候, IDA通常是将elf文件的基地址当作0. 此时我们我们如果在IDA里面看了一下地址, 然后想去gdb里面下个断点或者看个内存什么的也不是很很方便, 需要加上一个基地址什么的. 我们可以通过如下操作在IDA中设置可执行文件的基地址:
Edit -> Segments -> Rebase Program... -> 在value栏输入程序基地址(gdb下使用`vmmap`指令查看 or `cat /proc/pid/maps`查看)
# 一个典型值是 : 0x0000555555554000 (纯经验, 不一定靠谱)
源码级调试glibc中的函数
参考笔者的另一篇文章: glibc相关操作记录
gdb 自动化 (commands)
参考笔者的另一篇文章: gdb自动化操作
调试多进程程序
使用gdb提供的命令设置fork
时跟随的执行流:
set follow-fork-mode child/parent
与pwntools联动
在pwntools中启动gdb调试有两种方法
io = gdb.debug(["pwn", "arg1"], "gdb script")
io = process(["pwn", "arg1"])
gdb.attach(io, "gdb script")
第一种方法get shell时会失败, 注意下即可
第二种方法程序启动之后就会一直执行到阻塞处(如等待用户输入), 所以下断点要下载阻塞之后, 否则可能断不下来.
与IDA联动
qemu system mode 调试
通常是kernel pwn. qemu 启动时加上以下参数
-S -gdb tcp::1234
其中:
- -S表示停在执行之前等待gdb连接
- -gdb tcp::1234 表示监听 1234 端口等待 gdb 连接
调试脚本模板如下:
#!/bin/sh
gdb -q \
-ex "file vmlinux" \
-ex "target remote localhost:1234"
-ex "break *some_addr" \
-ex "break some_symbol" \
-ex "add-symbol-file file_path addr"
-ex "continue" \
;
qemu user-mode 调试
通常是一些非x86架构的题目. 对于非x86架构的题目调试的时候需要使用 gdb-multiarch
, 同时对于不同架构, gdb插件的支持情况也有所不同. 比如 riscv:rv64下pwngdb就用不了, 但是gef可以使用(偶尔会报错).
用qeme启动时加上-g port
参数, 设置监听gdb连接的端口.
riscv调试
#直接启动
./qemu-riscv64 main
#调试启动
./qemu-riscv64 -g 1234 main
#调试脚本(如果是32位的话将 riscv:rv64 换成 riscv:rv32 即可)
#!/bin/sh
gdb-multiarch -q \
-ex 'set architecture riscv:rv64' \
-ex 'file main' \
-ex 'target remote localhost:1234' \
;
arm 调试
# 执行脚本
qemu-aarch64 -cpu ma x -g 1234 -L . ./chall
# 调试脚本
gdb-multiarch -q \
-ex 'set architecture aarch64' \
-ex 'file pwn' \
-ex 'target remote localhost:1234' \
-ex continue \
;
其余架构的题目没有遇到过, 欢迎补充.
直接调试qemu程序
通常是一些qemu逃逸题目.
下面的实例来自于strng2 湖湘杯 2019 – 安全客
$ strng2 cat ./debug
file qemu-system-x86_64
b strng_mmio_read
b strng_mmio_write
b strng_pmio_read
b strng_pmio_write
set $state=0x555556a64db0
set $addr=$state+0xaf0
set $regs=$state+0xaf8
set $timer=$state+0xbf8
run -initrd ./rootfs.cpio -kernel ./vmlinuz-4.8.0-52-generic -append 'console=ttyS0 root=/dev/ram oops=panic panic=1' -enable-kvm -monitor /dev/null -m 64M --nographic -L ./dependency/usr/local/share/qemu -L pc-bios -device strng
$ strng2 sudo gdb --command=./debug
结语
都是一些经验之谈,欢迎补充 :smile:.