接触《深入理解计算机系统》这本书很久了,但一直都处于看的阶段,但懂是一回事,做出来又是另一回事。
于是就试着做了几个实验,发现这个二进制炸弹特别好玩,于是乎就有了此文。
知识储备
调试:
停在地址处: break *0x8012345
打印寄存器: print $eax
显示所有寄存器值:I reg
检测内存值:x /nfu addr
x 检测内存值, n 查看几个内存单元 f进制显示 u显示单字节双字节等。
寄存器:
eiz: 伪寄存器,值始终为0
为什么要用:占位 NOP 可以用来占位,但只是一个字节,长指令比短指令执行快,所以选用其占位。同样的占位指令还有: lea 0x0(%edi) %edi
汇编:
lea 指令:LEA src, dst=>要求src必须为粗出去地址。而寻址方式多种多样。
重要知识点:
switch语句的实现:跳转表
工具:
gdb 调试
objdump –t 查看符号表,即所使用的所有符号。
objdump –d 反汇编
strings 显示程序所用的全部字符串
拆解过程
main函数:
8048a52: e8 a5 07 00 00 call 80491fc <read_line>
8048a57: 83 c4 f4 add $0xfffffff4,%esp
8048a5a: 50 push %eax
8048a5b: e8 c0 00 00 00 call 8048b20 <phase_1>
通过调用库函数readline读入一行数据,字符串地址通过eax返回,然后将字符串传给phase_1函数。
同样,其后几个阶段遵循这种处理方式:读取一行字符串,调用phase_n,传入字符串参数。
阶段一:
阶段1比较简单,判断输入的字符串与程序内部某个字符串是否相当,不等即爆炸。
8048b2c: 68 c0 97 04 08 push $0x80497c0
8048b31: 50 push %eax
8048b32: e8 f9 04 00 00 call 8049030 <strings_not_equal>
所以解题的关键在于,找到程序中的字符串,使用gdb进行调试,打印地址0x80497c0的字符串。
答案显然:Public speaking is very easy.
阶段二:
phase2:
8048b56: 8d 45 e8 lea -0x18(%ebp),%eax
8048b59: 50 push %eax
8048b5a: 52 push %edx
8048b5b: e8 78 04 00 00 call 8048fd8 <read_six_numbers>
8048b60: 83 c4 10 add $0x10,%esp
8048b63: 83 7d e8 01 cmpl $0x1,-0x18(%ebp)
8048b67: 74 05 je 8048b6e <phase_2+0x26>
8048b69: e8 8e 09 00 00 call 80494fc <explode_bomb>
8048b6e: bb 01 00 00 00 mov $0x1,%ebx
8048b73: 8d 75 e8 lea -0x18(%ebp),%esi //esi = array
8048b76: 8d 43 01 lea 0x1(%ebx),%eax //eax = ebx + 1
8048b79: 0f af 44 9e fc imul -0x4(%esi,%ebx,4),%eax //eax = esi + ebx * 4 - 0x4
8048b7e: 39 04 9e cmp %eax,(%esi,%ebx,4) //eax == esi + ebx * 4
8048b81: 74 05 je 8048b88 <phase_2+0x40>
8048b83: e8 74 09 00 00 call 80494fc <explode_bomb>
8048b88: 43 inc %ebx
8048b89: 83 fb 05 cmp $0x5,%ebx
8048b8c: 7e e8 jle 8048b76 <phase_2+0x2e>
1. 输入六个数,通过read_six_sumbers将字符串拆分成6个数,存入 ebp -0x18的位置上。
2. cmpl $0x1,-0x18(%ebp) 如果数组第一个元素不是1,则爆炸。即第一个数要为数字1.
3. 8