发布于

PWN 基础

作者

复习(重修)汇编语言。发现边学边记笔记,当成一个项目来做的话有助于监督学习、提升学习效率。

李忠《x86 汇编语言:从实模式到保护模式》(第二版)检测点及章节习题 + pwndbg 速查手册 GitHub 地址:assembly.rip。欢迎提交 PR 指出错误~

目前仓库包含的内容有:

  • 检测点答案
  • 章节习题答案
  • 个人整理的 pwndbg 常用指令速查手册

其实本来想效仿 cheats.rs 做一个 x86-asm 的 Cheat Sheet 的,域名就买 assembly.rip。但是经过深思后还是暂时放弃了这个想法,不过或许以后有空可能会重启也说不准。


下面为我的 Foundation Knowledge Speed Run 的笔记。

NOTE

有关汇编语言的笔记主要是对于 x86 Assembly Crash Course 的总结,其中也包含了一些视频中没有的内容。PS: 建议结合视频阅读

一些你必须知道的

首先,什么是汇编代码?汇编代码是处理器在计算机上实际运行的代码。例如,以一些 C 代码为例:

#include <stdio.h>

void main(void)
{
    puts("Hello World!");
}

但实际上计算机不会运行你写的这份代码,而是该代码的汇编代码(C 语言是编译型语言)。如下所示:

0000000000001135 <main>:
    1135:       55                      push   rbp
    1136:       48 89 e5                mov    rbp,rsp
    1139:       48 8d 3d c4 0e 00 00    lea    rdi,[rip+0xec4]        # 2004 <_IO_stdin_used+0x4>
    1140:       e8 eb fe ff ff          call   1030 <puts@plt>
    1145:       90                      nop
    1146:       5d                      pop    rbp
    1147:       c3                      ret
    1148:       0f 1f 84 00 00 00 00    nop    DWORD PTR [rax+rax*1+0x0]
    114f:       00

像 C 这样的语言的目的是让我们不必编写繁琐的汇编代码,更方便的编写程序。我们编写代码并将其交给编译器,编译器会获取该代码并生成对应的汇编代码,这些代码同样实现了 C 代码所实现的内容,它们的区别只是在于语法不同。汇编代码才是处理器实际运行的代码。由于这是实际运行的代码,因此理解它会很有帮助。由于大多数时候我们得到的是程序编译后的二进制文件,因此我们只能通过阅读汇编代码来理解程序。尽管有像 IDAGhidra 这样的反编译工具可以根据反汇编代码生成更易于理解的 C 语言代码,但掌握汇编还是一项必不可少的技能。

此外,对于处理器,有很多不同的架构。不同类型的架构可以运行不同类型的汇编代码。通常,我们遇到最多的是 64-bit 和 32-bit 的 ELF (Executable and Linkable Format) 文件。我习惯分别将它们称为 x64x86 可执行文件。

寄存器 (Registers)

寄存器本质上是处理器临时存储数据的地方。以下是 x64 处理器的寄存器列表及一些常见用例。

rbp: Base Pointer, points to the bottom of the current stack frame
rsp: Stack Pointer, points to the top of the current stack frame
rip: Instruction Pointer, points to the instruction to be executed

General Purpose Registers
These can be used for a variety of different things
rax:
rbx:
rcx:
rdx:
rsi:
rdi:
r8:
r9:
r10:
r11:
r12:
r13:
r14:
r15:

x86 中,函数的参数保存在栈中;x64 中,函数的前 6 个参数保存在寄存器中,若超过 6 个,则会将其余的参数保存在栈上。前 6 个参数依次分别保存在这些寄存器中:

rdi:    First Argument
rsi:    Second Argument
rdx:    Third Argument
rcx:    Fourth Argument
r8:     Fifth Argument
r9:     Sixth Argument

x86 中,函数的返回值通常保存在 eax 中;x64 中,函数的返回值通常保存在 rax 中。

另外,寄存器有不同的大小。我们通常处理的大小有 8 Bytes、4 Bytes、2 Bytes 和 1 Byte。产生这些不同大小的寄存器的原因是由于技术的进步,我们可以在寄存器中存储更多的数据。

+-----------------+---------------+---------------+------------+
| 8 Byte Register | Lower 4 Bytes | Lower 2 Bytes | Lower Byte |
+-----------------+---------------+---------------+------------+
|   rbp           |     ebp       |     bp        |     bpl    |
|   rsp           |     esp       |     sp        |     spl    |
|   rip           |     eip       |               |            |
|   rax           |     eax       |     ax        |     al     |
|   rbx           |     ebx       |     bx        |     bl     |
|   rcx           |     ecx       |     cx        |     cl     |
|   rdx           |     edx       |     dx        |     dl     |
|   rsi           |     esi       |     si        |     sil    |
|   rdi           |     edi       |     di        |     dil    |
|   r8            |     r8d       |     r8w       |     r8b    |
|   r9            |     r9d       |     r9w       |     r9b    |
|   r10           |     r10d      |     r10w      |     r10b   |
|   r11           |     r11d      |     r11w      |     r11b   |
|   r12           |     r12d      |     r12w      |     r12b   |
|   r13           |     r13d      |     r13w      |     r13b   |
|   r14           |     r14d      |     r14w      |     r14b   |
|   r15           |     r15d      |     r15w      |     r15b   |
+-----------------+---------------+---------------+------------+

x64 中,我们可以用上 8 Bytes 的寄存器。然而,在 x86 中,我们能使用的最大的寄存器的容量只有 4 Bytes。

我们也可以使用比架构的最大寄存器更小的寄存器。比如,在 x64 中有 raxeaxaxal 寄存器。其中,rax 指向完整的八字节,eaxrax 的低四字节,axrax 的最后两个字节,alrax 的最后一个字节。

字 (Word)

你可能听说过「字」这个术语。一个字代表了两个字节的数据,一个双字则代表了四个字节的数据,一个四字就是八个字节的数据。

栈 (Stack)

通常,你处理的最常见的内存区域之一是栈。它是存储代码中的局部变量的地方。

例如,在此代码中,变量 x 存储在栈中:

#include <stdio.h>

void main(void)
{
    int x = 5;
    puts("hi");
}

通过汇编代码,我们可以看到它实际存储在地址为 rbp-0x4 的栈上。

0000000000001135 <main>:
    1135:       55                      push   rbp
    1136:       48 89 e5                mov    rbp,rsp
    1139:       48 83 ec 10             sub    rsp,0x10
    113d:       c7 45 fc 05 00 00 00    mov    DWORD PTR [rbp-0x4],0x5
    1144:       48 8d 3d b9 0e 00 00    lea    rdi,[rip+0xeb9]        # 2004 <_IO_stdin_used+0x4>
    114b:       e8 e0 fe ff ff          call   1030 <puts@plt>
    1150:       90                      nop
    1151:       c9                      leave
    1152:       c3                      ret
    1153:       66 2e 0f 1f 84 00 00    nop    WORD PTR cs:[rax+rax*1+0x0]
    115a:       00 00 00
    115d:       0f 1f 00                nop    DWORD PTR [rax]

栈可以通过 pushpop 指令来传递值。这也是从栈中添加或删除值的唯一方法,因为栈是一种 LIFO (Last in First Out) 数据结构。但是我们可以引用栈上的值。

栈的确切边界由 rbprsp 这两个寄存器记录。基指针寄存器 rbp 指向栈底;栈指针寄存器 rsp 指向栈顶。

标志位 (Flags)

有一个寄存器专门存储各种标志。标志是此寄存器的一个特定位,无论是否设置,通常都具有某种特定的含义。以下是一些标志列表。

00:     Carry Flag
01:     always 1
02:     Parity Flag
03:     always 0
04:     Adjust Flag
05:     always 0
06:     Zero Flag
07:     Sign Flag
08:     Trap Flag
09:     Interruption Flag
10:     Direction Flag
11:     Overflow Flag
12:     I/O Privilege Field lower bit
13:     I/O Privilege Field higher bit
14:     Nested Task Flag
15:     Resume Flag

除了上面列出的这些标志之外,还有很多其它的标志。但我们实际上并不会过多的处理它们(我们只会处理少数标志)。

如果你想了解更多信息,可以查看 FLAGS register

指令 (Instructions)

现在我们来介绍一些常见的指令。

mov

mov 指令将数据从一个寄存器移动到另一个寄存器。例如:

mov rax, rdx

这条指令会将数据从 rdx 寄存器移动到 rax 寄存器。

dereference

如果你看到 [],它其实是用来取消引用的,它处理指针。指针是指向特定内存地址的值(其实指针就是内存地址)。取消引用指针意味着将指针视为它指向的值。例如:

mov rax, [rdx]

将把 rdx 指向的值移动到 rax 寄存器中。另一方面:

mov [rax], rdx

将把 rdx 寄存器的值移动到 rax 寄存器指向的内存中。rax 寄存器的实际值不会被改变。

lea

lea 指令计算第二个操作数的地址,并将该地址移动到第一个操作数中。例如:

lea rdi, [rbx+0x10]

这会将 rbx+0x10 的地址移动到 rdi 寄存器中。

add

它会将两个值相加,并将总和存储在第一个参数中。例如:

add rax, rdx

这将使 rax 等于 rax + rdx 的值。

sub

该指令将从第一个操作数中减去第二个操作数,并将差值存储在第一个参数中。例如:

sub rsp, 0x10

这将使 rsp 等于 rsp - 0x10 的值。

xor

这将对给定的两个参数执行异或运算,并将结果存储在第一个参数中:

xor rdx, rax

这将使 rdx 等于 rdx ^ rax 的值。

andor 运算本质上也做了同样的事情,只是使用了 andor 二元运算符。

push

它将使栈增加 8 Bytes(对于 x64 为 8 Bytes;对于 x86,为 4 Bytes),然后将参数的内容推送到新的栈空间。例如:

push rax

这会使栈增加 8 Bytes,并且将 rax 寄存器的内容推送到栈顶。

pop

这会将顶部 8 Bytes(对于 x64 为 8 Bytes;对于 x86,为 4 Bytes)出栈并将其存储于参数中。然后它会缩小栈空间。例如:

pop rax

栈顶的 8 Bytes 最终将位于 rax 寄存器中。

jmp

这将跳转到某个特定的指令地址。它用于重定向代码执行。例如:

jmp 0x602010

该指令将导致代码执行跳转到 0x602010,并执行那里的任何指令。

call & ret

call 类似于 jmp 指令。不同之处在于它会将 rbprip 的值推送到栈上,然后跳转到指定的地址处继续执行。主要用于调用函数。函数执行完成后,将调用 ret 指令,该指令使用之前推送到栈中的 rbprip 的值从中断的地方继续执行。

cmp

它与 sub 指令类似,只不过它不将结果存储在参数中。它用于检查结果是小于零、大于零还是等于零。并根据值相应地设置标志位。

jnz / jz

非零跳转和零跳转 (jnz/jz) 指令与跳转指令相似,不同之处在于它们会根据零标志的状态执行跳转。对于 jz,它会在设置零标志时跳转;对于 jnz,情况正好相反。

小试牛刀

下面是一些简单的逆向问题,来自:CTF-Workshop

这些挑战的目的是让你获得一些逆向分析代码的经验,尝试弄清楚二进制文件在做什么。要将机器代码反汇编成汇编代码,你可以使用 objdump 之类的工具。

Hello World

首先,我们看一下汇编代码:

$ objdump -D hello_world -M intel | less

通过搜索字符串 main 找到入口函数后,我们看到以下内容:

080483fb <main>:
 80483fb:       8d 4c 24 04             lea    ecx,[esp+0x4]
 80483ff:       83 e4 f0                and    esp,0xfffffff0
 8048402:       ff 71 fc                push   DWORD PTR [ecx-0x4]
 8048405:       55                      push   ebp
 8048406:       89 e5                   mov    ebp,esp
 8048408:       51                      push   ecx
 8048409:       83 ec 04                sub    esp,0x4
 804840c:       83 ec 0c                sub    esp,0xc
 804840f:       68 b0 84 04 08          push   0x80484b0
 8048414:       e8 b7 fe ff ff          call   80482d0 <puts@plt>
 8048419:       83 c4 10                add    esp,0x10
 804841c:       b8 00 00 00 00          mov    eax,0x0
 8048421:       8b 4d fc                mov    ecx,DWORD PTR [ebp-0x4]
 8048424:       c9                      leave
 8048425:       8d 61 fc                lea    esp,[ecx-0x4]
 8048428:       c3                      ret
 8048429:       66 90                   xchg   ax,ax
 804842b:       66 90                   xchg   ax,ax
 804842d:       66 90                   xchg   ax,ax
 804842f:       90                      nop

阅读代码,我们看到一个对 puts 函数的调用:

push   0x80484b0
call   80482d0 <puts@plt>

在剩下的代码中,我们并没有看到有其它什么有趣的东西。由此推断这段代码的功能可能只是打印一个字符串。运行二进制文件,我们发现确实如此:

$ ./hello_world
hello world!

If then

同上,用 objdump 查看汇编代码:

$ objdump -D if_then -M intel | less

找到 main 函数后,我们看到:

080483fb <main>:
 80483fb:       8d 4c 24 04             lea    ecx,[esp+0x4]
 80483ff:       83 e4 f0                and    esp,0xfffffff0
 8048402:       ff 71 fc                push   DWORD PTR [ecx-0x4]
 8048405:       55                      push   ebp
 8048406:       89 e5                   mov    ebp,esp
 8048408:       51                      push   ecx
 8048409:       83 ec 14                sub    esp,0x14
 804840c:       c7 45 f4 0a 00 00 00    mov    DWORD PTR [ebp-0xc],0xa
 8048413:       83 7d f4 0a             cmp    DWORD PTR [ebp-0xc],0xa
 8048417:       75 10                   jne    8048429 <main+0x2e>
 8048419:       83 ec 0c                sub    esp,0xc
 804841c:       68 c0 84 04 08          push   0x80484c0
 8048421:       e8 aa fe ff ff          call   80482d0 <puts@plt>
 8048426:       83 c4 10                add    esp,0x10
 8048429:       b8 00 00 00 00          mov    eax,0x0
 804842e:       8b 4d fc                mov    ecx,DWORD PTR [ebp-0x4]
 8048431:       c9                      leave
 8048432:       8d 61 fc                lea    esp,[ecx-0x4]
 8048435:       c3                      ret
 8048436:       66 90                   xchg   ax,ax
 8048438:       66 90                   xchg   ax,ax
 804843a:       66 90                   xchg   ax,ax
 804843c:       66 90                   xchg   ax,ax
 804843e:       66 90                   xchg   ax,ax

它将值 0xa 传送到 ebp-0xc 中:

mov    DWORD PTR [ebp-0xc],0xa

紧接着,运行了 cmp 指令来检查 ebp-0xc0xa 是否相等。如果不相等,则跳转到 main+0x2e。由于它刚刚被赋予了值 0xa,因此它不会执行跳转:

cmp    DWORD PTR [ebp-0xc],0xa
jne    8048429 <main+0x2e>

然后,它应该调用 puts

sub    esp,0xc
push   0x80484c0
call   80482d0 <puts@plt>

因此,该程序的最终结果应该是调用 puts。运行测试,我们看到它的输出是:

$ ./if_then
x = ten

Loop

老样子:

$ objdump -D loop -M intel | less
080483fb <main>:
 80483fb:       8d 4c 24 04             lea    ecx,[esp+0x4]
 80483ff:       83 e4 f0                and    esp,0xfffffff0
 8048402:       ff 71 fc                push   DWORD PTR [ecx-0x4]
 8048405:       55                      push   ebp
 8048406:       89 e5                   mov    ebp,esp
 8048408:       51                      push   ecx
 8048409:       83 ec 14                sub    esp,0x14
 804840c:       c7 45 f4 00 00 00 00    mov    DWORD PTR [ebp-0xc],0x0
 8048413:       eb 17                   jmp    804842c <main+0x31>
 8048415:       83 ec 08                sub    esp,0x8
 8048418:       ff 75 f4                push   DWORD PTR [ebp-0xc]
 804841b:       68 c0 84 04 08          push   0x80484c0
 8048420:       e8 ab fe ff ff          call   80482d0 <printf@plt>
 8048425:       83 c4 10                add    esp,0x10
 8048428:       83 45 f4 01             add    DWORD PTR [ebp-0xc],0x1
 804842c:       83 7d f4 13             cmp    DWORD PTR [ebp-0xc],0x13
 8048430:       7e e3                   jle    8048415 <main+0x1a>
 8048432:       b8 00 00 00 00          mov    eax,0x0
 8048437:       8b 4d fc                mov    ecx,DWORD PTR [ebp-0x4]
 804843a:       c9                      leave
 804843b:       8d 61 fc                lea    esp,[ecx-0x4]
 804843e:       c3                      ret
 804843f:       90                      nop

我们看到它将位于 ebp-0xc 处的一个变量初始化为 0,然后跳转到 0x804842c (main+0x31)

mov    DWORD PTR [ebp-0xc],0x0
jmp    804842c <main+0x31>

查看 0x804842c 处的指令,我们看到以下内容:

cmp    DWORD PTR [ebp-0xc],0x13
jle    8048415 <main+0x1a>

我们看到它将 ebp-0xc 处的值与 0x13 进行比较,如果小于或等于,则跳转到 0x8048415 (0x80483fb+0x1a)。这给我们带来了一个 printf 调用:

sub    esp,0x8
push   DWORD PTR [ebp-0xc]
push   0x80484c0
call   80482d0 <printf@plt>

它看起来像是在以某种格式化字符串的形式打印 ebp-0xc 的值。之后我们可以看到它增加了 ebp-0xc 的值,然后再次执行 cmp

add    DWORD PTR [ebp-0xc],0x1

综上,把所有部分组合在一起,我们可以推断出这是一个将运行 20 次,并每次打印迭代计数器的 for 循环。用 C 语言的形式来写,应该类似以下内容:

int i = 0;
for (i = 0; i < 20; i++)
{
    printf("%d", i);
}

运行它,证明我们的分析是正确的:

$ ./loop
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

pwndbg

下面我们通过调试这个 hello_world 程序来学习如何使用 pwndbg

hello_world

Binary

如果你成功安装并设置了 pwndbg,打开后应如下所示:

$ gdb
GNU gdb (GDB) 14.2
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word".
pwndbg: loaded 154 pwndbg commands and 40 shell commands. Type pwndbg [--shell | --all] [filter] for a list.
pwndbg: created $rebase, $base, $ida GDB functions (can be used with print/break)
------- tip of the day (disable with set show-tips off) -------
GDB's follow-fork-mode parameter can be used to set whether to trace parent or child after fork() calls
pwndbg>

运行程序

通过 r 指令在 GDB 中运行程序:

$ gdb ./hello_world
GNU gdb (GDB) 14.2
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
pwndbg: loaded 154 pwndbg commands and 40 shell commands. Type pwndbg [--shell | --all] [filter] for a list.
pwndbg: created $rebase, $base, $ida GDB functions (can be used with print/break)
Reading symbols from ./hello_world...

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.archlinux.org>
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
(No debugging symbols found in ./hello_world)
------- tip of the day (disable with set show-tips off) -------
Use GDB's dprintf command to print all calls to given function. E.g. dprintf malloc, "malloc(%p)\n", (void*)$rdi will print all malloc calls
pwndbg> r
Starting program: /home/cub3y0nd/Downloads/hello_world
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
hello world!
[Inferior 1 (process 6393) exited normally]

我们可以设置断点来调试程序。断点是在程序中 GDB 停止执行以便你检查栈内容的位置。最常用的断点设置在 main 函数,可以使用 break mainb main 来设置。大多数 GDB 命令都有简写,可以自行查询。

pwndbg> b main
Breakpoint 1 at 0x8048409
pwndbg> r
Starting program: /home/cub3y0nd/Downloads/hello_world
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".

Breakpoint 1, 0x08048409 in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────
*EAX  0x80483fb (main) ◂— lea ecx, [esp + 4]
*EBX  0xf7f9ae2c ◂— 0x228d4c
*ECX  0xffffd650 ◂— 0x1
*EDX  0xffffd670 —▸ 0xf7f9ae2c ◂— 0x228d4c
*EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0x0
*ESI  0x8048430 (__libc_csu_init) ◂— push ebp
*EBP  0xffffd638 ◂— 0x0
*ESP  0xffffd634 —▸ 0xffffd650 ◂— 0x1
*EIP  0x8048409 (main+14) ◂— sub esp, 4
────────────────────────────────────────────────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x8048409 <main+14>    sub    esp, 4
   0x804840c <main+17>    sub    esp, 0ch
   0x804840f <main+20>    push   80484b0h
   0x8048414 <main+25>    call   80482d0h                      <puts@plt>

   0x8048419 <main+30>    add    esp, 10h
   0x804841c <main+33>    mov    eax, 0
   0x8048421 <main+38>    mov    ecx, dword ptr [ebp - 4]
   0x8048424 <main+41>    leave
   0x8048425 <main+42>    lea    esp, [ecx - 4]
   0x8048428 <main+45>    ret

   0x8048429              nop
────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────
00:0000│ esp 0xffffd634 —▸ 0xffffd650 ◂— 0x1
01:0004│ ebp 0xffffd638 ◂— 0x0
02:0008│+004 0xffffd63c —▸ 0xf7d92a77 ◂— add esp, 10h
03:000c│+008 0xffffd640 ◂— 0x0
04:0010│+00c 0xffffd644 ◂— 0x0
05:0014│+010 0xffffd648 ◂— 0x3
06:0018│+014 0xffffd64c —▸ 0xf7d92a77 ◂— add esp, 10h
07:001c│ ecx 0xffffd650 ◂— 0x1
──────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────
 ► 0 0x8048409 main+14
   1 0xf7d92a77
   2 0xf7d92b3d __libc_start_main+141
   3 0x8048321 _start+33
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

现在,你可以通过 nexti 指令来逐步执行该程序,直到程序结束。nexti 将在汇编层面逐条指令地执行程序,而不会进入诸如 puts 之类的函数调用,即「单步步过」。

类似的指令还有:

  • next - 单步步过,源代码层面的一步
  • step - 单步步入,源代码层面的一步
  • stepi - 单步步入,汇编层面的一步

断点

使用 disassembledisass 反汇编 main 函数:

pwndbg> disass main
Dump of assembler code for function main:
   0x080483fb <+0>:	lea    ecx,[esp+0x4]
   0x080483ff <+4>:	and    esp,0xfffffff0
   0x08048402 <+7>:	push   DWORD PTR [ecx-0x4]
   0x08048405 <+10>:	push   ebp
   0x08048406 <+11>:	mov    ebp,esp
   0x08048408 <+13>:	push   ecx
=> 0x08048409 <+14>:	sub    esp,0x4
   0x0804840c <+17>:	sub    esp,0xc
   0x0804840f <+20>:	push   0x80484b0
   0x08048414 <+25>:	call   0x80482d0 <puts@plt>
   0x08048419 <+30>:	add    esp,0x10
   0x0804841c <+33>:	mov    eax,0x0
   0x08048421 <+38>:	mov    ecx,DWORD PTR [ebp-0x4]
   0x08048424 <+41>:	leave
   0x08048425 <+42>:	lea    esp,[ecx-0x4]
   0x08048428 <+45>:	ret
End of assembler dump.

假设我们想在调用 puts 时中断,我们可以为该指令设置断点。

pwndbg> b *main+25
Breakpoint 1 at 0x8048414

或这样:

pwndbg> b *0x08048414
Breakpoint 1 at 0x8048414

当我们运行二进制文件并尝试执行该指令时,程序将暂停在断点处并将我们带入调试器控制台:

pwndbg> r
Starting program: /home/cub3y0nd/Downloads/hello_world
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".

Breakpoint 1, 0x08048414 in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────
*EAX  0x80483fb (main) ◂— lea ecx, [esp + 4]
*EBX  0xf7f9ae2c ◂— 0x228d4c
*ECX  0xffffd650 ◂— 0x1
*EDX  0xffffd670 —▸ 0xf7f9ae2c ◂— 0x228d4c
*EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0x0
*ESI  0x8048430 (__libc_csu_init) ◂— push ebp
*EBP  0xffffd638 ◂— 0x0
*ESP  0xffffd620 —▸ 0x80484b0 ◂— push 6f6c6c65h /* 'hello world!' */
*EIP  0x8048414 (main+25) —▸ 0xfffeb7e8 ◂— 0x0
────────────────────────────────────────────────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x8048414 <main+25>    call   80482d0h                      <puts@plt>
        s: 0x80484b0 ◂— 'hello world!'

   0x8048419 <main+30>    add    esp, 10h
   0x804841c <main+33>    mov    eax, 0
   0x8048421 <main+38>    mov    ecx, dword ptr [ebp - 4]
   0x8048424 <main+41>    leave
   0x8048425 <main+42>    lea    esp, [ecx - 4]
   0x8048428 <main+45>    ret

   0x8048429              nop
   0x804842b              nop
   0x804842d              nop
   0x804842f              nop
────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────
00:0000│ esp 0xffffd620 —▸ 0x80484b0 ◂— push 6f6c6c65h /* 'hello world!' */
01:0004│-014 0xffffd624 ◂— 0x0
... ↓        3 skipped
05:0014│-004 0xffffd634 —▸ 0xffffd650 ◂— 0x1
06:0018│ ebp 0xffffd638 ◂— 0x0
07:001c│+004 0xffffd63c —▸ 0xf7d92a77 ◂— add esp, 10h
──────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────
 ► 0 0x8048414 main+25
   1 0xf7d92a77
   2 0xf7d92b3d __libc_start_main+141
   3 0x8048321 _start+33
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

使用以下指令显示所有断点:

pwndbg> info breakpoints
Num     Type           Disp Enb Address    What
1       breakpoint     keep y   0x08048414 <main+25>
	breakpoint already hit 1 time

也可以用 info bi b

根据编号删除断点:

pwndbg> delete 1

也可以使用 del 1d 1

我们还可以为 puts 之类的函数设置断点:

pwndbg> b *puts
Breakpoint 1 at 0x80482d0
pwndbg> r
Starting program: /home/cub3y0nd/Downloads/hello_world
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".

Breakpoint 1, 0xf7ded060 in puts () from /usr/lib32/libc.so.6
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────
*EAX  0x80483fb (main) ◂— lea ecx, [esp + 4]
*EBX  0xf7f9ae2c ◂— 0x228d4c
*ECX  0xffffd650 ◂— 0x1
*EDX  0xffffd670 —▸ 0xf7f9ae2c ◂— 0x228d4c
*EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0x0
*ESI  0x8048430 (__libc_csu_init) ◂— push ebp
*EBP  0xffffd638 ◂— 0x0
*ESP  0xffffd61c —▸ 0x8048419 (main+30) ◂— add esp, 10h
*EIP  0xf7ded060 (puts) ◂— endbr32
────────────────────────────────────────────────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0xf7ded060 <puts>       endbr32
   0xf7ded064 <puts+4>     push   ebp
   0xf7ded065 <puts+5>     mov    ebp, esp
   0xf7ded067 <puts+7>     push   edi
   0xf7ded068 <puts+8>     call   0f7ef1d83h                    <0xf7ef1d83>

   0xf7ded06d <puts+13>    add    edi, 1addbfh
   0xf7ded073 <puts+19>    push   esi
   0xf7ded074 <puts+20>    push   ebx
   0xf7ded075 <puts+21>    sub    esp, 28h
   0xf7ded078 <puts+24>    mov    dword ptr [ebp - 1ch], edi
   0xf7ded07b <puts+27>    push   dword ptr [ebp + 8]
────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────
00:0000│ esp 0xffffd61c —▸ 0x8048419 (main+30) ◂— add esp, 10h
01:0004│-018 0xffffd620 —▸ 0x80484b0 ◂— push 6f6c6c65h /* 'hello world!' */
02:0008│-014 0xffffd624 ◂— 0x0
... ↓        3 skipped
06:0018│-004 0xffffd634 —▸ 0xffffd650 ◂— 0x1
07:001c│ ebp 0xffffd638 ◂— 0x0
──────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────
 ► 0 0xf7ded060 puts
   1 0x8048419 main+30
   2 0xf7d92a77
   3 0xf7d92b3d __libc_start_main+141
   4 0x8048321 _start+33
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

查看内存

GDB 可以查看内存的值。当我们进入调试器时,可以查看 esp 寄存器的内容。为了实现这一点,我们需要在 main 上中断,运行,然后推进三条指令:

pwndbg> b main
Breakpoint 1 at 0x8048409
pwndbg> r
Starting program: /home/cub3y0nd/Downloads/hello_world
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".

Breakpoint 1, 0x08048409 in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────
*EAX  0x80483fb (main) ◂— lea ecx, [esp + 4]
*EBX  0xf7f9ae2c ◂— 0x228d4c
*ECX  0xffffd650 ◂— 0x1
*EDX  0xffffd670 —▸ 0xf7f9ae2c ◂— 0x228d4c
*EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0x0
*ESI  0x8048430 (__libc_csu_init) ◂— push ebp
*EBP  0xffffd638 ◂— 0x0
*ESP  0xffffd634 —▸ 0xffffd650 ◂— 0x1
*EIP  0x8048409 (main+14) ◂— sub esp, 4
────────────────────────────────────────────────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────────────────────────────────────────────────
 ► 0x8048409 <main+14>    sub    esp, 4
   0x804840c <main+17>    sub    esp, 0ch
   0x804840f <main+20>    push   80484b0h
   0x8048414 <main+25>    call   80482d0h                      <puts@plt>

   0x8048419 <main+30>    add    esp, 10h
   0x804841c <main+33>    mov    eax, 0
   0x8048421 <main+38>    mov    ecx, dword ptr [ebp - 4]
   0x8048424 <main+41>    leave
   0x8048425 <main+42>    lea    esp, [ecx - 4]
   0x8048428 <main+45>    ret

   0x8048429              nop
────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────
00:0000│ esp 0xffffd634 —▸ 0xffffd650 ◂— 0x1
01:0004│ ebp 0xffffd638 ◂— 0x0
02:0008│+004 0xffffd63c —▸ 0xf7d92a77 ◂— add esp, 10h
03:000c│+008 0xffffd640 ◂— 0x0
04:0010│+00c 0xffffd644 ◂— 0x0
05:0014│+010 0xffffd648 ◂— 0x3
06:0018│+014 0xffffd64c —▸ 0xf7d92a77 ◂— add esp, 10h
07:001c│ ecx 0xffffd650 ◂— 0x1
──────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────
 ► 0 0x8048409 main+14
   1 0xf7d92a77
   2 0xf7d92b3d __libc_start_main+141
   3 0x8048321 _start+33
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> ni
0x0804840c in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────
 EAX  0x80483fb (main) ◂— lea ecx, [esp + 4]
 EBX  0xf7f9ae2c ◂— 0x228d4c
 ECX  0xffffd650 ◂— 0x1
 EDX  0xffffd670 —▸ 0xf7f9ae2c ◂— 0x228d4c
 EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0x0
 ESI  0x8048430 (__libc_csu_init) ◂— push ebp
 EBP  0xffffd638 ◂— 0x0
*ESP  0xffffd630 ◂— 0x0
*EIP  0x804840c (main+17) ◂— sub esp, 0ch
────────────────────────────────────────────────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────────────────────────────────────────────────
   0x8048409 <main+14>    sub    esp, 4
 ► 0x804840c <main+17>    sub    esp, 0ch
   0x804840f <main+20>    push   80484b0h
   0x8048414 <main+25>    call   80482d0h                      <puts@plt>

   0x8048419 <main+30>    add    esp, 10h
   0x804841c <main+33>    mov    eax, 0
   0x8048421 <main+38>    mov    ecx, dword ptr [ebp - 4]
   0x8048424 <main+41>    leave
   0x8048425 <main+42>    lea    esp, [ecx - 4]
   0x8048428 <main+45>    ret

   0x8048429              nop
────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────
00:0000│ esp 0xffffd630 ◂— 0x0
01:0004│-004 0xffffd634 —▸ 0xffffd650 ◂— 0x1
02:0008│ ebp 0xffffd638 ◂— 0x0
03:000c│+004 0xffffd63c —▸ 0xf7d92a77 ◂— add esp, 10h
04:0010│+008 0xffffd640 ◂— 0x0
05:0014│+00c 0xffffd644 ◂— 0x0
06:0018│+010 0xffffd648 ◂— 0x3
07:001c│+014 0xffffd64c —▸ 0xf7d92a77 ◂— add esp, 10h
──────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────
 ► 0 0x804840c main+17
   1 0xf7d92a77
   2 0xf7d92b3d __libc_start_main+141
   3 0x8048321 _start+33
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> ni
0x0804840f in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────
 EAX  0x80483fb (main) ◂— lea ecx, [esp + 4]
 EBX  0xf7f9ae2c ◂— 0x228d4c
 ECX  0xffffd650 ◂— 0x1
 EDX  0xffffd670 —▸ 0xf7f9ae2c ◂— 0x228d4c
 EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0x0
 ESI  0x8048430 (__libc_csu_init) ◂— push ebp
 EBP  0xffffd638 ◂— 0x0
*ESP  0xffffd624 ◂— 0x0
*EIP  0x804840f (main+20) ◂— push 80484b0h
────────────────────────────────────────────────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────────────────────────────────────────────────
   0x8048409 <main+14>    sub    esp, 4
   0x804840c <main+17>    sub    esp, 0ch
 ► 0x804840f <main+20>    push   80484b0h
   0x8048414 <main+25>    call   80482d0h                      <puts@plt>

   0x8048419 <main+30>    add    esp, 10h
   0x804841c <main+33>    mov    eax, 0
   0x8048421 <main+38>    mov    ecx, dword ptr [ebp - 4]
   0x8048424 <main+41>    leave
   0x8048425 <main+42>    lea    esp, [ecx - 4]
   0x8048428 <main+45>    ret

   0x8048429              nop
────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────
00:0000│ esp 0xffffd624 ◂— 0x0
... ↓        3 skipped
04:0010│-004 0xffffd634 —▸ 0xffffd650 ◂— 0x1
05:0014│ ebp 0xffffd638 ◂— 0x0
06:0018│+004 0xffffd63c —▸ 0xf7d92a77 ◂— add esp, 10h
07:001c│+008 0xffffd640 ◂— 0x0
──────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────
 ► 0 0x804840f main+20
   1 0xf7d92a77
   2 0xf7d92b3d __libc_start_main+141
   3 0x8048321 _start+33
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> ni
0x08048414 in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────
 EAX  0x80483fb (main) ◂— lea ecx, [esp + 4]
 EBX  0xf7f9ae2c ◂— 0x228d4c
 ECX  0xffffd650 ◂— 0x1
 EDX  0xffffd670 —▸ 0xf7f9ae2c ◂— 0x228d4c
 EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0x0
 ESI  0x8048430 (__libc_csu_init) ◂— push ebp
 EBP  0xffffd638 ◂— 0x0
*ESP  0xffffd620 —▸ 0x80484b0 ◂— push 6f6c6c65h /* 'hello world!' */
*EIP  0x8048414 (main+25) —▸ 0xfffeb7e8 ◂— 0x0
────────────────────────────────────────────────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────────────────────────────────────────────────
   0x8048409 <main+14>    sub    esp, 4
   0x804840c <main+17>    sub    esp, 0ch
   0x804840f <main+20>    push   80484b0h
 ► 0x8048414 <main+25>    call   80482d0h                      <puts@plt>
        s: 0x80484b0 ◂— 'hello world!'

   0x8048419 <main+30>    add    esp, 10h
   0x804841c <main+33>    mov    eax, 0
   0x8048421 <main+38>    mov    ecx, dword ptr [ebp - 4]
   0x8048424 <main+41>    leave
   0x8048425 <main+42>    lea    esp, [ecx - 4]
   0x8048428 <main+45>    ret

   0x8048429              nop
────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────
00:0000│ esp 0xffffd620 —▸ 0x80484b0 ◂— push 6f6c6c65h /* 'hello world!' */
01:0004│-014 0xffffd624 ◂— 0x0
... ↓        3 skipped
05:0014│-004 0xffffd634 —▸ 0xffffd650 ◂— 0x1
06:0018│ ebp 0xffffd638 ◂— 0x0
07:001c│+004 0xffffd63c —▸ 0xf7d92a77 ◂— add esp, 10h
──────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────
 ► 0 0x8048414 main+25
   1 0xf7d92a77
   2 0xf7d92b3d __libc_start_main+141
   3 0x8048321 _start+33
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

我们可以看到寄存器 esp 保存的是值 0xffffd620,这是一个指针。我们来看看它指向什么:

pwndbg> x/a 0xffffd620
0xffffd620:	0x80484b0
pwndbg> x/13c 0x80484b0
0x80484b0:	104 'h'	101 'e'	108 'l'	108 'l'	111 'o'	32 ' '	119 'w'	111 'o'
0x80484b8:	114 'r'	108 'l'	100 'd'	33 '!'	0 '\000'
pwndbg> x/s 0x80484b0
0x80484b0:	"hello world!"

我们可以看到它指向字符串 hello world!,它将由 puts 打印(因为 puts 接受一个参数,即 char 指针)。在 GDB 中,当你使用 x 指令查看内存时,你可以指定要以什么形式输出。常见的有:地址 x/a、字符数 x/10c 字符串 x/s、QWORD x/b 或 DWORD x/w

查看所有寄存器的内容:

pwndbg> info registers
eax            0x80483fb           134513659
ecx            0xffffd650          -10672
edx            0xffffd670          -10640
ebx            0xf7f9ae2c          -134631892
esp            0xffffd620          0xffffd620
ebp            0xffffd638          0xffffd638
esi            0x8048430           134513712
edi            0xf7ffcb60          -134231200
eip            0x8048414           0x8048414 <main+25>
eflags         0x296               [ PF AF SF IF ]
cs             0x23                35
ss             0x2b                43
ds             0x2b                43
es             0x2b                43
fs             0x0                 0
gs             0x63                99

查看栈帧:

pwndbg> info frame
Stack level 0, frame at 0xffffd650:
 eip = 0x8048414 in main; saved eip = 0xf7d92a77
 called by frame at 0xffffd6b0
 Arglist at 0xffffd638, args:
 Locals at 0xffffd638, Previous frame's sp is 0xffffd650
 Saved registers:
  ebp at 0xffffd638, eip at 0xffffd64c

查看 main 函数的反汇编代码:

pwndbg> disass main
Dump of assembler code for function main:
   0x080483fb <+0>:	lea    ecx,[esp+0x4]
   0x080483ff <+4>:	and    esp,0xfffffff0
   0x08048402 <+7>:	push   DWORD PTR [ecx-0x4]
   0x08048405 <+10>:	push   ebp
   0x08048406 <+11>:	mov    ebp,esp
   0x08048408 <+13>:	push   ecx
   0x08048409 <+14>:	sub    esp,0x4
   0x0804840c <+17>:	sub    esp,0xc
   0x0804840f <+20>:	push   0x80484b0
=> 0x08048414 <+25>:	call   0x80482d0 <puts@plt>
   0x08048419 <+30>:	add    esp,0x10
   0x0804841c <+33>:	mov    eax,0x0
   0x08048421 <+38>:	mov    ecx,DWORD PTR [ebp-0x4]
   0x08048424 <+41>:	leave
   0x08048425 <+42>:	lea    esp,[ecx-0x4]
   0x08048428 <+45>:	ret
End of assembler dump.

改变值

如你所见,我们正处于 puts 指令处。

我们可以更改想要打印的内容。但是,在许多程序中,你执行此操作的能力取决于你尝试替换的字符串的大小。如果你用太大的内容覆盖它,则存在覆盖其它内存并破坏程序的风险。有很多解决方法,但从 bin-ex 的角度来看,这很少适用。

pwndbg> set { char[12] } 0x80484b0 = "hello cubey!"
pwndbg> x/s 0x80484b0
0x80484b0:	"hello cubey!"
pwndbg> ni
hello cubey!
0x08048419 in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────────────────────────
*EAX  0xd
 EBX  0xf7f9ae2c ◂— 0x228d4c
*ECX  0xf7f9c8a0 ◂— 0x0
*EDX  0x0
 EDI  0xf7ffcb60 (_rtld_global_ro) ◂— 0x0
 ESI  0x8048430 (__libc_csu_init) ◂— push ebp
 EBP  0xffffd638 ◂— 0x0
 ESP  0xffffd620 —▸ 0x80484b0 ◂— push 6f6c6c65h /* 'hello cubey!' */
*EIP  0x8048419 (main+30) ◂— add esp, 10h
────────────────────────────────────────────────────────────────────[ DISASM / i386 / set emulate on ]────────────────────────────────────────────────────────────────────
   0x8048409  <main+14>    sub    esp, 4
   0x804840c  <main+17>    sub    esp, 0ch
   0x804840f  <main+20>    push   80484b0h
   0x8048414  <main+25>    call   80482d0h                      <puts@plt>

 ► 0x8048419  <main+30>    add    esp, 10h
   0x804841c  <main+33>    mov    eax, 0
   0x8048421  <main+38>    mov    ecx, dword ptr [ebp - 4]
   0x8048424  <main+41>    leave
   0x8048425  <main+42>    lea    esp, [ecx - 4]
   0x8048428  <main+45>    ret
   0xf7d92a77              add    esp, 10h
────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────
00:0000│ esp 0xffffd620 —▸ 0x80484b0 ◂— push 6f6c6c65h /* 'hello cubey!' */
01:0004│-014 0xffffd624 ◂— 0x0
... ↓        3 skipped
05:0014│-004 0xffffd634 —▸ 0xffffd650 ◂— 0x1
06:0018│ ebp 0xffffd638 ◂— 0x0
07:001c│+004 0xffffd63c —▸ 0xf7d92a77 ◂— add esp, 10h
──────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────
 ► 0 0x8048419 main+30
   1 0xf7d92a77
   2 0xf7d92b3d __libc_start_main+141
   3 0x8048321 _start+33
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

现在假设我们想将存储在地址 0x08048451 的值更改为 0xdeadbeef

pwndbg> x/a 0x08048451
0x8048451 <__libc_csu_init+33>:	0xff08838d
pwndbg> set *0x08048451 = 0xdeadbeef
pwndbg> x/a 0x08048451
0x8048451 <__libc_csu_init+33>:	0xdeadbeef

假设我们想直接跳转到 0x08048451 处的指令,并跳过其间的所有指令:

pwndbg> jump *0x08048451
Continuing at 0x8048451.

目前这些基本够入门调试器的使用了,在以后的学习中会逐渐掌握更多指令。

pwntools

pwntools 可以帮助我们快速编写漏洞利用脚本。

用法

这里只展示一小部分功能的用法,有关更多功能请查阅 pwntools document

如果我们想将其导入 python:

from pwn import *

如果我们想通过 TCP 连接到 github.com 上的服务器,端口 9000

target = remote('github.com', 9000)

如果你想运行二进制文件:

target = process('./challenge')

如果要将 gdb 附加到某个进程:

gdb.attach(target)

如果我们想将 gdb 附加到某个进程,并且立即向 gdb 传递一个命令以在 main 处设置断点:

gdb.attach(target, gdbscript='b* main')

现在进行实际的 I/O 操作。如果我们想将变量 x 发送到目标(目标可以是进程,也可以是 pwntools 建立的远程连接):

target.send(x)

如果我们想要发送变量 x,并在其末尾附加一个换行符:

target.sendline(x)

如果我们想从目标打印一行文本:

print(target.recvline())

如果我们想打印从 target 中获得的直到字符串 out 的所有文本:

print(target.recvuntil("out"))

还有一件事,ELF 采用低字节序存储数据,这意味着数据以最低有效字节优先的方式存储。在少数情况下,当我们扫描整数时,我们需要考虑到这一点。幸运的是,pwntools 会为我们处理这个问题。

将整数 y 打包为小端序 QWORD (常用于 x64):

p64(x)

将整数 y 打包为小端序 DWORD (常用于 x86):

p32(x)

它还可以解包我们获得的值。假设我们想解包一个小端序 QWORD 并获取其整数值:

u64(x)

要解包 DWORD

u32(x)

最后,如果要与目标交互:

target.interactive()