burpow
第06节-从逆向视角看程序结构

第06节-从逆向视角看程序结构

第 6 节:从逆向视角看程序结构

所属课程:操作系统自学路线:面向网络安全、逆向工程与漏洞分析
所属周次:第 3 周
课程主题:调试器和逆向入门
本节目标:理解源代码视角和二进制视角的区别,掌握符号表、strip、反汇编、反编译、字符串、交叉引用、函数调用图等逆向分析基础概念。


1. 本节课你要学会什么

学完这一节,你应该能回答下面几个问题:

  1. 源代码视角和二进制视角有什么区别?
  2. 编译后,函数名、变量名、类型信息还在不在?
  3. 符号表是什么?
  4. strip 为什么会让逆向变难?
  5. 反汇编和反编译有什么区别?
  6. Ghidra、IDA、x64dbg 各自更适合做什么?
  7. 字符串为什么是逆向分析的重要入口?
  8. 交叉引用是什么?
  9. 函数调用图能帮助我们看什么?
  10. 为什么逆向时不能完全相信反编译伪代码?

本节课的主线是:

1
源代码 -> 编译 -> 二进制文件 -> 反汇编 -> 反编译 -> 字符串/交叉引用/调用图 -> 推断程序逻辑

这节课会把前面学过的编译、汇编、调试器连接到真正的逆向分析流程中。


2. 什么是逆向工程

逆向工程可以先简单理解为:

在没有完整源码的情况下,通过分析二进制文件、运行行为和系统交互,推断程序的结构、逻辑和目的。

在软件安全中,逆向工程常用于:

  • 漏洞分析
  • 恶意代码分析
  • 补丁对比
  • 协议分析
  • 加密逻辑分析
  • CTF Reverse 题目
  • 软件兼容性研究
  • 安全审计

逆向工程不是“猜谜游戏”。

它依赖大量证据:

  • 文件格式
  • 汇编代码
  • 反编译伪代码
  • 字符串
  • 导入 API
  • 函数调用关系
  • 调试器运行结果
  • 文件/网络/进程行为

你的目标不是把每条指令翻译成 C,而是逐步回答:

1
2
3
4
5
6
这个程序是什么?
它从哪里开始执行?
它有哪些关键函数?
它处理了什么输入?
它调用了哪些系统 API?
它有什么可疑行为?

3. 源代码视角和二进制视角

写 C 程序时,你看到的是源代码视角。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

int check_password(const char *input) {
if (input[0] == 'A') {
return 1;
}
return 0;
}

int main() {
char buf[32];
scanf("%31s", buf);

if (check_password(buf)) {
printf("success\n");
} else {
printf("fail\n");
}

return 0;
}

你能直接看到:

  • 函数名
  • 变量名
  • 类型
  • if 结构
  • 字符串
  • 输入输出逻辑

但编译成二进制后,你看到的可能是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
push rbp
mov rbp, rsp
sub rsp, 0x30
lea rax, [rbp-0x20]
mov rsi, rax
lea rdi, [rip+0xe9f]
call __isoc99_scanf
lea rax, [rbp-0x20]
mov rdi, rax
call 0x401156
test eax, eax
je 0x4011c0
lea rdi, [rip+0xe8a]
call puts
jmp 0x4011cc
lea rdi, [rip+0xe86]
call puts

在二进制视角中,很多高级信息会消失或变得不明显。


4. 编译后哪些信息可能还在

编译后,二进制文件中可能还保留一些信息。

例如:

  • 机器指令
  • 字符串常量
  • 导入函数名
  • 导出函数名
  • 动态库依赖
  • 部分符号信息
  • 调试信息
  • 文件格式元数据

例如字符串:

1
2
3
success
fail
%31s

通常可能保留在 .rodata.rdata 中。

导入 API 也可能保留,例如:

1
2
3
4
5
6
printf
scanf
puts
CreateFileW
VirtualAlloc
CreateProcessW

这些信息是逆向分析的重要线索。


5. 编译后哪些信息可能消失

编译后,很多源代码级信息可能消失。

例如:

  • 局部变量名
  • 原始注释
  • 宏定义原貌
  • 高级控制结构
  • 部分函数名
  • 类型信息
  • 源代码文件结构
  • 代码格式

如果没有调试符号,你可能看不到:

1
int check_password(const char *input)

而只能看到:

1
2
3
FUN_00401156
sub_401156
function_401156

变量也可能变成:

1
2
3
4
local_20
param_1
iVar1
uVar2

所以逆向不是恢复“原始源码”,而是恢复“程序逻辑”。


6. 符号是什么

符号可以理解为:

程序中函数、全局变量等对象的名字和地址之间的对应关系。

例如:

1
2
3
main -> 0x401180
check_password -> 0x401156
global_var -> 0x404030

这些名字能帮助调试器和逆向工具显示更友好的信息。

如果二进制保留符号,你可能看到:

1
0000000000401156 <check_password>:

如果符号被去掉,你可能只看到:

1
0000000000401156:

或者工具给它自动命名:

1
2
FUN_00401156
sub_401156

7. 符号表是什么

符号表就是保存符号信息的数据结构。

在 ELF 文件中,常见符号相关节包括:

1
2
3
4
.symtab
.dynsym
.strtab
.dynstr

简化理解:

名称 作用
.symtab 普通符号表,可能包含较多函数和变量名
.dynsym 动态符号表,动态链接需要用到
.strtab 普通字符串表,保存符号名字符串
.dynstr 动态字符串表,保存动态符号名等

即使普通符号被去掉,动态链接程序通常仍会保留动态符号。

因为动态链接器运行时需要知道导入导出符号。


8. 用 nm 查看符号

Linux 下可以用 nm 查看符号。

准备程序 symbol_demo.c

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

int global_var = 123;

void helper() {
printf("helper\n");
}

int main() {
helper();
return 0;
}

编译:

1
gcc -g -O0 symbol_demo.c -o symbol_demo

查看符号:

1
nm symbol_demo

你可能看到:

1
2
3
0000000000001139 T helper
0000000000001150 T main
0000000000004018 D global_var

其中:

标记 含义
T 代码段中的符号,通常是函数
D 已初始化数据段中的符号
B BSS 中的符号
U 未定义符号,需要从外部库解析

9. strip 是什么

strip 用于移除二进制文件中的符号和调试信息。

例如:

1
2
cp symbol_demo symbol_demo_stripped
strip symbol_demo_stripped

再执行:

1
nm symbol_demo_stripped

你可能看到:

1
nm: symbol_demo_stripped: no symbols

这说明普通符号被移除了。

再用 objdump 看:

1
2
objdump -d -M intel symbol_demo
objdump -d -M intel symbol_demo_stripped

你会发现:

  • 未 strip 的版本函数名更清晰
  • strip 后很多函数名消失
  • 工具只能根据地址或自动分析结果命名函数

10. strip 后程序还能运行吗

可以。

因为普通符号和调试信息主要是给人、调试器、分析工具看的。

CPU 执行程序并不需要这些名字。

CPU 只需要:

  • 机器指令
  • 地址
  • 入口点
  • 必要的动态链接信息

所以:

1
去掉符号不影响程序运行,但会增加逆向分析难度。

这就是为什么真实商业软件、恶意代码、CTF 题目经常没有完整符号。


11. 调试信息是什么

调试信息比普通符号更丰富。

它可能包含:

  • 源文件路径
  • 行号
  • 局部变量名
  • 变量类型
  • 函数参数
  • 结构体定义

编译时加 -g

1
gcc -g demo.c -o demo

会把调试信息写入可执行文件。

有调试信息时,GDB 可以:

1
2
3
4
break main
list
print variable
bt

并显示源码级信息。

去掉调试信息后,GDB 仍能调试,但更多只能靠:

  • 汇编
  • 地址
  • 寄存器
  • 内存

12. 反汇编是什么

反汇编是:

把机器码字节转换成汇编指令。

例如机器码:

1
55 48 89 e5

反汇编后:

1
2
push rbp
mov rbp, rsp

常用反汇编工具:

  • objdump
  • GDB
  • IDA
  • Ghidra
  • x64dbg

反汇编结果比较接近 CPU 实际执行的内容。

它比反编译伪代码更底层,也更可靠。

但缺点是:

  • 可读性差
  • 需要理解寄存器和指令
  • 高级结构不明显

13. 反编译是什么

反编译是:

尝试把机器码或汇编还原成类似 C 语言的伪代码。

例如汇编:

1
2
3
cmp DWORD PTR [rbp-0x4], 0xa
jne 0x401180
call puts

反编译工具可能显示:

1
2
3
if (local_4 == 10) {
puts("yes");
}

常见反编译工具:

  • Ghidra Decompiler
  • IDA Hex-Rays
  • Binary Ninja

反编译优点:

  • 可读性强
  • 更接近源代码
  • 适合快速理解逻辑

缺点:

  • 可能不准确
  • 变量名是工具猜的
  • 类型可能错
  • 控制流可能被误识别
  • 优化后的代码可能很难还原
  • 混淆代码可能让伪代码非常混乱

所以要记住:

反编译伪代码是工具推测结果,不是原始源码。

遇到关键逻辑时,要回到汇编验证。


14. 反汇编和反编译的区别

对比项 反汇编 反编译
输入 机器码 机器码/汇编
输出 汇编指令 类 C 伪代码
可靠性 更接近真实执行 可能有误判
可读性 较差 较好
适合场景 精确分析、漏洞、控制流 快速理解程序逻辑
工具 objdump、GDB、IDA、x64dbg Ghidra、IDA Hex-Rays

推荐方法:

1
2
3
先用反编译快速理解大意
再用反汇编确认关键细节
最后用调试器动态验证

15. 字符串为什么重要

字符串是逆向分析中最常用的入口之一。

因为程序中很多行为会留下字符串线索。

例如:

  • 菜单文本
  • 错误信息
  • 成功/失败提示
  • 文件路径
  • URL
  • IP 地址
  • 注册表路径
  • 命令行参数
  • 加密前后的明文
  • API 名称

例如看到字符串:

1
2
3
4
5
success
wrong password
C:\\Users\\Public\\update.exe
http://example.com/api/check
Software\\Microsoft\\Windows\\CurrentVersion\\Run

你可以推测程序可能有:

  • 密码校验逻辑
  • 文件写入行为
  • 网络通信行为
  • 注册表持久化行为

字符串往往能帮助你快速定位关键函数。


16. 用 strings 查看字符串

Linux 下可以用:

1
strings program

例如:

1
strings symbol_demo

也可以结合 grep:

1
2
3
strings program | grep -i password
strings program | grep -i http
strings program | grep -i error

注意:

strings 只能看到明文字符串。

如果字符串被加密、压缩或运行时生成,strings 可能看不到。

这时需要:

  • 动态调试
  • 内存搜索
  • 找解密函数
  • 跟踪字符串使用位置

17. 交叉引用是什么

交叉引用英文常写作:

1
XREF

它表示:

某个函数、变量或字符串被哪里引用了。

例如程序中有字符串:

1
wrong password

你想知道哪里使用了它。

在 Ghidra 或 IDA 中,可以对这个字符串查看 XREF。

工具会告诉你:

1
这个字符串被 FUN_00401180 引用了

那么 FUN_00401180 很可能和密码校验或错误处理有关。

交叉引用非常重要,因为它能从线索跳到代码。

常见分析流程:

1
发现可疑字符串 -> 查看交叉引用 -> 找到使用它的函数 -> 分析函数逻辑

18. 函数调用图是什么

函数调用图展示函数之间的调用关系。

例如:

1
2
3
4
5
6
main
├── init_config
├── check_password
│ ├── strlen
│ └── strcmp
└── print_result

它能帮助你理解程序结构。

在复杂程序中,函数很多,单看汇编容易迷路。

调用图可以帮你判断:

  • 哪个函数是入口逻辑
  • 哪些函数是初始化
  • 哪些函数处理输入
  • 哪些函数调用敏感 API
  • 哪些函数可能是加密/解密逻辑
  • 哪些函数可能是网络通信逻辑

Ghidra、IDA、Binary Ninja 都能显示不同形式的函数图或调用图。


19. 控制流图是什么

控制流图通常展示一个函数内部的分支结构。

例如一个函数中有:

1
2
3
4
5
if (...) {
...
} else {
...
}

反汇编中会表现为多个基本块和跳转。

控制流图能帮助你看:

  • 条件判断
  • 循环
  • 错误分支
  • 成功分支
  • 复杂跳转

在逆向中,常见思路是:

1
找到关键判断 -> 看成功分支和失败分支 -> 反推条件

CTF Reverse 中尤其常见。


20. Ghidra 基本分析流程

Ghidra 是一个免费开源的逆向分析工具。

基本流程:

  1. 新建 Project
  2. Import File
  3. 选择目标二进制
  4. 让 Ghidra 自动分析
  5. 查看 Symbol Tree
  6. 查看 Functions
  7. 查看 Strings
  8. 查看 Decompiler
  9. 查看 Listing 汇编
  10. 使用 XREF 找引用

常见窗口:

窗口 用途
Listing 汇编和数据视图
Decompiler 反编译伪代码
Symbol Tree 函数、标签、符号
Program Trees 程序结构
Data Type Manager 类型信息
Defined Strings 字符串列表

初学时重点掌握:

  • main
  • 看字符串
  • 看交叉引用
  • 看反编译伪代码
  • 对照汇编确认关键逻辑

21. IDA 基本分析思路

IDA 是经典逆向工具。

常见概念:

  • Functions 窗口
  • Strings 窗口
  • Imports 窗口
  • Exports 窗口
  • Graph View
  • Text View
  • Xrefs
  • Pseudocode

IDA 的图形视图非常适合看函数控制流。

如果使用 IDA Free,也可以完成很多入门分析。

基本思路和 Ghidra 类似:

1
文件格式 -> 入口点 -> 字符串 -> 导入函数 -> 交叉引用 -> 关键函数 -> 动态验证

22. x64dbg 在逆向中的角色

Ghidra 和 IDA 更偏静态分析。

x64dbg 更偏 Windows 动态调试。

x64dbg 适合:

  • 下断点
  • 单步执行
  • 看寄存器
  • 看栈
  • 看内存
  • 看模块
  • 跟踪 API 调用
  • 修改运行时数据
  • 观察解密后的字符串
  • 分析简单壳或反调试

常见组合是:

1
2
Ghidra / IDA 静态定位关键函数
x64dbg 动态验证关键函数行为

23. Linux 实验:debug 版和 strip 版对比

创建 reverse_demo.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <string.h>

int check_password(const char *input) {
if (strcmp(input, "open_sesame") == 0) {
return 1;
}
return 0;
}

int main() {
char buf[64];

printf("input password: ");
scanf("%63s", buf);

if (check_password(buf)) {
printf("success\n");
} else {
printf("fail\n");
}

return 0;
}

编译 debug 版:

1
gcc -g -O0 reverse_demo.c -o reverse_demo_debug

编译普通版:

1
gcc -O0 reverse_demo.c -o reverse_demo_release

复制并 strip:

1
2
cp reverse_demo_release reverse_demo_stripped
strip reverse_demo_stripped

对比:

1
2
3
4
file reverse_demo_debug reverse_demo_release reverse_demo_stripped
nm reverse_demo_debug | grep check
nm reverse_demo_release | grep check
nm reverse_demo_stripped | grep check

观察:

  • 哪个版本符号最多?
  • strip 后还能看到 check_password 吗?
  • 程序是否仍然能运行?

24. Linux 实验:字符串和反汇编

查看字符串:

1
strings reverse_demo_stripped

你应该能看到:

1
2
3
4
open_sesame
input password:
success
fail

反汇编:

1
objdump -d -M intel reverse_demo_stripped > reverse_demo_stripped.asm

虽然函数名可能消失,但字符串还在。

你可以继续用:

1
objdump -s -j .rodata reverse_demo_stripped

查看只读数据区。

思考:

  • 密码字符串在哪里?
  • 成功/失败字符串在哪里?
  • 如果没有函数名,字符串是否仍然能帮助定位逻辑?

25. Ghidra 实验:用字符串找关键函数

用 Ghidra 打开 reverse_demo_stripped

步骤:

  1. Import File
  2. 运行自动分析
  3. 打开 Defined Strings
  4. 找到 open_sesame
  5. 右键查看 References / XREF
  6. 跳到引用它的函数
  7. 查看 Decompiler 伪代码
  8. 尝试判断哪个函数是 check_password
  9. 给函数重命名为 check_password
  10. 给局部变量改一个更清晰的名字

这一步很重要。

真实逆向中,你经常要自己给函数和变量重新命名。

重命名不是为了好看,而是为了建立理解。


26. Ghidra 实验:对比反汇编和反编译

在 Ghidra 中找到关键函数后,同时观察:

  • Listing 窗口中的汇编
  • Decompiler 窗口中的伪代码

重点看:

  • strcmp 调用
  • 参数是什么
  • 返回值如何判断
  • 成功和失败分支

思考:

  1. 反编译伪代码是否接近原始 C?
  2. 变量名是否还原正确?
  3. 函数名是否需要你手动修改?
  4. 如果伪代码看不懂,汇编能否提供更多确定信息?

27. Windows 对照实验:PE 中的字符串和导入表

如果你有 Windows 环境,可以写类似程序并编译成 exe。

使用工具:

  • Detect It Easy
  • PE-bear
  • Ghidra
  • x64dbg

观察:

  1. 文件是否是 PE
  2. 有哪些 Section
  3. Import Table 中有哪些函数
  4. 字符串窗口能否看到 successfail、密码字符串
  5. Ghidra 中能否通过字符串 XREF 找到关键函数
  6. x64dbg 中能否在 strcmp 或相关比较位置下断点

Windows 下导入函数可能来自:

1
2
3
msvcrt.dll
ucrtbase.dll
kernel32.dll

具体取决于编译器和运行库。


28. 逆向分析的基本流程

拿到一个陌生二进制时,可以按这个顺序来:

28.1 识别文件

Linux:

1
file program

Windows:

  • Detect It Easy
  • PE-bear

看:

  • ELF / PE
  • 32 位 / 64 位
  • 是否动态链接
  • 是否可能加壳

28.2 查看字符串

1
strings program

关注:

  • 错误信息
  • 成功信息
  • URL
  • 文件路径
  • 命令
  • 注册表路径
  • API 名称

28.3 查看导入函数

Linux:

1
2
3
ldd program
readelf -s program
objdump -T program

Windows:

  • PE-bear Import Table
  • Ghidra Imports
  • IDA Imports

关注:

  • 文件操作 API
  • 网络 API
  • 进程线程 API
  • 内存管理 API
  • 加密 API

28.4 找入口点和 main

入口点不一定是 main

真实流程通常是:

1
入口点 -> 运行时初始化 -> main

Ghidra / IDA 通常能帮助你定位 main 或类似函数。


28.5 用字符串和 XREF 找关键函数

这是最常用方法之一:

1
找到关键字符串 -> 查看引用 -> 找到关键函数 -> 分析逻辑

28.6 动态调试验证

静态分析只是推测。

关键结论最好用调试器验证:

  • 下断点
  • 观察参数
  • 观察返回值
  • 观察内存
  • 观察分支跳转

29. 从漏洞分析角度看程序结构

漏洞分析时,你也需要程序结构视角。

你要找:

  • 输入入口在哪里
  • 输入传给了哪些函数
  • 是否有长度检查
  • 是否调用危险函数
  • 数据是否进入栈或堆缓冲区
  • 崩溃点和输入处理函数之间的调用关系

常见危险函数线索:

1
2
3
4
5
6
7
strcpy
strcat
sprintf
gets
scanf
memcpy
strncpy 使用不当

如果在导入表或反编译中看到这些函数,要提高警惕。

但注意:

出现危险函数不等于一定有漏洞,关键看输入是否可控、长度是否受限、目标缓冲区大小是否足够。


30. 从恶意代码分析角度看程序结构

恶意代码分析时,结构视角同样重要。

你要找:

  • 初始化函数
  • 反调试函数
  • 解密函数
  • 配置解析函数
  • 网络通信函数
  • 文件写入函数
  • 持久化函数
  • 注入或执行函数

常见可疑 API 线索包括:

Windows:

1
2
3
4
5
6
7
8
9
10
11
12
13
CreateFileW
WriteFile
CreateProcessW
VirtualAlloc
VirtualProtect
WriteProcessMemory
CreateRemoteThread
RegSetValueExW
InternetOpenW
WinHttpOpen
connect
send
recv

Linux:

1
2
3
4
5
6
7
8
9
10
11
open
write
execve
fork
ptrace
mmap
mprotect
socket
connect
send
recv

这些 API 本身不一定恶意,但组合起来能描述行为。

例如:

1
VirtualAlloc + WriteProcessMemory + CreateRemoteThread

在安全分析中就非常值得关注。


31. 不要过度相信函数名

有些函数名是真实符号。

有些函数名是工具自动生成的。

有些函数名可能被开发者故意误导。

例如恶意代码可能把函数命名为:

1
2
3
normal_update
check_license
safe_function

但实际做的是可疑行为。

所以逆向时不要只相信名字,要看证据:

  • 函数调用了什么 API
  • 参数是什么
  • 写了哪些路径
  • 连了哪些地址
  • 改了哪些内存
  • 运行时行为是什么

32. 不要过度相信反编译变量类型

反编译器会猜变量类型。

例如它可能把某个变量猜成:

1
2
3
4
int
char *
undefined8
longlong

但这些类型不一定准确。

如果你分析关键逻辑,要验证:

  • 这个变量被当作地址使用吗?
  • 它传给了哪个函数?
  • 它参与了整数运算还是指针运算?
  • 它对应的寄存器和内存访问宽度是多少?

类型错误会导致伪代码误导你。

这就是为什么关键处要看汇编。


33. 本节重点总结

你需要记住这些核心结论:

  1. 逆向工程是在没有完整源码时推断程序结构和行为。
  2. 源代码编译后,很多高级信息会消失或变得不明显。
  3. 符号表保存函数、变量等名字和地址的对应关系。
  4. strip 会移除很多符号和调试信息,使逆向更困难。
  5. 反汇编更接近真实机器执行,反编译更容易阅读但可能不准确。
  6. 字符串是逆向分析的重要入口。
  7. 交叉引用能帮助你从字符串、函数或变量跳到使用它们的位置。
  8. 函数调用图和控制流图能帮助理解程序结构。
  9. 静态分析和动态调试应该结合使用。
  10. 逆向时要看证据,不要盲目信任函数名、变量名或反编译伪代码。

34. 本节课后作业

作业 1:debug / release / stripped 对比

reverse_demo.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <string.h>

int check_password(const char *input) {
if (strcmp(input, "open_sesame") == 0) {
return 1;
}
return 0;
}

int main() {
char buf[64];

printf("input password: ");
scanf("%63s", buf);

if (check_password(buf)) {
printf("success\n");
} else {
printf("fail\n");
}

return 0;
}

编译:

1
2
3
4
gcc -g -O0 reverse_demo.c -o reverse_demo_debug
gcc -O0 reverse_demo.c -o reverse_demo_release
cp reverse_demo_release reverse_demo_stripped
strip reverse_demo_stripped

执行:

1
2
3
4
file reverse_demo_debug reverse_demo_release reverse_demo_stripped
nm reverse_demo_debug | grep check
nm reverse_demo_release | grep check
nm reverse_demo_stripped | grep check

提交内容:

1
2
3
4
1. 三个文件的 file 输出
2. 三个文件中 nm 是否能看到 check_password
3. strip 后程序是否还能运行
4. strip 对逆向有什么影响

作业 2:字符串分析

执行:

1
2
strings reverse_demo_stripped
objdump -s -j .rodata reverse_demo_stripped

提交内容:

1
2
3
4
1. 你找到的关键字符串
2. 密码字符串是否还能看到
3. success/fail 是否还能看到
4. 为什么字符串能帮助定位关键逻辑

作业 3:Ghidra 分析 stripped 程序

用 Ghidra 打开 reverse_demo_stripped

要求:

  1. 找到 Defined Strings
  2. 找到 open_sesame
  3. 查看它的 XREF
  4. 跳到引用它的函数
  5. 判断哪个函数是密码检查函数
  6. 给该函数重命名为 check_password
  7. 找到 success/fail 分支

提交内容:

1
2
3
4
5
1. 关键字符串截图或记录
2. 引用 open_sesame 的函数地址
3. 你重命名的函数名
4. 你如何判断成功/失败分支
5. 反编译伪代码中哪些地方可能是工具猜测的

作业 4:动态验证

用 GDB 或 x64dbg 动态调试程序。

Linux GDB 示例:

1
gdb ./reverse_demo_debug
1
2
3
4
5
6
7
break check_password
run
bt
info registers
x/s $rdi
finish
info registers

输入:

1
open_sesame

再测试错误密码。

提交内容:

1
2
3
4
1. 正确密码时 check_password 返回值
2. 错误密码时 check_password 返回值
3. 参数字符串在哪里看到
4. 动态调试是否验证了静态分析结论

35. 自测题

题 1

源代码视角和二进制视角最大的区别是什么?

题 2

符号表的作用是什么?

题 3

strip 会对逆向分析造成什么影响?

题 4

反汇编和反编译有什么区别?

题 5

为什么不能完全相信反编译伪代码?

题 6

字符串为什么是逆向分析的重要入口?

题 7

交叉引用 XREF 是什么?

题 8

拿到一个陌生二进制,你可以按什么基本流程分析?


36. 自测题参考答案

答 1

源代码视角保留函数名、变量名、类型、控制结构和注释等高级信息;二进制视角主要面对机器指令、地址、字符串、导入函数和有限符号,很多高级信息已经丢失或需要推断。

答 2

符号表保存函数、全局变量等对象的名字和地址之间的对应关系,帮助调试器和逆向工具显示更清晰的信息。

答 3

strip 会移除很多普通符号和调试信息,使函数名、变量信息、源码行号等消失,从而增加逆向分析难度,但通常不影响程序运行。

答 4

反汇编把机器码转换成汇编指令,更接近真实执行;反编译尝试把机器码还原成类 C 伪代码,可读性更好,但可能不准确。

答 5

因为反编译器需要猜测变量类型、控制结构和函数边界。优化、混淆、缺少符号等情况都可能导致伪代码不准确。

答 6

字符串常常包含错误信息、成功提示、文件路径、URL、命令、注册表路径等行为线索,可以帮助快速定位关键函数。

答 7

交叉引用表示某个函数、变量或字符串被哪些位置引用。通过 XREF 可以从一个线索跳到使用它的代码位置。

答 8

可以先识别文件格式,再查看字符串和导入函数,然后找入口点和 main,利用字符串和 XREF 找关键函数,最后用调试器动态验证分析结论。


37. 下一节预告

下一节课会讲:

1
进程是什么

你会学习:

  • 程序和进程的区别
  • PID、父进程、子进程
  • 进程状态
  • PCB 的概念
  • Linux fork
  • Windows CreateProcess
  • 为什么进程行为分析是恶意代码分析的重要基础
隐藏
换装
本文作者:burpow
本文链接:https://youthfulnesszxx.github.io/2026/05/28/第06节-从逆向视角看程序结构/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可