手机网站 标题长度,wordpress多个菜单,lamp wordpress,网站建设方向市场分析CSAPP - bomblab phase_2 分析 文章目录 CSAPP - bomblab phase_2 分析概要第一次反编译 phase_2()反编译 explode_bomb()反编译 read_six_numbers()第二次反编译 phase_2整理#xff1a; 合并所有反编译出的代码 概要
bomblab phase_2 的答案#xff0c;网络上相关的文章、…CSAPP - bomblab phase_2 分析 文章目录 CSAPP - bomblab phase_2 分析概要第一次反编译 phase_2()反编译 explode_bomb()反编译 read_six_numbers()第二次反编译 phase_2整理 合并所有反编译出的代码 概要
bomblab phase_2 的答案网络上相关的文章、视频有不少了。不过反汇编这件事情只看别人答案还是没法覆盖一个汇编小白的技术盲点的真实的反汇编过程也往往不是想答案那样一次性顺畅得到结果的。
对于 phase_2, 我尝试翻译 phase_2 的汇编代码到 C 代码 第一次失败了临时变量分配栈空间不熟悉并且遇到了一些疑问
explode_bomb() 调用了好几次为什么随便输入一个错的输入炸弹只爆炸一次read_six_numbers() 的输入参数是怎样的第二个参数必须是6个int大小吗
实际上直觉仅仅是提供了猜想方向实际工程中很多 bug 的排查如果相信了函数名字就不需要排查 bug 了。要利用汇编知识和调试技能充分验证你的猜想恐怕这才是 bomblab 的意义 而 “输入的数字是6个第一个数字是1并且是公比为2的等比数列”这个答案是在执行了反编译后自然而然的答案。即使 read_six_numbers() 故意改为 read_seven_numbers() 我依然可以分析得出正确答案。
第一次反编译 phase_2()
(gdb) disassemble phase_2
Dump of assembler code for function phase_2: // phase_2(char* input) {0x0000000000400efc 0: push rbp //0x0000000000400efd 1: push rbx //0x0000000000400efe 2: sub rsp,0x28 //0x0000000000400f02 6: mov rsi,rsp // ??0x0000000000400f05 9: call 0x40145c read_six_numbers // read_six_numbers(input, ??);0x0000000000400f0a 14: cmp DWORD PTR [rsp],0x1 // if (*rsp 1)0x0000000000400f0e 18: je 0x400f30 phase_252 // goto label520x0000000000400f10 20: call 0x40143a explode_bomb // explode_bomb();0x0000000000400f15 25: jmp 0x400f30 phase_252 // goto label52;0x0000000000400f17 27: mov eax,DWORD PTR [rbx-0x4] // eax *(rbx - 4);0x0000000000400f1a 30: add eax,eax // eax * 2;0x0000000000400f1c 32: cmp DWORD PTR [rbx],eax // if (*rbx eax)0x0000000000400f1e 34: je 0x400f25 phase_241 // goto label41;0x0000000000400f20 36: call 0x40143a explode_bomb // explode_bomb()0x0000000000400f25 41: add rbx,0x4 // rbx 40x0000000000400f29 45: cmp rbx,rbp // if (rbx ! rbp)0x0000000000400f2c 48: jne 0x400f17 phase_227 // goto label270x0000000000400f2e 50: jmp 0x400f3c phase_264 // goto label640x0000000000400f30 52: lea rbx,[rsp0x4] // char* rbx rsp 40x0000000000400f35 57: lea rbp,[rsp0x18] // char* rbp rsp 240x0000000000400f3a 62: jmp 0x400f17 phase_227 // goto label270x0000000000400f3c 64: add rsp,0x28 //0x0000000000400f40 68: pop rbx //0x0000000000400f41 69: pop rbp //0x0000000000400f42 70: ret // return
End of assembler dump.很明显 逐句翻译汇编代码遇到了瓶颈。对于汇编小白这很正常。先记录下来逐一击破
sub rsp, 0x28 和 mov rsi, rsp 是在干啥explode_bomb() 调用了多次吗随便输入一组数据为啥只输出一次 BOMB!!!read_six_numbers() 是在干啥会不会暗藏炸弹
反编译 explode_bomb()
在分析 phase_2 的汇编代码时 发现调用了多次 explode_bomb. 比如准备一个非法的输入 0 1 2 3 4 5 在仅仅查看 phase_2 汇编代码情况下直觉会认为应该输出多次 BOOM!!!
然而实际只输出一次。这不免让人疑惑explode_bomb() 里调用了 exit 吗 反汇编看看:
(gdb) disassemble explode_bomb
Dump of assembler code for function explode_bomb: // void explode_bomb() {0x000000000040143a 0: sub rsp,0x8 //0x000000000040143e 4: mov edi,0x4025a3 // x /s 0x4025a3 得到 \nBOOM!!!0x0000000000401443 9: call 0x400b10 putsplt // puts(\nBOOM!!!);0x0000000000401448 14: mov edi,0x4025ac // x /s 0x4025ac 得到 The bomb has blown up.0x000000000040144d 19: call 0x400b10 putsplt // puts(The bomb has blown up.);0x0000000000401452 24: mov edi,0x8 // int t 8;0x0000000000401457 29: call 0x400c20 exitplt // exit(t);
End of assembler dump.C代码整理一下
void explode_bomb()
{puts(\nBOOM!!!);puts(The bomb has blown up.);exit(8);
}反编译 read_six_numbers() 0x0000000000400e44 164: mov edi,0x4023a80x0000000000400e49 169: call 0x400b10 putsplt0x0000000000400e4e 174: call 0x40149e read_line0x0000000000400e53 179: mov rdi,rax0x0000000000400e56 182: call 0x400efc phase_2Dump of assembler code for function phase_2:0x0000000000400efc 0: push rbp0x0000000000400efd 1: push rbx0x0000000000400efe 2: sub rsp,0x280x0000000000400f02 6: mov rsi,rsp0x0000000000400f05 9: call 0x40145c read_six_numbers上述两段汇编用来确认 read_six_numbers() 的第一个输入是 char* input。
(gdb) disassemble read_six_numbers
Dump of assembler code for function read_six_numbers: // void read_six_numbers(char* input, int* data) {0x000000000040145c 0: sub rsp,0x18 // int* temp[3];0x0000000000401460 4: mov rdx,rsi // int* c0 data;0x0000000000401463 7: lea rcx,[rsi0x4] // int* c1 data[1];0x0000000000401467 11: lea rax,[rsi0x14] // int* c5 data[5];0x000000000040146b 15: mov QWORD PTR [rsp0x8],rax // temp[1] c5;0x0000000000401470 20: lea rax,[rsi0x10] // int* c4 data[4];0x0000000000401474 24: mov QWORD PTR [rsp],rax // temp[0] c4;0x0000000000401478 28: lea r9,[rsi0xc] // int* c3 data[3]0x000000000040147c 32: lea r8,[rsi0x8] // int* c2 data[2]0x0000000000401480 36: mov esi,0x4025c3 // x /x 0x4025c3 结果为 %d %d %d %d %d %d0x0000000000401485 41: mov eax,0x0 // int ret 00x000000000040148a 46: call 0x400bf0 __isoc99_sscanfplt // int ret sscanf(input, %d %d %d %d %d %d, c0, c1, c2, c3, temp[0], temp[1]);0x000000000040148f 51: cmp eax,0x5 // if (ret 5)0x0000000000401492 54: jg 0x401499 read_six_numbers61 // goto label610x0000000000401494 56: call 0x40143a explode_bomb // explode_bomb()0x0000000000401499 61: add rsp,0x18 //0x000000000040149d 65: ret // }
End of assembler dump.x86_64 架构中用于传递函数参数时如果参数都是整数或整数指针那么
rdi第一个整数参数 rsi第二个整数参数 rdx第三个整数参数 rcx第四个整数参数 r8第五个整数参数 r9第六个整数参数
第七个和第八个参数用栈传递那么这两个参数各自占用的栈空间都是 64 位。
这是因为 x86_64 架构中栈上的数据总是以 64 位为单位对齐的。这意味着即使您传递一个 32 位的参数它在栈上也会占用 64 位的空间。
因此第七个和第八个参数用栈传递那么这两个参数各自占用的栈空间都是 64 位。
正因如此 上述反编译过程中 sscanf() 的最后两个参数才得以确认。
void read_six_numbers(char* input, int* data)
{int* temp[3];int*c0 data;int* c1 data[1];int* c5 data[5];temp[1] c5;int* c4 data[4];temp[0] c4;int* c3 data[3];int* c2 data[2];int ret sscanf(input, %d %d %d %d %d %d, c0, c1, c2, c3, temp[0], temp[1]);if (ret 5){return;}explode_bomb();
}第二次反编译 phase_2
这里涉及到了栈的增长。在 x86_64 中栈底位于高地址栈顶位于低地址栈增长意味着栈顶向栈底的相反方向延伸 也就是说新的栈顶的地址值是变小了也就是做减法。rsp 表示栈顶指针。
于是乎可以理解sub rsp, 0x28 是说栈空间增加 0x28 (40) 字节。这40字节用来存放临时变量。
栈的初始状态
-----------------
| | - 栈顶 (rsp)
-----------------
| |
-----------------
| |
-----------------
| |
-----------------
| |
-----------------
| |
-----------------
| | - 栈底 (高地址)
-----------------执行 sub rsp, 0x28 后的状态
-----------------
| 未使用空间 | - 新的栈顶 (rsp - 0x28)
-----------------
| |
-----------------
| |
-----------------
| |
-----------------
| 预留空间 | - 旧的栈顶 (rsp)
-----------------
| |
-----------------
| |
-----------------
| | - 栈底 (高地址)
-----------------栈增长方向向下地址减小
请注意栈上的 “未使用空间” 现在变成了 “预留空间”用于存储局部变量或为函数调用做准备。这块空间的大小是 0x28 (40字节)。
(gdb) disassemble phase_2
Dump of assembler code for function phase_2: // phase_2(char* input) {0x0000000000400efc 0: push rbp //0x0000000000400efd 1: push rbx //0x0000000000400efe 2: sub rsp,0x28 // 临时变量使用。可以先看做 char mem[40];0x0000000000400f02 6: mov rsi,rsp // 无法确定 read_six_numbers() 第二个参数是什么盲猜是 int data[10];0x0000000000400f05 9: call 0x40145c read_six_numbers // read_six_numbers(input, data);0x0000000000400f0a 14: cmp DWORD PTR [rsp],0x1 // if (data[0] 1)0x0000000000400f0e 18: je 0x400f30 phase_252 // goto label520x0000000000400f10 20: call 0x40143a explode_bomb // explode_bomb();0x0000000000400f15 25: jmp 0x400f30 phase_252 // goto label52;0x0000000000400f17 27: mov eax,DWORD PTR [rbx-0x4] // int t *(p1-1);0x0000000000400f1a 30: add eax,eax // t * 2;0x0000000000400f1c 32: cmp DWORD PTR [rbx],eax // if (*p1 t)0x0000000000400f1e 34: je 0x400f25 phase_241 // goto label41;0x0000000000400f20 36: call 0x40143a explode_bomb // explode_bomb()0x0000000000400f25 41: add rbx,0x4 // p1;0x0000000000400f29 45: cmp rbx,rbp // if (p1 ! p2)0x0000000000400f2c 48: jne 0x400f17 phase_227 // goto label270x0000000000400f2e 50: jmp 0x400f3c phase_264 // goto label640x0000000000400f30 52: lea rbx,[rsp0x4] // int* p1 data[1];0x0000000000400f35 57: lea rbp,[rsp0x18] // int* p2 data[6]; // 0x18 168 24, 24/460x0000000000400f3a 62: jmp 0x400f17 phase_227 // goto label270x0000000000400f3c 64: add rsp,0x28 //0x0000000000400f40 68: pop rbx //0x0000000000400f41 69: pop rbp //0x0000000000400f42 70: ret // return
End of assembler dump.void phase_2(char* input)
{int data[10];read_six_numbers(input, data);if (data[0] 1){goto label52;}explode_bom();goto label52;
label27:int t *(p1-1);t * 2;if (*p1 t){goto label41;}explode_bomb();
label41:p1;if (p1 ! p2){goto label27;}goto label64;
label52:int* p1 data[1];int* p2 data[6];goto label27;
label64:return;
}void phase_2(char* input)
{int data[10];read_six_numbers(input, data);if (data[0] 1){goto label52;}explode_bom();label52:int* p1 data[1];int* p2 data[6];label27:int t *(p1-1);t * 2;if (*p1 t){goto label41;}explode_bomb();
label41:p1;if (p1 ! p2){goto label27;}goto label64;label64:return;
}void phase_2(char* input)
{int data[10];read_six_numbers(input, data);if (data[0] ! 1){explode_bomb();}int* p1 data[1];int* p2 data[6];while (true){int t *(p1-1);if (*p1 ! t){explode_bomb();}p1;if (p1 p2){return;}}
}整理 合并所有反编译出的代码
void explode_bomb()
{puts(\nBOOM!!!);puts(The bomb has blown up.);exit(8);
}void read_six_numbers(char* input, int* data)
{int* temp[3];int*c0 data;int* c1 data[1];int* c5 data[5];temp[1] c5;int* c4 data[4];temp[0] c4;int* c3 data[3];int* c2 data[2];int ret sscanf(input, %d %d %d %d %d %d, c0, c1, c2, c3, temp[0], temp[1]);if (ret 5){return;}explode_bomb();
}void phase_2(char* input)
{int data[10];read_six_numbers(input, data);if (data[0] ! 1){explode_bomb();}int* p1 data[1];int* p2 data[6];while (true){int t *(p1-1);if (*p1 ! t){explode_bomb();}p1;if (p1 p2){return;}}
}