惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
P
Privacy & Cybersecurity Law Blog
G
GRAHAM CLULEY
T
The Exploit Database - CXSecurity.com
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Project Zero
Project Zero
S
Security @ Cisco Blogs
TaoSecurity Blog
TaoSecurity Blog
A
Arctic Wolf
Webroot Blog
Webroot Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
Security Latest
Security Latest
H
Heimdal Security Blog
N
News and Events Feed by Topic
N
News | PayPal Newsroom
T
Tor Project blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
The Last Watchdog
The Last Watchdog
Y
Y Combinator Blog
宝玉的分享
宝玉的分享
Scott Helme
Scott Helme
A
About on SuperTechFans
M
MIT News - Artificial intelligence
V
V2EX
V
Visual Studio Blog
Recorded Future
Recorded Future
博客园 - 叶小钗
F
Fortinet All Blogs
L
Lohrmann on Cybersecurity
The GitHub Blog
The GitHub Blog
博客园 - Franky
P
Proofpoint News Feed
MyScale Blog
MyScale Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Secure Thoughts
D
DataBreaches.Net
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 三生石上(FineUI控件)
I
InfoQ
SecWiki News
SecWiki News
Blog — PlanetScale
Blog — PlanetScale
Engineering at Meta
Engineering at Meta
J
Java Code Geeks
B
Blog RSS Feed
AWS News Blog
AWS News Blog
Know Your Adversary
Know Your Adversary
V
Vulnerabilities – Threatpost
H
Help Net Security

Watermelonabc的Blog

福州大学单片机原理与应用(跨学科)复习 福州大学毛概期末复习 x86 汇编语言详解 - 2 CCB 2026 Half-Final Guangzhou 视频也可以含恶意代码吗 Hello,2026 2025 福州大学算法与数据结构复习 2025 福州大学习概期末复习 Frida 入门 记一场贴吧交流 虚拟机保护 2025 强网杯 S9 Reverse 复现 你如何在 FZU 取到快递 2025 福州大学离散数学笔记 2025 福州大学数字逻辑笔记 C# 逆向入门 安卓刷机一点通 Z3-Solver 教程 出题人的 “恶意” 编译器为我做了什么 Hash 系列算法 程序脱壳 Linux 简单教程 安卓逆向介绍 IDA 类型修复 到旗山了
CSAPP Lab2 Note
Watermelonabc · 2025-07-17 · via Watermelonabc的Blog

CASPP 的 LAB 放在 Lab Assignments 上,在 “Self-Study Handout” 出下载对应 LAB 的资源。

课程 Lab 说明:“二进制炸弹” 是一个提供给学生的可执行文件。运行时,它会提示用户输入 6 个不同的字符串。如果这些字符串中有任何一个不正确,炸弹就会 “爆炸” 并打印错误信息。学生必须通过反汇编和逆向自己的炸弹来确定这 6 个字符串应该是什么。这个实验让学生理解汇编语言,并强迫他们学习如何使用调试器(GDB)。这是一个你可以自己尝试的 Linux/x86-64 二进制炸弹。

我们选择的是 “自学” 模块,没有远程服务端检查和扣分机制,可以随意调试。但是,CMU 的同学就会有这些机制,输错了就扣分,所以需要小心调试。

解压 tar 包,得到三个文件 bombbomb.c 和一个 READMEREADME 只说了这是一个 x86-64 程序。

bomb.c 解读

bomb.c 应该就是 bomb 的主代码了。

代码引入了两个自定义库 support.hphases.h,也就是说我们想要的字符串必须从程序中得到。

然后是命令行参数要求,总结如下:

  1. bomb:标准的按行输入
  2. bomb <file>:先从 <file> 中读取输入,结束后再转到标准输入
  3. 不可提供更多命令行参数,否则打印用法

然后就是打印提示信息并要求输入了。整个代码解读完毕,现在转向程序。

bomb 逆向 - GDB

在 Linux 上使用包管理器安装 GDB,使用 gdb bomb 开始调试

基本操作:

bomb 逆向 - IDA

这个 Lab 是需要在 Linux 环境下运行的,不过我们在 Windows 上,所以先用 IDA 分析一下。这里可以用 bomb.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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
int __fastcall main(int argc, const char **argv)
{
const char *line;
const char *v3;
const char *v4;
const char *v5;
const char *v6;
const char *v7;

if ( argc == 1 )
{
infile = (FILE *)stdin;
}
else
{
if ( argc != 2 )
{
__printf_chk(1, "Usage: %s [<input_file>]\n", *argv);
exit(8);
}
infile = fopen(argv[1], "r");
if ( !infile )
{
__printf_chk(1, "%s: Error: Couldn't open %s\n", *argv, argv[1]);
exit(8);
}
}
initialize_bomb();
puts("Welcome to my fiendish little bomb. You have 6 phases with");
puts("which to blow yourself up. Have a nice day!");
line = read_line();
phase_1(line);
phase_defused();
puts("Phase 1 defused. How about the next one?");
v3 = read_line();
phase_2(v3);
phase_defused();
puts("That's number 2. Keep going!");
v4 = read_line();
phase_3(v4);
phase_defused();
puts("Halfway there!");
v5 = read_line();
phase_4(v5);
phase_defused();
puts("So you got that one. Try this one.");
v6 = read_line();
phase_5((__int64)v6);
phase_defused();
puts("Good work! On to the next...");
v7 = read_line();
phase_6(v7);
phase_defused();
return 0;
}

先来看 Phase1,点进 phase_1(line),即可得到内容:

1
result = strings_not_equal(line, "Border relations with Canada have never been better.");

Phase1 就是 Border relations with Canada have never been better.

接下来是 Phase2。有了 Phase1 的经验,我们点开 phase_2(v5)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
__int64 __fastcall phase_2(__int64 a1)
{
__int64 result;
char *v2;
int v3;
char v4;
char v5;

read_six_numbers(a1, &v3);
if ( v3 != 1 )
explode_bomb();
v2 = &v4;
do
{
result = (unsigned int)(2 * *((_DWORD *)v2 - 1));
if ( *(_DWORD *)v2 != (_DWORD)result )
explode_bomb();
v2 += 4;
}
while ( v2 != &v5 );
return result;
}

先看第一个读取函数 read_six_numbers(a1, &v3)

1
2
3
4
5
6
7
8
9
__int64 __fastcall read_six_numbers(__int64 a1, __int64 a2)
{
__int64 n5;

n5 = __isoc99_sscanf(a1, "%d %d %d %d %d %d", a2, a2 + 4, a2 + 1, a2 + 12, a2 + 2, a2 + 20);
if ( (int)n5 <= 5 )
explode_bomb();
return n5;
}

核心就是 __isoc99_sscanf。查询函数定义[1] 可知,这里是从我们输入的 Phase2 中读取格式化的 “%d %d %d %d %d %d” 并写入 v3[i]

我们可以在这里修复一下函数定义,修复后的 phase_2(v5)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int __fastcall phase_2(const char *a1)
{
int result;
int *v2;
int v3[6];
int v4;

read_six_numbers(a1, v3);
if ( v3[0] != 1 )
explode_bomb();
v2 = &v3[1];
do
{
result = 2 * *(v2 - 1);
if ( *v2 != result )
explode_bomb();
++v2;
}
while ( v2 != &v4 );
return result;
}

可以知道,v3[0] ,即第一个输入值必然是 1v2 对应第二个输入值的地址。

然后进入一个 do-while 循环,循环条件是 v2 不等于 v4地址

循环体的逻辑是计算 2 * v2[i-1] 的值,然后和 v2[i] 进行比较。如果不相等,就引爆炸弹。然后移动到下一个元素。

v4 和我们的输入无关且分配内存时接在 v3 之后,基于内存连续性,我们认为数组 v3 结束于地址 v4


  1. C 库函数 int sscanf ↩︎