gdb是用于调试C程序的一种工具,一般包含在gcc工具链中,
常用于调试用gcc -g编译的可执行文件。
1. 断点命令列表
gdb支持断点命令列表,即在断点发生时执行一连串命令。
如果断点命令列表中包含cont(continue的缩写),
则其行为很像OllyDbg的日志断点。
不过,gdb断点命令列表是针对带源码的程序调试,
而OllyDbg则是汇编级的。
例如,如果我用-g开关编译lua(Windows下用MinGW,Linux下用gcc)
把CC=gcc
改为CC= gcc -g
并且注释掉#$(RANLIB) $@
然后用gdb尝试对llex.c文件的llex静态函数进行断点并输出其参数ls的地址。
可以先写一个gdb脚本(保存为gdbinit.txt):
# # 编译lua时修改Makefile使用-g开关 # CC= gcc -g # 并且注释掉#$(RANLIB) $@ # # 测试平台: # 1. GNU gdb 6.3 for MinGW # 2. GNU gdb (GDB) 7.1-ubuntu # # 启动命令行: # gdb --command=gdbinit.txt --quiet ./lua # # see # http://ftp.gnu.org/old-gnu/Manuals/gdb-5.1.1/html_node/gdb_34.html # Breakpoint command lists b llex.c:llex commands silent printf "ls is %p\n", ls cont end # see # gdb-6.3\gdb\windows-nat.c # Linux下需要注释掉 set debugevents off set debugexceptions off # set args -e "print('hello, world')" run -e "print('hello, world')" quit
这里指定了一个命令列表,然后用
-e "print('hello, world')"
参数执行lua脚本。
其中,如果用Ubuntu的gdb,则删除以下两句
set debugevents off set debugexceptions off
然后执行
--command用于指定前面提到的那个gdbinit脚本
则输出如下信息:
Breakpoint 1 at 0x40a794: file llex.c, line 333.
ls is 0022FB50
ls is 0022FB50
ls is 0022FB50
ls is 0022FB50
ls is 0022FB50
hello, world
Program exited normally.
可以看到lua在解析执行
时,执行了5次llex,每次的ls指针都是相同的。
这里还可以使用更复杂的表达式,然后用gdb自带的printf输出到控制台上。
20120411补注:
网上有许多写得很复杂的gdb脚本,例如这个:
https://github.com/mkottman/lua-gdb-helper
可以参考,不过我自己没有测试过。
----------------------
2. 基于二进制地址的断点设置(仅适用于mingw带源码编译的调试版exe)
如果用mingw编译的exe,可以用nm命令查找符号所在地址。
用这种方法,不需要指定函数所在的文件名。
$ nm ./lua.exe | grep llex
0040a780 t _llex
然后在gdb中设置这个地址
(gdb) b *0x40a780
Breakpoint 1 at 0x40a780: file llex.c, line 332.
3. 用gcc工具链查找函数(包括系统API)引用(仅适用于mingw带源码编译的调试版exe)
如果要查找所有调用llex的地址,可以使用objdump进行反编译
$ objdump -d ./lua.exe | grep call | grep llex
40b14b: e8 30 f6 ff ff call 40a780 <_llex>
40b179: e8 02 f6 ff ff call 40a780 <_llex>
可以看到调用llex函数的地方有两个:0x40b14b和0x40b179。
还可以用addr2line转换为可读的函数名。
$ objdump -d ./lua.exe | grep call | grep llex | addr2line -f -e ./lua.exe
luaX_next
D:/java/home/Administrator/testgprof/lua-5.1.4/src/llex.c:453
luaX_lookahead
D:/java/home/Administrator/testgprof/lua-5.1.4/src/llex.c:459
可以看到引用llex的函数是luaX_next和luaX_lookahead。
利用上面查找到的地址,
用b *0x40b14b和b *0x40b179在gdb中添加断点。
用这种方法可以实现类似API断点的效果
(例如把断点设置在所有使用fprintf的地方)
$ objdump -d ./lua.exe | grep call | grep fprintf
4013de: e8 6d 1a 02 00 call 422e50 <_fprintf>
4013fb: e8 50 1a 02 00 call 422e50 <_fprintf>
40208d: e8 be 0d 02 00 call 422e50 <_fprintf>
405bad: e8 9e d2 01 00 call 422e50 <_fprintf>
414ed2: e8 79 df 00 00 call 422e50 <_fprintf>
41e6d6: e8 cd 49 00 00 call 4230a8 <_vfprintf>
4. 用OllyDbg确定API引用的地址
确定API引用的地址,有助于用断点使程序尽可能停在执行该API之前。
以OllyDbg 1.10为例,要调试的exe为testbp.exe
1) 如果OllyDbg的标题栏显示此时CPU窗口显示的是exe所在的模块
标题应该是:
OllyDbg - testbp.exe -[CPU - module testbp]
如果标题是:
OllyDbg - testbp.exe -[CPU - module kernel32]
需要切换回exe所在模块:
CPU->右键->View->View->Module testbp
2) 查找导入API函数的引用位置
CPU->右键->Search for->Name (Label) in current module (Ctrl+N)
弹出Names in testbp列表一般很长,可以粘贴出来查找。
如果对
KERNEL32.OutputDebugStringA
感兴趣,那么
选中->右键->Find references to import
则弹出一个引用列表,显示该API在反编译后被引用的所有地址
(如果API的地址不是动态获取的)。
3) 查看某个地址附近的指令
有时用工具知道大概要关心的地址,可以在
CPU->右键->Go to->Expression
输入地址(可忽略0x前缀)跳转到相应位置。
CPU->右键->Go to->Previous procedure
跳转到当前地址之前最近的过程入口点(相当于函数入口,一般标有$)。
4) 列出交叉引用表(call <...>)
CPU->右键->Search for->All intermodular calls
和1, 2的做法差不多,也可用于下API断点或日志断点。
5. 用IDA Pro确定API引用的地址
View->Open subviews->Imports
(或View->Open subviews->Names,列出所有模块的符号,
或View->Open subviews->Functions,列出所有方法)
选中感兴趣的API如fprintf(或Search->Search...查找字符串)
双击跳到IDA View A视图
然后用鼠标把光标移到
.idata:0042A390 ; int fprintf(FILE *File, const char *Format, ...)
的fprintf
右键->Jump to xref to operand... X
打开交叉引用列表,列出所有引用fprintf函数的相对地址。
如果要查看绝对地址,需要双击跳转。
6. 无源码的内存查看(类似OllyDbg的log breakpoint)
用gcc编译的可执行文件(包括exe)可以用命令列表实现日志断点(立刻恢复的断点)。
但是,即便无源码,也可以通过直接读取内存,或通过寄存器的指针值读取内存。
例如,
用OllyDbg依附mysqld后步进调试,(不能打开Handle窗口,OllyDbg会崩溃)
发现mysqld在执行到0x0045da00地址时ebx指向一个字符串,
是网络接收到的命令(不带末尾的分号),可以用如下gdb命令查看此时的字符串值
(gdb) attach 7820
(gdb) b *0x0045da00
(gdb) c
(gdb) i r
(gdb) p $ebx
(gdb) x/s $ebx
(gdb) printf "%s\n", $ebx
每当mysqld接收到字符串时,执行到0x0045da00处会被gdb中断,
然后可以通过x/s $ebx或printf "%s\n", $ebx输出接收到的字符串。
x表示读内存,ebx作为寄存器,必须带$前缀,s表示数据类型为字符串。
用这种方法结合命令列表可实时输出mysqld通过recv调用获取的字符串内容。
7. 产生core核心转储文件,分析堆栈回溯和其它运行时信息(主要用于linux)(对于win32的mingw,见第8项)
(1) 用ulimit和kill生成core文件
这种方法会中止进程,但可以查看较详细的堆栈回溯信息。
$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 20
file size (blocks, -f) unlimited
pending signals (-i) 16382
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) unlimited
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
$ ulimit -c unlimited
$ ./lua
(注意,ulimit只对当前会话有效)
$ ps -a | grep lua
19950 pts/0 00:00:00 lua
$ kill -6 19950
(kill -6是Aborted,kill -11是Segmentation fault)
$ ls | grep core
core
$ gdb ./lua ./core
(gdb) bt
(2) 强制生成core文件
这种方法不中止进程,但堆栈回溯信息不详。
$ ps -a | grep lua
19678 pts/0 00:00:00 lua
$ gcore 19678
0x00ef9422 in __kernel_vsyscall ()
Saved corefile core.19678
$ gdb ./lua ./core.19678
(20140126补充:)
8.win32 mingw gdb的attach调试(相当于linux的core转储,用于判断程序卡在哪个位置),
(1)用-g3 -O0参数编译程序。
(2)如果程序会崩溃的话,直接用gdb启动程序(run命令)即可,不需要用attach命令,gdb会自动暂停,用bt命令可看到后退回溯(backtrace)。
(3)否则,可以尝试用attach(相当于上面的core转储)
首先,不用gdb直接启动程序,然后通过任务管理器查询到进程号(通常是4位整数),然后在合适的时候手动执行attach:
(gdb) attach <进程号>
然后列举线程
(gdb) info threads
切换线程(t=thread命令)
(gdb) thread <线程编号>
打印后退回溯(bt=backtrace命令)
(gdb) bt
通常主线程在thread 1上,但有可能会看不到bt,如果是这样需要暂停多次(退出后在合适的时候重新执行attach命令)。
如果仍无法看到bt,可以考虑用breakpoint方法(这个更需要技巧确定断在什么位置)
(gdb) b <文件名>:<行号>
删除breakpoint。
(gdb) info break
(gdb) remove <断点序号>
(TODO)
------------------------
参考资料:
1. Debugging Mozilla with gdb
https://developer.mozilla.org/en/Debugging_Mozilla_on_Linux_FAQ
4. eglog
5. Digital Travesia
http://hp.vector.co.jp/authors/VA028184/
6. GDB 多线程调试
http://www.yuanma.org/data/2009/0407/article_3605.htm
http://www.linuxforum.net/forum/gshowflat.php?Cat=&Board=program&Number=692404&page=0&view=collapsed
http://www.ibm.com/developerworks/cn/java/j-memoryanalyzer/
7. DLL劫持技术(内存补丁技术) (转)
http://hi.baidu.com/woaiwlaopo/blog/item/699f2a3fb0485ee954e7238d.html
8. gdb的一些命令
http://blog.sinzy.net/ifyr/entry/22198
9. Windows API Hooking Tutorial
http://ruffnex.oc.to/kenji/text/api_hook/
10. Visual Novel tools
(其它,游戏提取,与上述内容无关。。。)
11. Welcome to m-akita's Home Page
http://m-akita.sakura.ne.jp/index.php
12. asmodean's reverse engineering page - news and updates
13. VN Translation
http://tlwiki.tsukuru.info/index.php?title=Tools
14. notazsite
http://anime.geocities.jp/notazsite1/soft/index.html
Irregular child .. ?
http://hp.vector.co.jp/authors/VA018359/index2.html
(20130715)
15. 混沌の部屋
http://www.geocities.jp/hoku_hoshi/index2.html
(20150528)
16. 【教材】用OllyDbg找出Agth提取GAL文本的特殊码(详细新人版)
http://867258173.diandian.com/post/2014-02-12/40060970396