本文主要是介绍CSAPP - 反编译 initialize_bomb(),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
CSAPP - 保持好奇,反汇编 initialize_bomb()
相比于直接看 bomblab phase_1 的答案,我更想搞懂答案之外涉及的每个函数的反汇编 - 反正是一个实验,代码能复杂到哪里去? 而搞懂这些函数, 无疑对于实际工程中的各种 debug 问题, 能补全基本的 gdb 调试技能。这一篇是分析 initialize_bomb() 函数.
好奇 - Ctrl-C 被接管了?
查看 bomb.c 可以看到,除了读取文件、打印提示信息,在 phase_1() 之前还做了一件事:初始化 bomb:
int main()
{.../* Do all sorts of secret stuff that makes the bomb harder to defuse. */initialize_bomb(); // 好奇这里read_line();phase_1();...
}
如果是第一次运行 bomb, 输入字符串后希望临时退出, 会发现 Ctrl+C 并不能立即退出。原因是 initialize_bomb() 里使用了信号量,捕获了 Ctrl+C。 当然,这是一个猜测,需要从汇编代码验证。 和上一篇的方式一样,先获取汇编代码, 再逐句翻译, 随后整理和简化C代码。
反汇编 initialize_bomb() - 翻译出来了,但是很懵
(gdb) disassemble initialize_bomb
Dump of assembler code for function initialize_bomb: // void initialize_bomb() {0x00000000004013a2 <+0>: sub rsp,0x8 //0x00000000004013a6 <+4>: mov esi,0x4012a0 // void* p2 = 0x4012a0;0x00000000004013ab <+9>: mov edi,0x2 // int p1 = 2;0x00000000004013b0 <+14>: call 0x400b90 <signal@plt> // signal(p1, p2);0x00000000004013b5 <+19>: add rsp,0x8 //0x00000000004013b9 <+23>: ret // }
End of assembler dump.
于是踉踉跄跄的写出 C 代码:
void initialize_bomb()
{void* p2 = 0x4012a0;signal(2, p2);
}
其中 void* p2 = 0x4012a0
让人费解, 需要结合 signal 的函数原型分析:
man signalNAMEsignal - ANSI C signal handlingSYNOPSIS#include <signal.h>typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);
可以发现, signal() 第二个参数是一个函数指针, 对应到 initialize_bomb() 函数中, 0x4012a0 是这个函数指针的值, 查看其汇编代码:
神奇的 0x4012a0 - 来反汇编吧!
(gdb) disassemble 0x4012a0
Dump of assembler code for function sig_handler: // sig_handler() {0x00000000004012a0 <+0>: sub rsp,0x8 //0x00000000004012a4 <+4>: mov edi,0x4024c0 // const char* p1 = (char*)0x4024c0; , 用 x /s 查看知道是 "So you think you can stop the bomb with ctrl-c, do you?"0x00000000004012a9 <+9>: call 0x400b10 <puts@plt> // puts(p1);0x00000000004012ae <+14>: mov edi,0x3 // int p2 = 3;0x00000000004012b3 <+19>: call 0x400c50 <sleep@plt> // sleep(p2);0x00000000004012b8 <+24>: mov esi,0x402582 // int p3 = 0x402582; 0x402582 值为 "Well..."0x00000000004012bd <+29>: mov edi,0x1 // int p4 = 1;0x00000000004012c2 <+34>: mov eax,0x0 // int ret = 0;0x00000000004012c7 <+39>: call 0x400c00 <__printf_chk@plt> // __printf_chk(p4, p3); __printf_chk 是 printf 的安全版本,会检查格式字符串有效性0x00000000004012cc <+44>: mov rdi,QWORD PTR [rip+0x20246d] # 0x603740 <stdout@@GLIBC_2.2.5>0x00000000004012d3 <+51>: call 0x400be0 <fflush@plt> // fflush(stdou);0x00000000004012d8 <+56>: mov edi,0x1 // int p5 = 1;0x00000000004012dd <+61>: call 0x400c50 <sleep@plt> // sleep(p5);0x00000000004012e2 <+66>: mov edi,0x40258a // const char* p6 = (char*)0x40258a; "OK. :-)"0x00000000004012e7 <+71>: call 0x400b10 <puts@plt> // puts(p6);0x00000000004012ec <+76>: mov edi,0x10 // int p7 = 16;0x00000000004012f1 <+81>: call 0x400c20 <exit@plt> // exit(p7); // 返回16
End of assembler dump.
这段代码,相比于 phase_1 本身的代码,有意思的多。
首先我们验证, 当启动 bomb 程序后,输入 Ctrl+C, 并等待5秒左右,是否会退出,返回值是什么:
zz@Legion-R7000P% ./bomb
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
^CSo you think you can stop the bomb with ctrl-c, do you?
Well...OK. :-)
zz@Legion-R7000P% echo $?
16
返回值的确是16,和直接从汇编代码看到的一致。
对应的C代码,整理一下:
void sig_handler(int )
{puts("So you think you can stop the bomb with ctrl-c, do you?");sleep(3);printf("Well...");fflush(stdout);sleep(1);puts("OK. :-)");exit(16);
}void initialize_bomb()
{signal(2, sig_handler);
}
验证 - 捕获 Ctrl-C, 是这么玩的吗?
首先拿出这几次反汇编中,人工写出的C代码:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <signal.h>int string_length(const char* str)
{for (const char* p = str; ; p++){int ret = p - str;if (*p == 0){return ret;}}
}int strings_not_equal(const char* s1, const char* s2)
{const char* p1 = s1;const char* p2 = s2;int len1 = string_length(s1);int len2 = string_length(s2);if (len1 != len2){return 1;}while (true){char c1 = *p1;if (c1 =='\0'){return 0;}if (c1 != *p2){return 1;}p1++;p2++;}
}void sig_handler(int)
{puts("So you think you can stop the bomb with ctrl-c, do you?");sleep(3);printf("Well...");fflush(stdout);sleep(1);puts("OK. :-)");exit(16);
}void initialize_bomb()
{signal(2, sig_handler);
}
然后添加一小段调用代码, 也就是 main() 函数: 每隔1秒打印一个 hello world N, N 是递增的数字。 而在打印 hello world N 的过程中, 如果按下了 Ctrl+C, 就会捕获到信号,打印出和 bomblab 一样的输出,并最终结束:
int main()
{initialize_bomb();int i = 0;while (true){printf("hello world, %d\n", i);i += 1;sleep(1);}return 0;
}
运行一下, 的确如此, 就是这么玩的:
zz@Legion-R7000P% gcc test.c
zz@Legion-R7000P% ./a.out
hello world, 0
hello world, 1
hello world, 2
^CSo you think you can stop the bomb with ctrl-c, do you?
Well...OK. :-)
总结
- 保持好奇, 保持一定可以搞清楚的信念, 汇编之下, 了无秘密。
x /s 0x4024c0
命令再次被使用,使用了好几次, gdb 命令得到了强化- 看到往 rsi 寄存器(函数第二个参数)存入莫名奇妙的数字,不要慌,它就是一个函数的地址, 照常去 disas 它,没有难度, sig_handler() 很简单
- printf 和 __printf_chk, 这里确实是可以忽略和猜测的函数
- 整理好每一个反汇编出来的函数, 然后按自己想法,添加 main 函数去验证, bomb lab 的神秘性一点一点被拨开。
这篇关于CSAPP - 反编译 initialize_bomb()的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!