Debuggers 1012:Introductory GDB
编程语言心法参考:http://www.yinwang.org/blog-cn/2017/07/06/master-pl
英语阅读速成:http://www.yinwang.org/blog-cn/2018/11/23/grammar
记住:晦涩难懂的示例代码直接丢GPT
希望你也搞一个证书,纪念纪念自己的旅途。
OpenSecurityTraining2 Learning Paths: Vulnerability Hunting & Exploitation
python:https://www.learnpython.org/
路径图:https://ost2.fyi/OST2_LP_Vulns_Exploits.pdf
Debuggers 1012:Introductory GDB(与 python ) --> Architecture 1001:x86-64 Assembly --> Reverse Engineering 3201:Symbolic Analysis
Download & Install
设置 Ubuntu 系统
VMWare Workstation Player 16: x86-64 Ubuntu 20.04 VM
https://www.youtube.com/watch?v=wGL-IFr-8Is
https://ubuntu.com/download/desktop
VirtualBox 6.1: x86-64 Ubuntu 20.04 VM
https://apps.p.ost2.fyi/learning/course/course-v1:OpenSecurityTraining2+Lab_Setup_x86-64_Ubuntu+2021_v1/home
安装 gcc 和 gdb
sudo apt-get install -y gcc gdb
gcc –v
gdb –version
Create Test Executable 创建测试可执行文件
Compiling Test Programs fibber.c and echo.c 编译测试程序 fibber.c 和 echo.c
将以下代码复制并粘贴到名为 fibber.c 文件中
// fibber.c: A simple recursive Fibbonacci sequence calculation program
#include <stdio.h>
unsigned int fibbonacci(unsigned int n){
if(n <= 1){
return n;
}
else{
return (fibbonacci(n-1) + fibbonacci(n-2));
}
}
int main(){
unsigned int n = 10;
printf("First %d elements of the Fibbonacci sequence: ", n);
for(unsigned int i = 0; i < n; i++){
printf("%d ", fibbonacci(i));
}
printf("\n");
return 0;
}
编译它
gcc -ggdb fibber.c -o fibber_bin # -ggdb 将GDB debugging symbols添加到二进制文件中
通过运行“./fibber_bin”(不带引号)确认其已执行。
复制并粘贴以下代码到名为 echo.c 的文件中
// echo.c: A simple program to take input on the command line and echo it back out
#include <stdio.h>
void main(int argc, char ** argv){
if(argv[1] != NULL && argv[1] != ""){
printf("You entered %s for argv[1]\n", argv[1]);
} else {
printf("You didn't enter an argv[1]\n");
}
}
Compile it with:
gcc -ggdb echo.c -o echo_bin
通过运行“./echo_bin yo”(不带引号)确认其执行。
Loading Binaries 加载二进制文件
Loading a Binary from Disk 从磁盘加载二进制文件
从文件系统加载二进制文件:选项 1
user@ubuntu:~$ gdb --quiet ./fibber_bin
Reading symbols from ./fibber_bin...
(No debugging symbols found in ./fibber_bin)
(gdb)
--quiet is equivalent to -q
从文件系统加载二进制文件:选项 2
user@ubuntu:~$ gdb -q
(gdb) file ./fibber_bin
Reading symbols from ./fibber_bin...
(No debugging symbols found in ./fibber_bin)
(gdb)
退出 GDB
user@ubuntu:~$ gdb -q
(gdb) quit
user@ubuntu:~$
Starting, Stopping, Restarting Binaries 启动、停止、重新启动二进制文件
Commands: run, start, continue 命令:运行、启动、继续
运行(或重新运行)程序
run(简称:r)命令将运行程序
https://www.sourceware.org/gdb/current/onlinedocs/gdb
https://web.archive.org/web/20221209001604/http://www.sourceware.org/gdb/current/onlinedocs/gdb/Starting.html#Starting
运行需要 CLI 参数的程序
如果您的程序需要 CLI 参数,则可以在运行命令后传递它们,就像您直接调用程序一样。
比如:./echo_bin yo
user@ubuntu:~$ gdb ./echo_bin -q
Reading symbols from ./echo_bin...
(gdb) r
Starting program: /home/user/echo_bin
You didn't enter an argv[1]
[Inferior 1 (process 60868) exited with code 034]
(gdb) r hi
Starting program: /home/user/echo_bin hi
You entered hi for argv[1]
[Inferior 1 (process 60872) exited with code 033]
(gdb) r yo
Starting program: /home/user/echo_bin yo
You entered yo for argv[1]
[Inferior 1 (process 60873) exited with code 033]
启动程序并在其入口点中断
start 命令类似于 run,只不过它在程序的入口点设置断点。(后面的二进制课程将教你如何自己找到入口点。)
user@ubuntu:~$ gdb ./fibber_bin -q
Reading symbols from ./fibber_bin...
(gdb) start
Temporary breakpoint 1 at 0x1189
Starting program: /home/user/fibber_bin
Temporary breakpoint 1, 0x0000555555555189 in main ()
(gdb)
与运行类似,您可以在启动命令后指定其他 CLI 参数:
user@ubuntu:~$ gdb ./echo_bin -q
Reading symbols from ./echo_bin...
(No debugging symbols found in ./echo_bin)
(gdb) start hi
Temporary breakpoint 1 at 0x1169
Starting program: /home/user/echo_bin hi
Temporary breakpoint 1, 0x0000555555555169 in main ()
(gdb)
继续执行在断点处停止的程序
如果你使用 start 启动了程序,那么可以使用 continue(缩写:c)从程序设置的临时断点继续执行。
user@ubuntu:~$ gdb ./echo_bin -q
Reading symbols from ./echo_bin...
(gdb) start hi
Temporary breakpoint 1 at 0x1169
Starting program: /home/user/echo_bin hi
Temporary breakpoint 1, 0x0000555555555169 in main ()
(gdb) c
Continuing.
You entered hi for argv[1]
[Inferior 1 (process 60985) exited with code 033]
(gdb)
Working with Breakpoints in Source & Assembly Debugging 在源代码和汇编调试中使用断点
Code Breakpoints: Setting, Listing, and Deleting 代码断点:设置、列出和删除
设置代码断点:
可以使用 break 命令(缩写:b)设置断点。
(gdb) break main
Breakpoint 1 at 0x11a9
(gdb) b fibbonacci
Breakpoint 2 at 0x1169
列出代码断点:
可以使用 info breakpoints 或更短的形式 info break 和 info b 列出断点
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000000011a9 <main>
2 breakpoint keep y 0x0000000000001169 <fibbonacci>
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000000011a9 <main>
2 breakpoint keep y 0x0000000000001169 <fibbonacci>
取消设置代码断点:
clear <address>
命令可以删除地址处的断点,该地址由符号名称或 * 后跟绝对内存地址指定。
(gdb) clear *0x00000000000011a9
(gdb) info b
Deleted breakpoint 1 Num Type Disp Enb Address What
2 breakpoint keep y 0x0000000000001169 <fibbonacci>
(gdb) clear fibbonacci
(gdb) info b
Deleted breakpoint 2 No breakpoints or watchpoints.
delete <breakpoint number from info breakpoints>
(缩写:d)将删除“info b”输出中指定的编号所给出的特定断点。
(gdb) b main
Breakpoint 3 at 0x11a9
(gdb) info b
Num Type Disp Enb Address What
3 breakpoint keep y 0x00000000000011a9 <main>
(gdb) delete 3
(gdb) info b
No breakpoints or watchpoints.
Breakpoints as offets vs. addresses in memory 断点作为偏移量 VS 内存中的地址
请注意,当断点最初打印出来时,它们是较小的数字。但是当程序启动并再次打印出来时,它们是较大的数字。这是因为在程序运行之前,“地址”实际上只是可执行文件代码执行开始处的偏移量。调试器还不知道程序将在内存中的哪个位置加载,直到它实际执行。这也是因为操作系统可能会随机化可执行文件在内存中的加载位置(一种称为地址空间布局随机化 (ASLR) 的机制,旨在帮助缓解一些安全漏洞。)因此,在可执行文件启动后,调试器现在可以显示可执行文件在内存中加载并启动后的真实地址。(注意:如果您执行相同的步骤,您的地址通常会与下面显示的不同。)
sec875@ubuntu:~$ gdb ./fibber_bin -q
Reading symbols from ./fibber_bin...
(gdb) b main
Breakpoint 1 at 0x11a9: file fibber.c, line 13.
(gdb) break fibbonacci
Breakpoint 2 at 0x1169: file fibber.c, line 4.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000000011a9 in main at fibber.c:13
2 breakpoint keep y 0x0000000000001169 in fibbonacci at fibber.c:4
(gdb) r
Starting program: /home/sec875/fibber_bin
Breakpoint 1, main () at fibber.c:13
13 int main(){
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551a9 in main at fibber.c:13
breakpoint already hit 1 time
2 breakpoint keep y 0x0000555555555169 in fibbonacci at fibber.c:4
(gdb) delete 2
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551a9 in main at fibber.c:13
breakpoint already hit 1 time
(gdb) clear *0x00005555555551a9
(gdb) info b
Deleted breakpoint 1 No breakpoints or watchpoints.
(gdb) b main
Breakpoint 3 at 0x5555555551a9: file fibber.c, line 13.
(gdb) delete 3
(gdb) info b
No breakpoints or watchpoints.
(gdb) quit
A debugging session is active.
Inferior 1 [process 6761] will be killed.
Quit anyway? (y or n) y
sec875@ubuntu:~$
检查符号表:https://sourceware.org/gdb/current/onlinedocs/gdb.html/Symbols.html#Symbols
查询程序中定义的符号(变量、函数和类型的名称)。此信息是程序文本中固有的,不会随着程序的执行而改变。GDB 会在程序的符号表、启动 GDB 时指定的文件中(请参阅选择文件)或文件管理命令之一(请参阅指定文件的命令)中找到它。
sec875@ubuntu:~$ gdb ./fibber_bin1 -q
Reading symbols from ./fibber_bin1...
(No debugging symbols found in ./fibber_bin1)
(gdb) p 'main'
$1 = {<text variable, no debug info>} 0x11a9 <main>
(gdb) p 'fibbonacci'
$2 = {<text variable, no debug info>} 0x1169 <fibbonacci>
(gdb) info address main
Symbol "main" is at 0x11a9 in a file compiled without debugging.
(gdb) info address fibbonacci
Symbol "fibbonacci" is at 0x1169 in a file compiled without debugging.
(gdb) b main
Breakpoint 1 at 0x11a9
(gdb) b fibbonacci
Breakpoint 2 at 0x1169
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000000011a9 <main>
2 breakpoint keep y 0x0000000000001169 <fibbonacci>
(gdb) info symbol 0x11a9
main in section .text
(gdb) info symbol 0x1169
fibbonacci in section .text
(gdb)
无符号(fibber_bin1)与有符号(fibber_bin)
gcc -ggdb fibber.c -o fibber_bin # -ggdb 将GDB debugging symbols添加到二进制文件中
sec875@ubuntu:~$ gdb ./fibber_bin1 -q
Reading symbols from ./fibber_bin1...
(No debugging symbols found in ./fibber_bin1)
(gdb) p './fibber.c'::main
No symbol table is loaded. Use the "file" command.
(gdb) p 'fibber.c'::main
No symbol table is loaded. Use the "file" command.
(gdb) file fibber.bin1
fibber.bin1: No such file or directory.
(gdb) file fibber.c
"/home/sec875/fibber.c": not in executable format: file format not recognized
(gdb) file fibber_bin1
Reading symbols from fibber_bin1...
(No debugging symbols found in fibber_bin1)
(gdb) q
sec875@ubuntu:~$ gdb ./fibber_bin -q
Reading symbols from ./fibber_bin...
(gdb) p 'fibber.c'::main
$1 = {int ()} 0x11a9 <main>
(gdb) p 'fibber.c'::return
No symbol "return" in specified context.
(gdb) p 'fibber.c'::fibbonacci
$2 = {unsigned int (unsigned int)} 0x1169 <fibbonacci>
(gdb) b main
Breakpoint 1 at 0x11a9: file fibber.c, line 13.
(gdb) c
The program is not being run.
(gdb) r
Starting program: /home/sec875/fibber_bin
Breakpoint 1, main () at fibber.c:13
13 int main(){
(gdb) p 'fibber.c'::main
$3 = {int ()} 0x5555555551a9 <main>
(gdb)
All Breakpoints: Disabling and Enabling 所有断点:禁用和启用
禁用断点:
从“info b”获取断点编号后,可以使用以下命令禁用断点
disable <breakpoint number>
随后运行“info b”将在输出的“Enb”(启用)列中将断点显示为“n”。例如:
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551a9 <main>
breakpoint already hit 1 time
(gdb) disable 1
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep n 0x00005555555551a9 <main>
breakpoint already hit 1 time
启用断点:
从“info b”获取断点编号后,可以使用以下命令启用断点
enable <breakpoint number>
随后运行“info b”将在输出的“Enb”(启用)列中将断点显示为“y”。例如:
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep n 0x00005555555551a9 <main>
breakpoint already hit 1 time
(gdb) enable 1
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551a9 <main>
breakpoint already hit 1 time
演练
sec875@ubuntu:~$ gdb ./fibber_bin -q
Reading symbols from ./fibber_bin...
(gdb) b main
Breakpoint 1 at 0x11a9: file fibber.c, line 13.
(gdb) b fibbonacci
Breakpoint 2 at 0x1169: file fibber.c, line 4.
(gdb) r
Starting program: /home/sec875/fibber_bin
Breakpoint 1, main () at fibber.c:13
13 int main(){
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551a9 in main at fibber.c:13
breakpoint already hit 1 time
2 breakpoint keep y 0x0000555555555169 in fibbonacci at fibber.c:4
(gdb) c
Continuing.
Breakpoint 2, fibbonacci (n=21845) at fibber.c:4
4 unsigned int fibbonacci(unsigned int n){
(gdb) c
Continuing.
Breakpoint 2, fibbonacci (n=0) at fibber.c:4
4 unsigned int fibbonacci(unsigned int n){
(gdb) c
Continuing.
Breakpoint 2, fibbonacci (n=1) at fibber.c:4
4 unsigned int fibbonacci(unsigned int n){
(gdb) disable 2
(gdb) c
Continuing.
First 10 elements of the Fibbonacci sequence: 0 1 1 2 3 5 8 13 21 34
[Inferior 1 (process 1923) exited normally]
(gdb) r
Starting program: /home/sec875/fibber_bin
Breakpoint 1, main () at fibber.c:13
13 int main(){
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551a9 in main at fibber.c:13
breakpoint already hit 1 time
2 breakpoint keep n 0x0000555555555169 in fibbonacci at fibber.c:4
(gdb) enable 2
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551a9 in main at fibber.c:13
breakpoint already hit 1 time
2 breakpoint keep y 0x0000555555555169 in fibbonacci at fibber.c:4
(gdb) c
Continuing.
Breakpoint 2, fibbonacci (n=21845) at fibber.c:4
4 unsigned int fibbonacci(unsigned int n){
(gdb) c
Continuing.
Breakpoint 2, fibbonacci (n=0) at fibber.c:4
4 unsigned int fibbonacci(unsigned int n){
(gdb) q
A debugging session is active.
Inferior 1 [process 2302] will be killed.
Quit anyway? (y or n) y
sec875@ubuntu:~$
Examining Source Code 检查源代码
列出源代码行
此选项仅与您拥有要调试的程序的源代码并且它是在启用符号的情况下构建的(gcc 的 -g 或 -ggdb 标志)有关。在对不透明二进制文件进行逆向工程时,它不会很有用,但在一些 OST2 课程(例如 Architecture 4021:Introductory UEFI)中很有用,您可以在其中自己编译源代码。
list
命令将显示当前停止位置周围的源代码。
list <function name>
命令将显示给定函数之前和之后的源代码。
list <source file name>:<line number>
list <源文件名>:<行号> 命令将显示给定文件中给定行之前和之后的源代码。
https://web.archive.org/web/20220606212422/https://sourceware.org/gdb/current/onlinedocs/gdb/List.html#List
演练:
sec875@ubuntu:~$ gdb ./fibber_bin1 -q
Reading symbols from ./fibber_bin1...
(No debugging symbols found in ./fibber_bin1)
(gdb) list
No symbol table is loaded. Use the "file" command.
(gdb) quit
sec875@ubuntu:~$ gdb ./fibber_bin -q
Reading symbols from ./fibber_bin...
(gdb) list
1 // fibber.c: A simple recursive Fibbonacci sequence calculation program
2 #include <stdio.h>
3
4 unsigned int fibbonacci(unsigned int n){
5 if(n <= 1){
6 return n;
7 }
8 else{
9 return (fibbonacci(n-1) + fibbonacci(n-2));
10 }
(gdb) b main
Breakpoint 1 at 0x11a9: file fibber.c, line 13.
(gdb) r
Starting program: /home/sec875/fibber_bin
Breakpoint 1, main () at fibber.c:13
13 int main(){
(gdb) list
8 else{
9 return (fibbonacci(n-1) + fibbonacci(n-2));
10 }
11 }
12
13 int main(){
14 unsigned int n = 10;
15
16 printf("First %d elements of the Fibbonacci sequence: ", n);
17
(gdb) list fibber.c:0
1 // fibber.c: A simple recursive Fibbonacci sequence calculation program
2 #include <stdio.h>
3
4 unsigned int fibbonacci(unsigned int n){
5 if(n <= 1){
6 return n;
7 }
8 else{
9 return (fibbonacci(n-1) + fibbonacci(n-2));
10 }
(gdb) quit
A debugging session is active.
Inferior 1 [process 2801] will be killed.
Quit anyway? (y or n) y
sec875@ubuntu:~$
忘记屏幕录制应用程序和模糊的视频。 体验轻量级、基于文本的终端录制方法。 https://asciinema.org/
Examining Assembly 检查汇编
One-time Disassembly Display 一次性反汇编展示
disassemble
反汇编命令(简称:disas
)本身将显示您所停留的当前位置周围的汇编。
disassemble <address or symbol name>
disassemble <地址或符号名称> 命令将反汇编由地址或符号名称指定的内存。
您可以选择在反汇编后给出 /r
参数来显示构成指令的 raw 原始字节。
您可以选择在反汇编后给出 /m
参数来 mix 混合源代码和汇编代码。
要同时给出两个参数,必须将它们组合为“/rm
”。
https://web.archive.org/web/20221127202729/https://sourceware.org/gdb/current/onlinedocs/gdb/Machine-Code.html
或者,x
(examine检查 内存)命令的一种形式(我们稍后会详细了解)可以在给定地址处显示一定数量的指令(在下面的示例中为 10 条)。该形式为:
x/10i <address>
Continuously-updating Disassembly Display 持续更新反汇编展示
display
命令的一种形式(我们稍后会详细了解)可以确保每次程序停止时,都会从寄存器中给出的地址开始打印出一些指令(在下面的例子中为 10 条),该地址表示 CPU 要执行的下一条汇编指令。该形式为:
display/10i <instruction pointer / program counter>
在 x86 系统上,该寄存器被称为“ instruction pointer 指令指针”,对于 64 位代码来说,具体来说是“RIP”,因此命令将是 display/10i $rip
在 x86 系统上,该寄存器被称为“指令指针”,对于 32 位代码来说具体是“EIP”,因此命令为 display/10i $eip
在 ARM 或 RISC-V 系统上,寄存器被称为“ program counter 程序计数器”,即 display/10i $pc
演练
sec875@ubuntu:~$ gdb ./fibber_bin1 -q
Reading symbols from ./fibber_bin1...
(No debugging symbols found in ./fibber_bin1)
(gdb) b main
Breakpoint 1 at 0x11a9
(gdb) b fibbonacci
Breakpoint 2 at 0x1169
(gdb) r
Starting program: /home/sec875/fibber_bin1
Breakpoint 1, 0x00005555555551a9 in main ()
(gdb) disas
Dump of assembler code for function main:
=> 0x00005555555551a9 <+0>: endbr64
0x00005555555551ad <+4>: push %rbp
0x00005555555551ae <+5>: mov %rsp,%rbp
0x00005555555551b1 <+8>: sub $0x10,%rsp
0x00005555555551b5 <+12>: movl $0xa,-0x4(%rbp)
0x00005555555551bc <+19>: mov -0x4(%rbp),%eax
0x00005555555551bf <+22>: mov %eax,%esi
0x00005555555551c1 <+24>: lea 0xe40(%rip),%rdi # 0x555555556008
0x00005555555551c8 <+31>: mov $0x0,%eax
0x00005555555551cd <+36>: callq 0x555555555070 <printf@plt>
0x00005555555551d2 <+41>: movl $0x0,-0x8(%rbp)
0x00005555555551d9 <+48>: jmp 0x5555555551fc <main+83>
0x00005555555551db <+50>: mov -0x8(%rbp),%eax
0x00005555555551de <+53>: mov %eax,%edi
0x00005555555551e0 <+55>: callq 0x555555555169 <fibbonacci>
0x00005555555551e5 <+60>: mov %eax,%esi
0x00005555555551e7 <+62>: lea 0xe49(%rip),%rdi # 0x555555556037
0x00005555555551ee <+69>: mov $0x0,%eax
0x00005555555551f3 <+74>: callq 0x555555555070 <printf@plt>
0x00005555555551f8 <+79>: addl $0x1,-0x8(%rbp)
0x00005555555551fc <+83>: mov -0x8(%rbp),%eax
0x00005555555551ff <+86>: cmp -0x4(%rbp),%eax
0x0000555555555202 <+89>: jb 0x5555555551db <main+50>
0x0000555555555204 <+91>: mov $0xa,%edi
0x0000555555555209 <+96>: callq 0x555555555060 <putchar@plt>
0x000055555555520e <+101>: mov $0x0,%eax
0x0000555555555213 <+106>: leaveq
0x0000555555555214 <+107>: retq
End of assembler dump.
(gdb) x/10i fibbonacci
0x555555555169 <fibbonacci>: endbr64
0x55555555516d <fibbonacci+4>: push %rbp
0x55555555516e <fibbonacci+5>: mov %rsp,%rbp
0x555555555171 <fibbonacci+8>: push %rbx
0x555555555172 <fibbonacci+9>: sub $0x18,%rsp
0x555555555176 <fibbonacci+13>: mov %edi,-0x14(%rbp)
0x555555555179 <fibbonacci+16>: cmpl $0x1,-0x14(%rbp)
0x55555555517d <fibbonacci+20>: ja 0x555555555184 <fibbonacci+27>
0x55555555517f <fibbonacci+22>: mov -0x14(%rbp),%eax
0x555555555182 <fibbonacci+25>: jmp 0x5555555551a2 <fibbonacci+57>
(gdb) display/10i $rip
1: x/10i $rip
=> 0x5555555551a9 <main>: endbr64
0x5555555551ad <main+4>: push %rbp
0x5555555551ae <main+5>: mov %rsp,%rbp
0x5555555551b1 <main+8>: sub $0x10,%rsp
0x5555555551b5 <main+12>: movl $0xa,-0x4(%rbp)
0x5555555551bc <main+19>: mov -0x4(%rbp),%eax
0x5555555551bf <main+22>: mov %eax,%esi
0x5555555551c1 <main+24>: lea 0xe40(%rip),%rdi # 0x555555556008
0x5555555551c8 <main+31>: mov $0x0,%eax
0x5555555551cd <main+36>: callq 0x555555555070 <printf@plt>
(gdb) c
Continuing.
Breakpoint 2, 0x0000555555555169 in fibbonacci ()
1: x/10i $rip
=> 0x555555555169 <fibbonacci>: endbr64
0x55555555516d <fibbonacci+4>: push %rbp
0x55555555516e <fibbonacci+5>: mov %rsp,%rbp
0x555555555171 <fibbonacci+8>: push %rbx
0x555555555172 <fibbonacci+9>: sub $0x18,%rsp
0x555555555176 <fibbonacci+13>: mov %edi,-0x14(%rbp)
0x555555555179 <fibbonacci+16>: cmpl $0x1,-0x14(%rbp)
0x55555555517d <fibbonacci+20>: ja 0x555555555184 <fibbonacci+27>
0x55555555517f <fibbonacci+22>: mov -0x14(%rbp),%eax
0x555555555182 <fibbonacci+25>: jmp 0x5555555551a2 <fibbonacci+57>
(gdb) c
Continuing.
Breakpoint 2, 0x0000555555555169 in fibbonacci ()
1: x/10i $rip
=> 0x555555555169 <fibbonacci>: endbr64
0x55555555516d <fibbonacci+4>: push %rbp
0x55555555516e <fibbonacci+5>: mov %rsp,%rbp
0x555555555171 <fibbonacci+8>: push %rbx
0x555555555172 <fibbonacci+9>: sub $0x18,%rsp
0x555555555176 <fibbonacci+13>: mov %edi,-0x14(%rbp)
0x555555555179 <fibbonacci+16>: cmpl $0x1,-0x14(%rbp)
0x55555555517d <fibbonacci+20>: ja 0x555555555184 <fibbonacci+27>
0x55555555517f <fibbonacci+22>: mov -0x14(%rbp),%eax
0x555555555182 <fibbonacci+25>: jmp 0x5555555551a2 <fibbonacci+57>
(gdb) disas main
Dump of assembler code for function main:
0x00005555555551a9 <+0>: endbr64
0x00005555555551ad <+4>: push %rbp
0x00005555555551ae <+5>: mov %rsp,%rbp
0x00005555555551b1 <+8>: sub $0x10,%rsp
0x00005555555551b5 <+12>: movl $0xa,-0x4(%rbp)
0x00005555555551bc <+19>: mov -0x4(%rbp),%eax
0x00005555555551bf <+22>: mov %eax,%esi
0x00005555555551c1 <+24>: lea 0xe40(%rip),%rdi # 0x555555556008
0x00005555555551c8 <+31>: mov $0x0,%eax
0x00005555555551cd <+36>: callq 0x555555555070 <printf@plt>
0x00005555555551d2 <+41>: movl $0x0,-0x8(%rbp)
0x00005555555551d9 <+48>: jmp 0x5555555551fc <main+83>
0x00005555555551db <+50>: mov -0x8(%rbp),%eax
0x00005555555551de <+53>: mov %eax,%edi
0x00005555555551e0 <+55>: callq 0x555555555169 <fibbonacci>
0x00005555555551e5 <+60>: mov %eax,%esi
0x00005555555551e7 <+62>: lea 0xe49(%rip),%rdi # 0x555555556037
0x00005555555551ee <+69>: mov $0x0,%eax
0x00005555555551f3 <+74>: callq 0x555555555070 <printf@plt>
0x00005555555551f8 <+79>: addl $0x1,-0x8(%rbp)
0x00005555555551fc <+83>: mov -0x8(%rbp),%eax
0x00005555555551ff <+86>: cmp -0x4(%rbp),%eax
0x0000555555555202 <+89>: jb 0x5555555551db <main+50>
0x0000555555555204 <+91>: mov $0xa,%edi
0x0000555555555209 <+96>: callq 0x555555555060 <putchar@plt>
0x000055555555520e <+101>: mov $0x0,%eax
0x0000555555555213 <+106>: leaveq
0x0000555555555214 <+107>: retq
End of assembler dump.
(gdb) b *0x000055555555520e
Breakpoint 3 at 0x55555555520e
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551a9 <main>
breakpoint already hit 1 time
2 breakpoint keep y 0x0000555555555169 <fibbonacci>
breakpoint already hit 2 times
3 breakpoint keep y 0x000055555555520e <main+101>
(gdb) d 2
(gdb) c
Continuing.
First 10 elements of the Fibbonacci sequence: 0 1 1 2 3 5 8 13 21 34
Breakpoint 3, 0x000055555555520e in main ()
1: x/10i $rip
=> 0x55555555520e <main+101>: mov $0x0,%eax
0x555555555213 <main+106>: leaveq
0x555555555214 <main+107>: retq
0x555555555215: nopw %cs:0x0(%rax,%rax,1)
0x55555555521f: nop
0x555555555220 <__libc_csu_init>: endbr64
0x555555555224 <__libc_csu_init+4>: push %r15
0x555555555226 <__libc_csu_init+6>:
lea 0x2b83(%rip),%r15 # 0x555555557db0
0x55555555522d <__libc_csu_init+13>: push %r14
0x55555555522f <__libc_csu_init+15>: mov %rdx,%r14
(gdb) disas
Dump of assembler code for function main:
0x00005555555551a9 <+0>: endbr64
0x00005555555551ad <+4>: push %rbp
0x00005555555551ae <+5>: mov %rsp,%rbp
0x00005555555551b1 <+8>: sub $0x10,%rsp
0x00005555555551b5 <+12>: movl $0xa,-0x4(%rbp)
0x00005555555551bc <+19>: mov -0x4(%rbp),%eax
0x00005555555551bf <+22>: mov %eax,%esi
0x00005555555551c1 <+24>: lea 0xe40(%rip),%rdi # 0x555555556008
0x00005555555551c8 <+31>: mov $0x0,%eax
0x00005555555551cd <+36>: callq 0x555555555070 <printf@plt>
0x00005555555551d2 <+41>: movl $0x0,-0x8(%rbp)
0x00005555555551d9 <+48>: jmp 0x5555555551fc <main+83>
0x00005555555551db <+50>: mov -0x8(%rbp),%eax
0x00005555555551de <+53>: mov %eax,%edi
0x00005555555551e0 <+55>: callq 0x555555555169 <fibbonacci>
0x00005555555551e5 <+60>: mov %eax,%esi
0x00005555555551e7 <+62>: lea 0xe49(%rip),%rdi # 0x555555556037
0x00005555555551ee <+69>: mov $0x0,%eax
0x00005555555551f3 <+74>: callq 0x555555555070 <printf@plt>
0x00005555555551f8 <+79>: addl $0x1,-0x8(%rbp)
0x00005555555551fc <+83>: mov -0x8(%rbp),%eax
0x00005555555551ff <+86>: cmp -0x4(%rbp),%eax
0x0000555555555202 <+89>: jb 0x5555555551db <main+50>
0x0000555555555204 <+91>: mov $0xa,%edi
0x0000555555555209 <+96>: callq 0x555555555060 <putchar@plt>
=> 0x000055555555520e <+101>: mov $0x0,%eax
0x0000555555555213 <+106>: leaveq
0x0000555555555214 <+107>: retq
End of assembler dump.
(gdb) quit
A debugging session is active.
Inferior 1 [process 2967] will be killed.
Quit anyway? (y or n) y
sec875@ubuntu:~$
Examining and Modifying Registers and Memory 检查和修改寄存器和内存
Examine Registers & Memory 检查寄存器和内存
Commands: x (examine), print, display
许多 GDB 命令支持格式说明符,我们将其称为 /FMT(因为在 gdb help 命令中每个命令都这样称呼它)
(gdb) help
格式说明符
/FMT 是 / 字符、后跟数字 n、格式说明符 f 和单位大小说明符 u 的组合。
n、f 和 u 都是可选的,如果未指定,则默认为上次使用的值或默认初始值。
/多少条n;输出格式f和单位大小byte还是word
我们之前看到的命令“display/10i
” 的n为10 和 f为‘i’,用于指令,不需要指定u,因为它对于指令来说没有意义。
Print value of expression EXP each time the program stops.
Usage: display[/FMT] EXP
/FMT may be used before EXP as in the "print" command.
/FMT "i" or "s" or including a size-letter is allowed,
as in the "x" command, and then EXP is used to get the address to examine
and examining is done as in the "x" command.
With no argument, display all currently requested auto-display expressions.
Use "undisplay" to cancel display requests previously made.
(gdb)
格式说明符 f 的最常见值是:
‘x’ 表示十六进制
‘d’ 表示有符号十进制
‘u’ 表示无符号十进制
‘c’ 表示 ASCII 字符
‘s’ 表示完整(假定以空字符结尾)ASCII 字符串。
此处给出了受支持的格式的完整列表。https://web.archive.org/web/20221004134402/http://sourceware.org/gdb/onlinedocs/gdb/Output-Formats.html
u 的单位大小为:
‘b’ 表示字节 bytes
‘h’ 表示半字 half-words(2 个字节)
‘w’ 表示字 words(4 个字节)
‘g’ 表示大字 giant-words(8 个字节)
Intel 注释:GDB 的字(4 个字节)术语与 Intel 的字(2 个字节)术语不一致!这是你必须记住并牢记的事情。
RISC-V 注意事项:GDB 的字(4 字节)术语与 RISC-V 的字术语一致。主要的术语差异在于 64 位(8 字节)值在 GDB 中称为“giant-words 巨字”,在 RISC-V 中称为“double-words 双字”。您只需记住这一点即可。
查看寄存器
“info registers”(简称:info r)命令将默认打印一些 GDB 开发人员认为最常用的寄存器集。
在 Intel 上,这些是general purpose registers 通用寄存器、eflags 寄存器和 segment registers 段寄存器(在 Arch2001 中了解)。
在 RISC-V 上,这些是通用寄存器(x0-x31,由其 ABI 名称显示)和程序计数器 (pc) 寄存器。
您还可以通过在“info r”后列出寄存器来指定您想要查看的特定寄存器集。https://web.archive.org/web/20221128034353/https://sourceware.org/gdb/onlinedocs/gdb/Registers.html
您还可以使用带有 /FMT 说明符的“print”命令(缩写形式:p),仅使用 f 格式说明符,来打印单个寄存器。
https://web.archive.org/web/20220726115258/https://sourceware.org/gdb/current/onlinedocs/gdb/Data.html#index-printing-data
print 命令要求寄存器名称以 $ 为前缀。而“info r”命令则不需要这样的前缀。
在 Intel 上,这可能会与 x86-64 AT&T 语法要求相混淆,即立即数以 $ 为前缀,而寄存器以 % 为前缀。您只需记住这一点即可。
Intel (x86-64) ‘info r’ example:
(gdb) info r rax rbx rsp
rax 0xface 64206
rbx 0x555555555220 93824992236064
rsp 0x7fffffffe038 0x7fffffffe038
Intel (x86-64) ‘print’ example:
(gdb) p/x $rax
$11 = 0xface
(gdb) p/u $rax
$12 = 64206
(gdb) p/d $rax
$13 = 64206
(gdb) p/d $ax
$14 = -1330
(gdb) p/gd $ax
Size letters are meaningless in "print" command.
(gdb) p/10d $ax
Item count other than 1 is meaningless in "print" command.
RISC-V (RV64I) ‘info r’ example:
(gdb) info r fp sp a4 a5
fp 0x7ffffffffaa8 0x7ffffffffaa8
sp 0x7ffffffff920 0x7ffffffff920
a4 0x7ffffffff940 140737488353600
a5 0x41 65
RISC-V (RV64I) ‘print’ example:
(gdb) p/x $a0
$2 = 0x41
(gdb) p/u $a0
$3 = 65
(gdb) p/d $a0
$4 = 65
(gdb) p/gx $fp
Size letters are meaningless in "print" command.
(gdb) p/10x $fp
Item count other than 1 is meaningless in "print" command.
Viewing (Examining) Memory 查看(检查)内存
“x”命令(用于检查内存)支持/FMT 说明符。
https://web.archive.org/web/20221230195737/https://sourceware.org/gdb/current/onlinedocs/gdb/Memory.html#index-examining-memory (其中包括对/FMT 的讨论)
Intel (x86-64) ‘x’ examine memory example:
(gdb) x/8xb $rsp
0x7fffffffe038: 0xb3 0xd0 0xde 0xf7 0xff 0x7f 0x00 0x00
(gdb) x/4xh $rsp
0x7fffffffe038: 0xd0b3 0xf7de 0x7fff 0x0000
(gdb) x/2xw $rsp
0x7fffffffe038: 0xf7ded0b3 0x00007fff
(gdb) x/1xg $rsp
0x7fffffffe038: 0x00007ffff7ded0b3
(gdb) x/s 0x555555556008
0x555555556008: "First %d elements of the Fibbonacci sequence: "
(gdb) x/3i $rip
=> 0x5555555551a9 <main>: endbr64
0x5555555551ad <main+4>: push %rbp
0x5555555551ae <main+5>: mov %rsp,%rbp
RISC-V (RV64I) ‘x’ examine memory example:
(gdb) x/8xb $sp
0x7ffffffff920: 0x50 0xe0 0xff 0xf7 0xff 0x7f 0x00 0x00
(gdb) x/4xh $sp
0x7ffffffff920: 0xe050 0xf7ff 0x7fff 0x0000
(gdb) x/2xw $sp
0x7ffffffff920: 0xf7ffe050 0x00007fff
(gdb) x/1xg $sp
0x7ffffffff920: 0x00007ffff7ffe050
(gdb) x/s 0x555555555790
0x555555555790: "First %d elements of the Fibbonacci sequence: "
(gdb) x/4i $pc
=> 0x555555555712 <main>: addi sp,sp,-32
0x555555555714 <main+2>: sd ra,24(sp)
0x555555555716 <main+4>: sd s0,16(sp)
0x555555555718 <main+6>: addi s0,sp,32
演练
sec875@ubuntu:~$ gdb ./fibber_bin -q
Reading symbols from ./fibber_bin...
(gdb) b *main
Breakpoint 1 at 0x11a9: file fibber.c, line 13.
(gdb) r
Starting program: /home/sec875/fibber_bin
Breakpoint 1, main () at fibber.c:13
13 int main(){
(gdb) info registers
rax 0x5555555551a9 93824992235945
rbx 0x555555555220 93824992236064
rcx 0x555555555220 93824992236064
rdx 0x7fffffffe128 140737488347432
rsi 0x7fffffffe118 140737488347416
rdi 0x1 1
rbp 0x0 0x0
rsp 0x7fffffffe028 0x7fffffffe028
r8 0x0 0
r9 0x7ffff7fe0d60 140737354009952
r10 0x0 0
r11 0x7ffff7f738f0 140737353562352
r12 0x555555555080 93824992235648
r13 0x7fffffffe110 140737488347408
r14 0x0 0
r15 0x0 0
rip 0x5555555551a9 0x5555555551a9 <main>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) clear
(gdb) info r
Deleted breakpoint 1 rax 0x5555555551a9 93824992235945
rbx 0x555555555220 93824992236064
rcx 0x555555555220 93824992236064
rdx 0x7fffffffe128 140737488347432
rsi 0x7fffffffe118 140737488347416
rdi 0x1 1
rbp 0x0 0x0
rsp 0x7fffffffe028 0x7fffffffe028
r8 0x0 0
r9 0x7ffff7fe0d60 140737354009952
r10 0x0 0
r11 0x7ffff7f738f0 140737353562352
r12 0x555555555080 93824992235648
r13 0x7fffffffe110 140737488347408
r14 0x0 0
r15 0x0 0
rip 0x5555555551a9 0x5555555551a9 <main>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) info r rip eflags r8
rip 0x5555555551a9 0x5555555551a9 <main>
eflags 0x246 [ PF ZF IF ]
r8 0x0 0
(gdb) p/xg $rbx $rdi
Size letters are meaningless in "print" command.
(gdb) p/x $rbx $rdi
A syntax error in expression, near `$rdi'.
(gdb) p/x $rbx
$1 = 0x555555555220
(gdb) p/x $r8
$2 = 0x0
(gdb) x/16xb 0x5555555551a9
0x5555555551a9 <main>: 0xf3 0x0f 0x1e 0xfa 0x55 0x48 0x89 0xe5
0x5555555551b1 <main+8>: 0x48 0x83 0xec 0x10 0xc7 0x45 0xfc 0x0a
(gdb) x/8xh 0x5555555551a9
0x5555555551a9 <main>: 0x0ff3 0xfa1e 0x4855 0xe589 0x8348 0x10ec 0x45c7 0x0afc
(gdb) x/4xw 0x5555555551a9
0x5555555551a9 <main>: 0xfa1e0ff3 0xe5894855 0x10ec8348 0x0afc45c7
(gdb) x/2xg 0x5555555551a9
0x5555555551a9 <main>: 0xe5894855fa1e0ff3 0x0afc45c710ec8348
(gdb) quit
A debugging session is active.
Inferior 1 [process 3251] will be killed.
Quit anyway? (y or n) y
sec875@ubuntu:~$
Modify Registers & Memory 修改寄存器和内存
Commands: set
修改寄存器
“set”命令与等号结合使用时可用于设置寄存器。
Intel(x86-64)‘设置’寄存器值示例:
(gdb) set $rax = 0xdeadbeeff00dface
(gdb) p/x $rax
$15 = 0xdeadbeeff00dface
(gdb) set $ax = 0xcafef00d
(gdb) p/x $rax
$16 = 0xdeadbeeff00df00d
请注意,写入的大小是寄存器的大小(16 位,ax)而不是立即数的大小(32 位)。
RISC-V (RV64I) ‘set’ register value example:
(gdb) p/x $a0
$4 = 0x41
(gdb) set $a0 = 0xdeadbeeff00dface
(gdb) p/x $a0
$5 = 0xdeadbeeff00dface
修改内存
与寄存器类似,可以使用“set”命令修改内存,也可以选择指定 C 样式类型以指定要写入的长度。
小于指定内存写入大小的立即数将进行零扩展,而不是进行符号扩展。
Intel (x86-64) ‘set’ memory example:
(gdb) x/1xg $rsp
0x7fffffffe038: 0x00007ffff7ded0b3
(gdb) set {char}$rsp = 0xFF
(gdb) x/1xg $rsp
0x7fffffffe038: 0x00007ffff7ded0ff
(gdb) set {short}$rsp = 0xFF
(gdb) x/1xg $rsp
0x7fffffffe038: 0x00007ffff7de00ff
(gdb) set {short}$rsp = 0xFFFF
(gdb) x/1xg $rsp
0x7fffffffe038: 0x00007ffff7deffff
(gdb) set {long long}$rsp = 0x1337bee7
(gdb) x/1xg $rsp
0x7fffffffe038: 0x000000001337bee7
请注意,对于“set {short}$rsp = 0xFF”,它没有将 1 字节值符号扩展为 2 字节短值 0xFFFF。它只是将 0xFF 零扩展为 0x00FF,这就是底部 2 个字节变成 0x00FF 的原因。
RISC-V (RV64I) ‘set’ memory example:
(gdb) x/1xg $sp
0x7ffffffff920: 0x00007ffff7ffe050
(gdb) set {char}$sp = 0xFF
(gdb) x/1xg $sp
0x7ffffffff920: 0x00007ffff7ffe0ff
(gdb) set {short}$sp = 0xFF
(gdb) x/1xg $sp
0x7ffffffff920: 0x00007ffff7ff00ff
(gdb) set {short}$sp = 0xFFFF
(gdb) x/1xg $sp
0x7ffffffff920: 0x00007ffff7ffffff
(gdb) set {long long}$sp = 0x1337bee7
(gdb) x/1xg $sp
0x7ffffffff920: 0x000000001337bee7
请注意,对于“set {short}$sp = 0xFF”,它没有将 1 字节值符号扩展为 2 字节短值 0xFFFF。它只是将 0xFF 零扩展为 0x00FF,这就是底部 2 个字节变成 0x00FF 的原因。
演练:
sec875@ubuntu:~$ gdb ./fibber_bin -q
Reading symbols from ./fibber_bin...
(gdb) b *main
Breakpoint 1 at 0x11a9: file fibber.c, line 13.
(gdb) r
Starting program: /home/sec875/fibber_bin
Breakpoint 1, main () at fibber.c:13
13 int main(){
(gdb) info r rdi
rdi 0x1 1
(gdb) set $rdi = 0xdeabdeef
(gdb) p/x $rdi
$1 = 0xdeabdeef
(gdb) x/16xb $rsp
0x7fffffffe028: 0x83 0x80 0xde 0xf7 0xff 0x7f 0x00 0x00
0x7fffffffe030: 0x20 0xc6 0xff 0xf7 0xff 0x7f 0x00 0x00
(gdb) x/2xg $rsp
0x7fffffffe028: 0x00007ffff7de8083 0x00007ffff7ffc620
(gdb) set {char}$rsp=0x11223344
(gdb) x/2xg $rsp
0x7fffffffe028: 0x00007ffff7de8044 0x00007ffff7ffc620
(gdb) set {int}$rsp=0x11223344
(gdb) x/2xg $rsp
0x7fffffffe028: 0x00007fff11223344 0x00007ffff7ffc620
(gdb) quit
A debugging session is active.
Inferior 1 [process 3382] will be killed.
Quit anyway? (y or n) y
sec875@ubuntu:~$
Setting Up an Updating Stack View 设置更新栈视图
更新 Stack View
只需使用前面讨论过的显示命令即可完成此操作,使用 /FMT 格式说明符指示要从堆栈指针寄存器中的地址开始显示多少个十六进制字(32 位)或巨字(64 位)。通常,您应该将大小与指针大小相匹配。因此,32 位架构上显示字,64 位上显示巨字。例如
display/10xg $rsp
on x86 64-bit.
display/10xw $esp
on x86 32-bit.
display/10xg $sp
on ARM or RISC-V 64-bit (RV64I).
display/10xw $sp
on ARM or RISC-V 32-bit (RV32I/RC32E).
演练
sec875@ubuntu:~$ gdb ./fibber_bin1 -q
Reading symbols from ./fibber_bin1...
(No debugging symbols found in ./fibber_bin1)
(gdb) b main
Breakpoint 1 at 0x11a9
(gdb) b fibbonacci
Breakpoint 2 at 0x1169
(gdb) r
Starting program: /home/sec875/fibber_bin1
Breakpoint 1, 0x00005555555551a9 in main ()
(gdb) display/10i $rip
1: x/10i $rip
=> 0x5555555551a9 <main>: endbr64
0x5555555551ad <main+4>: push %rbp
0x5555555551ae <main+5>: mov %rsp,%rbp
0x5555555551b1 <main+8>: sub $0x10,%rsp
0x5555555551b5 <main+12>: movl $0xa,-0x4(%rbp)
0x5555555551bc <main+19>: mov -0x4(%rbp),%eax
0x5555555551bf <main+22>: mov %eax,%esi
0x5555555551c1 <main+24>: lea 0xe40(%rip),%rdi # 0x555555556008
0x5555555551c8 <main+31>: mov $0x0,%eax
0x5555555551cd <main+36>: callq 0x555555555070 <printf@plt>
(gdb) display/10xg $rsp
2: x/10xg $rsp
0x7fffffffe018: 0x00007ffff7de8083 0x00007ffff7ffc620
0x7fffffffe028: 0x00007fffffffe108 0x0000000100000000
0x7fffffffe038: 0x00005555555551a9 0x0000555555555220
0x7fffffffe048: 0xcee116a85070b21d 0x0000555555555080
0x7fffffffe058: 0x00007fffffffe100 0x0000000000000000
(gdb) c
Continuing.
Breakpoint 2, 0x0000555555555169 in fibbonacci ()
1: x/10i $rip
=> 0x555555555169 <fibbonacci>: endbr64
0x55555555516d <fibbonacci+4>: push %rbp
0x55555555516e <fibbonacci+5>: mov %rsp,%rbp
0x555555555171 <fibbonacci+8>: push %rbx
0x555555555172 <fibbonacci+9>: sub $0x18,%rsp
0x555555555176 <fibbonacci+13>: mov %edi,-0x14(%rbp)
0x555555555179 <fibbonacci+16>: cmpl $0x1,-0x14(%rbp)
0x55555555517d <fibbonacci+20>: ja 0x555555555184 <fibbonacci+27>
0x55555555517f <fibbonacci+22>: mov -0x14(%rbp),%eax
0x555555555182 <fibbonacci+25>: jmp 0x5555555551a2 <fibbonacci+57>
2: x/10xg $rsp
0x7fffffffdff8: 0x00005555555551e5 0x00007fffffffe100
0x7fffffffe008: 0x0000000a00000000 0x0000000000000000
0x7fffffffe018: 0x00007ffff7de8083 0x00007ffff7ffc620
0x7fffffffe028: 0x00007fffffffe108 0x0000000100000000
0x7fffffffe038: 0x00005555555551a9 0x0000555555555220
(gdb) c
Continuing.
Breakpoint 2, 0x0000555555555169 in fibbonacci ()
1: x/10i $rip
=> 0x555555555169 <fibbonacci>: endbr64
0x55555555516d <fibbonacci+4>: push %rbp
0x55555555516e <fibbonacci+5>: mov %rsp,%rbp
0x555555555171 <fibbonacci+8>: push %rbx
0x555555555172 <fibbonacci+9>: sub $0x18,%rsp
0x555555555176 <fibbonacci+13>: mov %edi,-0x14(%rbp)
0x555555555179 <fibbonacci+16>: cmpl $0x1,-0x14(%rbp)
0x55555555517d <fibbonacci+20>: ja 0x555555555184 <fibbonacci+27>
0x55555555517f <fibbonacci+22>: mov -0x14(%rbp),%eax
0x555555555182 <fibbonacci+25>: jmp 0x5555555551a2 <fibbonacci+57>
2: x/10xg $rsp
0x7fffffffdf68: 0x0000555555555191 0x0000000000000000
0x7fffffffdf78: 0x0000000400000000 0x0000000000000000
0x7fffffffdf88: 0x0000555555555220 0x00007fffffffdfc0
0x7fffffffdf98: 0x0000555555555191 0x0000000000000000
0x7fffffffdfa8: 0x0000000500000000 0x0000555555554040
(gdb) quit
A debugging session is active.
Inferior 1 [process 3459] will be killed.
Quit anyway? (y or n) y
sec875@ubuntu:~$
Stack Backtrace 栈回溯
堆栈回溯
backtrace(简称:bt)提供调用堆栈回溯。最近调用的函数位于回溯的顶部,而最近最少调用的函数位于底部。或者换句话说,顶部的函数是由其下方的函数调用的。显示的地址是函数完成后将返回的位置。
例如,当第一次在 fibbonacci() 的入口点中断时,bt 可能会产生类似以下输出的内容:
(gdb) bt
#0 fibbonacci (n=0) at ./fibber.c:4
#1 0x0000555555555742 in main () at ./fibber.c:18
因为 fibbonacci() 是一个递归函数,所以如果继续执行,可能会看到如下输出:
(gdb) bt
#0 fibbonacci (n=3) at ./fibber.c:4
#1 0x00005555555556ea in fibbonacci (n=4) at ./fibber.c:8
#2 0x00005555555556ea in fibbonacci (n=5) at ./fibber.c:8
#3 0x0000555555555742 in main () at ./fibber.c:18
Stepping Through Assembly 逐步执行汇编
Step Into vs. Step Over vs. Step Out: 步入 vs. 跨过 vs. 退出:
Step Over vs. Step Into vs. Step Out
与其他调试器一样,“step over”的概念表示 UI 应该跳过调用指令 - 执行其中的所有指令,但直到到达调用指令之后的指令时,调试器 UI 才显示为已停止。另一方面,“Step into”将进入调用指令。“Step out”将一直步进直到到达函数退出(调试器在执行“return”类型指令时会启发式地确定函数已完成。)
注意:与其他 gdb 指令一样,如果您在空行中输入 return,gdb 将重新执行上一个命令。此行为在步进上下文中特别有用,因为您无需在每次想要步进时重新输入步进指令,而是只需继续按回车键即可。
Step Over 跨过
nexti(简称:ni)跳过下一条指令,继续执行下一条指令,尤其是当它是一条可能返回相同位置的控制流指令时(如“调用”类型指令)。
https://web.archive.org/web/20221211033727/https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#Continuing-and-Stepping
Step Into 步入
stepi(缩写:si)进入下一条指令,从该指令所指向的位置继续执行,特别是如果它是一条控制流指令(例如“调用”或“跳转”类型的指令)。
https://web.archive.org/web/20221211033727/https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#Continuing-and-Stepping
Step Out 退出
finish(简称:fin)退出当前函数上下文,完成其余代码的执行,直到函数结束。
https://web.archive.org/web/20221211033727/https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#Continuing-and-Stepping
演练
sec875@ubuntu:~$ gdb ./fibber_bin1 -q
Reading symbols from ./fibber_bin1...
(No debugging symbols found in ./fibber_bin1)
(gdb) b main
Breakpoint 1 at 0x11a9
(gdb) display/10i $rip
1: x/10i $rip
<error: No registers.>
(gdb) r
Starting program: /home/sec875/fibber_bin1
Breakpoint 1, 0x00005555555551a9 in main ()
1: x/10i $rip
=> 0x5555555551a9 <main>: endbr64
0x5555555551ad <main+4>: push %rbp
0x5555555551ae <main+5>: mov %rsp,%rbp
0x5555555551b1 <main+8>: sub $0x10,%rsp
0x5555555551b5 <main+12>: movl $0xa,-0x4(%rbp)
0x5555555551bc <main+19>: mov -0x4(%rbp),%eax
0x5555555551bf <main+22>: mov %eax,%esi
0x5555555551c1 <main+24>: lea 0xe40(%rip),%rdi # 0x555555556008
0x5555555551c8 <main+31>: mov $0x0,%eax
0x5555555551cd <main+36>: callq 0x555555555070 <printf@plt>
(gdb) ni
0x00005555555551ad in main ()
1: x/10i $rip
=> 0x5555555551ad <main+4>: push %rbp
0x5555555551ae <main+5>: mov %rsp,%rbp
0x5555555551b1 <main+8>: sub $0x10,%rsp
0x5555555551b5 <main+12>: movl $0xa,-0x4(%rbp)
0x5555555551bc <main+19>: mov -0x4(%rbp),%eax
0x5555555551bf <main+22>: mov %eax,%esi
0x5555555551c1 <main+24>: lea 0xe40(%rip),%rdi # 0x555555556008
0x5555555551c8 <main+31>: mov $0x0,%eax
0x5555555551cd <main+36>: callq 0x555555555070 <printf@plt>
0x5555555551d2 <main+41>: movl $0x0,-0x8(%rbp)
...
(gdb)
0x00005555555551fc in main ()
1: x/10i $rip
=> 0x5555555551fc <main+83>: mov -0x8(%rbp),%eax
0x5555555551ff <main+86>: cmp -0x4(%rbp),%eax
0x555555555202 <main+89>: jb 0x5555555551db <main+50>
0x555555555204 <main+91>: mov $0xa,%edi
0x555555555209 <main+96>: callq 0x555555555060 <putchar@plt>
0x55555555520e <main+101>: mov $0x0,%eax
0x555555555213 <main+106>: leaveq
0x555555555214 <main+107>: retq
0x555555555215: nopw %cs:0x0(%rax,%rax,1)
0x55555555521f: nop
(gdb) si
0x00005555555551ff in main ()
1: x/10i $rip
=> 0x5555555551ff <main+86>: cmp -0x4(%rbp),%eax
0x555555555202 <main+89>: jb 0x5555555551db <main+50>
0x555555555204 <main+91>: mov $0xa,%edi
0x555555555209 <main+96>: callq 0x555555555060 <putchar@plt>
0x55555555520e <main+101>: mov $0x0,%eax
0x555555555213 <main+106>: leaveq
0x555555555214 <main+107>: retq
0x555555555215: nopw %cs:0x0(%rax,%rax,1)
0x55555555521f: nop
0x555555555220 <__libc_csu_init>: endbr64
(gdb)
0x0000555555555202 in main ()
1: x/10i $rip
=> 0x555555555202 <main+89>: jb 0x5555555551db <main+50>
0x555555555204 <main+91>: mov $0xa,%edi
0x555555555209 <main+96>: callq 0x555555555060 <putchar@plt>
0x55555555520e <main+101>: mov $0x0,%eax
0x555555555213 <main+106>: leaveq
0x555555555214 <main+107>: retq
0x555555555215: nopw %cs:0x0(%rax,%rax,1)
0x55555555521f: nop
0x555555555220 <__libc_csu_init>: endbr64
0x555555555224 <__libc_csu_init+4>: push %r15
(gdb)
0x00005555555551e0 in main ()
1: x/10i $rip
=> 0x5555555551e0 <main+55>: callq 0x555555555169 <fibbonacci>
0x5555555551e5 <main+60>: mov %eax,%esi
0x5555555551e7 <main+62>: lea 0xe49(%rip),%rdi # 0x555555556037
0x5555555551ee <main+69>: mov $0x0,%eax
0x5555555551f3 <main+74>: callq 0x555555555070 <printf@plt>
0x5555555551f8 <main+79>: addl $0x1,-0x8(%rbp)
0x5555555551fc <main+83>: mov -0x8(%rbp),%eax
0x5555555551ff <main+86>: cmp -0x4(%rbp),%eax
0x555555555202 <main+89>: jb 0x5555555551db <main+50>
0x555555555204 <main+91>: mov $0xa,%edi
(gdb) si
0x0000555555555169 in fibbonacci ()
1: x/10i $rip
=> 0x555555555169 <fibbonacci>: endbr64
0x55555555516d <fibbonacci+4>: push %rbp
0x55555555516e <fibbonacci+5>: mov %rsp,%rbp
0x555555555171 <fibbonacci+8>: push %rbx
0x555555555172 <fibbonacci+9>: sub $0x18,%rsp
0x555555555176 <fibbonacci+13>: mov %edi,-0x14(%rbp)
0x555555555179 <fibbonacci+16>: cmpl $0x1,-0x14(%rbp)
0x55555555517d <fibbonacci+20>: ja 0x555555555184 <fibbonacci+27>
0x55555555517f <fibbonacci+22>: mov -0x14(%rbp),%eax
0x555555555182 <fibbonacci+25>: jmp 0x5555555551a2 <fibbonacci+57>
(gdb)
0x000055555555516e in fibbonacci ()
1: x/10i $rip
=> 0x55555555516e <fibbonacci+5>: mov %rsp,%rbp
0x555555555171 <fibbonacci+8>: push %rbx
0x555555555172 <fibbonacci+9>: sub $0x18,%rsp
0x555555555176 <fibbonacci+13>: mov %edi,-0x14(%rbp)
0x555555555179 <fibbonacci+16>: cmpl $0x1,-0x14(%rbp)
0x55555555517d <fibbonacci+20>: ja 0x555555555184 <fibbonacci+27>
0x55555555517f <fibbonacci+22>: mov -0x14(%rbp),%eax
0x555555555182 <fibbonacci+25>: jmp 0x5555555551a2 <fibbonacci+57>
0x555555555184 <fibbonacci+27>: mov -0x14(%rbp),%eax
0x555555555187 <fibbonacci+30>: sub $0x1,%eax
(gdb)
0x0000555555555172 in fibbonacci ()
1: x/10i $rip
=> 0x555555555172 <fibbonacci+9>: sub $0x18,%rsp
0x555555555176 <fibbonacci+13>: mov %edi,-0x14(%rbp)
0x555555555179 <fibbonacci+16>: cmpl $0x1,-0x14(%rbp)
0x55555555517d <fibbonacci+20>: ja 0x555555555184 <fibbonacci+27>
0x55555555517f <fibbonacci+22>: mov -0x14(%rbp),%eax
0x555555555182 <fibbonacci+25>: jmp 0x5555555551a2 <fibbonacci+57>
0x555555555184 <fibbonacci+27>: mov -0x14(%rbp),%eax
0x555555555187 <fibbonacci+30>: sub $0x1,%eax
0x55555555518a <fibbonacci+33>: mov %eax,%edi
0x55555555518c <fibbonacci+35>: callq 0x555555555169 <fibbonacci>
(gdb) fin
Run till exit from #0 0x0000555555555172 in fibbonacci ()
0x00005555555551e5 in main ()
1: x/10i $rip
=> 0x5555555551e5 <main+60>: mov %eax,%esi
0x5555555551e7 <main+62>: lea 0xe49(%rip),%rdi # 0x555555556037
0x5555555551ee <main+69>: mov $0x0,%eax
0x5555555551f3 <main+74>: callq 0x555555555070 <printf@plt>
0x5555555551f8 <main+79>: addl $0x1,-0x8(%rbp)
0x5555555551fc <main+83>: mov -0x8(%rbp),%eax
0x5555555551ff <main+86>: cmp -0x4(%rbp),%eax
0x555555555202 <main+89>: jb 0x5555555551db <main+50>
0x555555555204 <main+91>: mov $0xa,%edi
0x555555555209 <main+96>: callq 0x555555555060 <putchar@plt>
(gdb) ni
0x00005555555551e7 in main ()
1: x/10i $rip
=> 0x5555555551e7 <main+62>: lea 0xe49(%rip),%rdi # 0x555555556037
0x5555555551ee <main+69>: mov $0x0,%eax
0x5555555551f3 <main+74>: callq 0x555555555070 <printf@plt>
0x5555555551f8 <main+79>: addl $0x1,-0x8(%rbp)
0x5555555551fc <main+83>: mov -0x8(%rbp),%eax
0x5555555551ff <main+86>: cmp -0x4(%rbp),%eax
0x555555555202 <main+89>: jb 0x5555555551db <main+50>
0x555555555204 <main+91>: mov $0xa,%edi
0x555555555209 <main+96>: callq 0x555555555060 <putchar@plt>
0x55555555520e <main+101>: mov $0x0,%eax
(gdb)
0x00005555555551ee in main ()
1: x/10i $rip
=> 0x5555555551ee <main+69>: mov $0x0,%eax
0x5555555551f3 <main+74>: callq 0x555555555070 <printf@plt>
0x5555555551f8 <main+79>: addl $0x1,-0x8(%rbp)
0x5555555551fc <main+83>: mov -0x8(%rbp),%eax
0x5555555551ff <main+86>: cmp -0x4(%rbp),%eax
0x555555555202 <main+89>: jb 0x5555555551db <main+50>
0x555555555204 <main+91>: mov $0xa,%edi
0x555555555209 <main+96>: callq 0x555555555060 <putchar@plt>
0x55555555520e <main+101>: mov $0x0,%eax
0x555555555213 <main+106>: leaveq
(gdb) quit
A debugging session is active.
Inferior 1 [process 3811] will be killed.
Quit anyway? (y or n) y
sec875@ubuntu:~$
Run Until Address 运行至地址
临时代码断点,又称运行至地址
运行至地址(又称临时断点)
如果您只想向前执行一些指令,但不想设置然后删除断点,或者不想计算指令数量,则可以使用“until <address>
”(缩写:u)命令。
但请注意,如果由于某种原因始终无法到达地址(例如因为控制流分支到其他方向),则在退出当前函数时,until 命令也会中断。
演练
sec875@ubuntu:~$ gdb ./fibber_bin1 -q
Reading symbols from ./fibber_bin1...
(No debugging symbols found in ./fibber_bin1)
(gdb) b main
Breakpoint 1 at 0x11a9
(gdb) r
Starting program: /home/sec875/fibber_bin1
Breakpoint 1, 0x00005555555551a9 in main ()
(gdb) disas
Dump of assembler code for function main:
=> 0x00005555555551a9 <+0>: endbr64
0x00005555555551ad <+4>: push %rbp
0x00005555555551ae <+5>: mov %rsp,%rbp
0x00005555555551b1 <+8>: sub $0x10,%rsp
0x00005555555551b5 <+12>: movl $0xa,-0x4(%rbp)
0x00005555555551bc <+19>: mov -0x4(%rbp),%eax
0x00005555555551bf <+22>: mov %eax,%esi
0x00005555555551c1 <+24>: lea 0xe40(%rip),%rdi # 0x555555556008
0x00005555555551c8 <+31>: mov $0x0,%eax
0x00005555555551cd <+36>: callq 0x555555555070 <printf@plt>
0x00005555555551d2 <+41>: movl $0x0,-0x8(%rbp)
0x00005555555551d9 <+48>: jmp 0x5555555551fc <main+83>
0x00005555555551db <+50>: mov -0x8(%rbp),%eax
0x00005555555551de <+53>: mov %eax,%edi
0x00005555555551e0 <+55>: callq 0x555555555169 <fibbonacci>
0x00005555555551e5 <+60>: mov %eax,%esi
0x00005555555551e7 <+62>: lea 0xe49(%rip),%rdi # 0x555555556037
0x00005555555551ee <+69>: mov $0x0,%eax
0x00005555555551f3 <+74>: callq 0x555555555070 <printf@plt>
0x00005555555551f8 <+79>: addl $0x1,-0x8(%rbp)
0x00005555555551fc <+83>: mov -0x8(%rbp),%eax
0x00005555555551ff <+86>: cmp -0x4(%rbp),%eax
0x0000555555555202 <+89>: jb 0x5555555551db <main+50>
0x0000555555555204 <+91>: mov $0xa,%edi
0x0000555555555209 <+96>: callq 0x555555555060 <putchar@plt>
0x000055555555520e <+101>: mov $0x0,%eax
0x0000555555555213 <+106>: leaveq
0x0000555555555214 <+107>: retq
End of assembler dump.
(gdb) running until after fibbonacci returns...
Undefined command: "running". Try "help".
(gdb) u *0x00005555555551e5
0x00005555555551e5 in main ()
(gdb) disas
Dump of assembler code for function main:
0x00005555555551a9 <+0>: endbr64
0x00005555555551ad <+4>: push %rbp
0x00005555555551ae <+5>: mov %rsp,%rbp
0x00005555555551b1 <+8>: sub $0x10,%rsp
0x00005555555551b5 <+12>: movl $0xa,-0x4(%rbp)
0x00005555555551bc <+19>: mov -0x4(%rbp),%eax
0x00005555555551bf <+22>: mov %eax,%esi
0x00005555555551c1 <+24>: lea 0xe40(%rip),%rdi # 0x555555556008
0x00005555555551c8 <+31>: mov $0x0,%eax
0x00005555555551cd <+36>: callq 0x555555555070 <printf@plt>
0x00005555555551d2 <+41>: movl $0x0,-0x8(%rbp)
0x00005555555551d9 <+48>: jmp 0x5555555551fc <main+83>
0x00005555555551db <+50>: mov -0x8(%rbp),%eax
0x00005555555551de <+53>: mov %eax,%edi
0x00005555555551e0 <+55>: callq 0x555555555169 <fibbonacci>
=> 0x00005555555551e5 <+60>: mov %eax,%esi
0x00005555555551e7 <+62>: lea 0xe49(%rip),%rdi # 0x555555556037
0x00005555555551ee <+69>: mov $0x0,%eax
0x00005555555551f3 <+74>: callq 0x555555555070 <printf@plt>
0x00005555555551f8 <+79>: addl $0x1,-0x8(%rbp)
0x00005555555551fc <+83>: mov -0x8(%rbp),%eax
0x00005555555551ff <+86>: cmp -0x4(%rbp),%eax
0x0000555555555202 <+89>: jb 0x5555555551db <main+50>
0x0000555555555204 <+91>: mov $0xa,%edi
0x0000555555555209 <+96>: callq 0x555555555060 <putchar@plt>
0x000055555555520e <+101>: mov $0x0,%eax
0x0000555555555213 <+106>: leaveq
0x0000555555555214 <+107>: retq
End of assembler dump.
(gdb) now running until after the next printf
Undefined command: "now". Try "help".
(gdb) until *0x00005555555551f8
0x00005555555551f8 in main ()
(gdb) disas
Dump of assembler code for function main:
0x00005555555551a9 <+0>: endbr64
0x00005555555551ad <+4>: push %rbp
0x00005555555551ae <+5>: mov %rsp,%rbp
0x00005555555551b1 <+8>: sub $0x10,%rsp
0x00005555555551b5 <+12>: movl $0xa,-0x4(%rbp)
0x00005555555551bc <+19>: mov -0x4(%rbp),%eax
0x00005555555551bf <+22>: mov %eax,%esi
0x00005555555551c1 <+24>: lea 0xe40(%rip),%rdi # 0x555555556008
0x00005555555551c8 <+31>: mov $0x0,%eax
0x00005555555551cd <+36>: callq 0x555555555070 <printf@plt>
0x00005555555551d2 <+41>: movl $0x0,-0x8(%rbp)
0x00005555555551d9 <+48>: jmp 0x5555555551fc <main+83>
0x00005555555551db <+50>: mov -0x8(%rbp),%eax
0x00005555555551de <+53>: mov %eax,%edi
0x00005555555551e0 <+55>: callq 0x555555555169 <fibbonacci>
0x00005555555551e5 <+60>: mov %eax,%esi
0x00005555555551e7 <+62>: lea 0xe49(%rip),%rdi # 0x555555556037
0x00005555555551ee <+69>: mov $0x0,%eax
0x00005555555551f3 <+74>: callq 0x555555555070 <printf@plt>
=> 0x00005555555551f8 <+79>: addl $0x1,-0x8(%rbp)
0x00005555555551fc <+83>: mov -0x8(%rbp),%eax
0x00005555555551ff <+86>: cmp -0x4(%rbp),%eax
0x0000555555555202 <+89>: jb 0x5555555551db <main+50>
0x0000555555555204 <+91>: mov $0xa,%edi
0x0000555555555209 <+96>: callq 0x555555555060 <putchar@plt>
0x000055555555520e <+101>: mov $0x0,%eax
0x0000555555555213 <+106>: leaveq
0x0000555555555214 <+107>: retq
End of assembler dump.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551a9 <main>
breakpoint already hit 1 time
(gdb) Quit
(gdb) q
A debugging session is active.
Inferior 1 [process 3875] will be killed.
Quit anyway? (y or n) y
sec875@ubuntu:~$
GDB Misc GDB 杂项
Saving a Persistent GDB Configuration 保存持久 GDB 配置
GDB 命令文件
通常,您会在 gdb 中多次启动和停止相同或不同的可执行文件。您可能希望每次启动 gdb 时都运行相同的命令,以设置一组通用寄存器、内存或指令的视图。或者您可能希望设置一些常用断点。这可以通过创建一个纯文本文件(例如 ~/x.cfg)并输入您希望在启动时在 gdb 中执行的一组 gdb 命令(每行一个)来实现。然后,当您启动 gdb 时,使用 -x 选项指定命令文件(例如 -x ~/x.cfg
)。
Intel(x86-64)推荐启动命令
将这些复制并粘贴到您自己的 ~/x.cfg 命令文本文件中:
display/10i $rip
display/x $rbp
display/x $rsp
display/x $rax
display/x $rbx
display/x $rcx
display/x $rdx
display/x $rdi
display/x $rsi
display/x $r8
display/x $r9
display/x $r12
display/x $r13
display/x $r14
display/10gx $rsp
start
RISC-V (RV32I/RV64I) recommended starting commands
将这些复制并粘贴到您自己的 ~/x.cfg 命令文本文件中:
display/x $ra
display/x $a0
display/x $a1
display/x $a4
display/x $a5
display/x $s0
display/10gx $sp
display/10i $pc
start
Changing Assembly Syntax 更改汇编语法
更改反汇编语法(RISC-V)
您可以禁用 ABI 寄存器名称的使用,并恢复为仅使用 x0-x31 寄存器名称,方法是:
set disassembler-options numeric
您可以使用以下命令在汇编输出中禁用伪指令(别名):
set disassembler-options no-aliases
注意:我认为目前存在一个错误,因为“set disassembler-options”应该清除这些选项,但并没有取消设置这些选项(“unset disassembler-options”命令也不起作用)。撤消这些选项的唯一方法是退出并重新启动 gdb。(但如果你找到了另一种方法,LMK!)
Mini-Course Complete! 迷你课程完成
Mini-Course Complete! 迷你课程完成
干得好!现在您可以在依赖这些技能的各种 OST 课程中使用这些技能!(例如 Arch1001:x86-64 汇编,或 Arch1005:RISC-V 汇编)。
这里有一些 GDB command cheatsheets GDB 命令备忘单,供以后参考:1、2。
https://darkdust.net/files/GDB Cheat Sheet.pdf
https://users.ece.utexas.edu/~adnan/gdb-refcard.pdf