- 发布于
PWN 基础
- 作者
- Name
- CuB3y0nd
- GitHub
- @CuB3y0nd
复习(重修)汇编语言。发现边学边记笔记,当成一个项目来做的话有助于监督学习、提升学习效率。
李忠《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 代码所实现的内容,它们的区别只是在于语法不同。汇编代码才是处理器实际运行的代码。由于这是实际运行的代码,因此理解它会很有帮助。由于大多数时候我们得到的是程序编译后的二进制文件,因此我们只能通过阅读汇编代码来理解程序。尽管有像 IDA
、Ghidra
这样的反编译工具可以根据反汇编代码生成更易于理解的 C 语言代码,但掌握汇编还是一项必不可少的技能。
此外,对于处理器,有很多不同的架构。不同类型的架构可以运行不同类型的汇编代码。通常,我们遇到最多的是 64-bit 和 32-bit 的 ELF (Executable and Linkable Format) 文件。我习惯分别将它们称为 x64
和 x86
可执行文件。
寄存器 (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
中有 rax
、eax
、ax
和 al
寄存器。其中,rax
指向完整的八字节,eax
是 rax
的低四字节,ax
是 rax
的最后两个字节,al
是 rax
的最后一个字节。
字 (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]
栈可以通过 push
和 pop
指令来传递值。这也是从栈中添加或删除值的唯一方法,因为栈是一种 LIFO (Last in First Out) 数据结构。但是我们可以引用栈上的值。
栈的确切边界由 rbp
和 rsp
这两个寄存器记录。基指针寄存器 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
的值。
and
和 or
运算本质上也做了同样的事情,只是使用了 and
或 or
二元运算符。
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
指令。不同之处在于它会将 rbp
和 rip
的值推送到栈上,然后跳转到指定的地址处继续执行。主要用于调用函数。函数执行完成后,将调用 ret
指令,该指令使用之前推送到栈中的 rbp
和 rip
的值从中断的地方继续执行。
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-0xc
与 0xa
是否相等。如果不相等,则跳转到 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 main
或 b 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
- 单步步入,汇编层面的一步
断点
使用 disassemble
或 disass
反汇编 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 b
或 i b
。
根据编号删除断点:
pwndbg> delete 1
也可以使用 del 1
或 d 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()