官网 http://csapp.cs.cmu.edu/3e/labs.html
Students implement simple logical, two’s complement, and floating point functions, but using a highly restricted subset of C. For example, they might be asked to compute the absolute value of a number using only bit-level operations and straightline code. This lab helps students understand the bit-level representations of C data types and the bit-level behavior of the operations on data.
参考 《深入理解计算机系统》(CSAPP)实验二 —— Bomb Lab
csapp(3e)的bomblab的phase_6详解(没有详细到逐行解析的程度)
准备工作 bomb.c是本实验的源码,一共有phase_1 ~ phase_6 6个炸弹,从命令行输入的内容必须要和phase函数中的一致,否则就会爆炸退出程序。因为phase函数并没有给出源码,所以无法得知其期望的字符串是什么。
bomb文件是编译完成的文件,反汇编下这个文件,从反汇编推算下其内容是什么。
objdump -d bomb > bomb.asm
从main函数开始分析。
0000000000400 da0 <main>: 400 da0: 53 push %rbx # saved register 400 da1: 83 ff 01 cmp $0x1 ,%edi # if(argc==1) 400 da4: 75 10 jne 400 db6 <main+0x16 > # 不相等 跳到400db6 400 da6: 48 8 b 05 9 b 29 20 00 mov 0x20299b (%rip),%rax # 603748 <stdin@@GLIBC_2.2.5> 400 dad: 48 89 05 b4 29 20 00 mov %rax,0x2029b4 (%rip) # 603768 <infile> 400 db4: eb 63 jmp 400e19 <main+0x79 > # 跳到initialize_bomb 400 db6: 48 89 f3 mov %rsi,%rbx 400 db9: 83 ff 02 cmp $0x2 ,%edi # if(argc==2) 400 dbc: 75 3 a jne 400 df8 <main+0x58 > # 不相等 跳到400df8 400 dbe: 48 8 b 7 e 08 mov 0x8 (%rsi),%rdi 400 dc2: be b4 22 40 00 mov $0x4022b4 ,%esi 400 dc7: e8 44 fe ff ff callq 400 c10 <fopen@plt> 400 dcc: 48 89 05 95 29 20 00 mov %rax,0x202995 (%rip) # 603768 <infile> 400 dd3: 48 85 c0 test %rax,%rax 400 dd6: 75 41 jne 400e19 <main+0x79 > # 跳到initialize_bomb 400 dd8: 48 8 b 4 b 08 mov 0x8 (%rbx),%rcx 400 ddc: 48 8 b 13 mov (%rbx),%rdx 400 ddf: be b6 22 40 00 mov $0x4022b6 ,%esi 400 de4: bf 01 00 00 00 mov $0x1 ,%edi 400 de9: e8 12 fe ff ff callq 400 c00 <__printf_chk@plt> 400 dee: bf 08 00 00 00 mov $0x8 ,%edi 400 df3: e8 28 fe ff ff callq 400 c20 <exit@plt> 400 df8: 48 8 b 16 mov (%rsi),%rdx 400 dfb: be d3 22 40 00 mov $0x4022d3 ,%esi 400e00 : bf 01 00 00 00 mov $0x1 ,%edi 400e05 : b8 00 00 00 00 mov $0x0 ,%eax 400e0 a: e8 f1 fd ff ff callq 400 c00 <__printf_chk@plt> 400e0 f: bf 08 00 00 00 mov $0x8 ,%edi 400e14 : e8 07 fe ff ff callq 400 c20 <exit@plt> 400e19 : e8 84 05 00 00 callq 4013 a2 <initialize_bomb> 400e1 e: bf 38 23 40 00 mov $0x402338 ,%edi 400e23 : e8 e8 fc ff ff callq 400 b10 <puts@plt> 400e28 : bf 78 23 40 00 mov $0x402378 ,%edi 400e2 d: e8 de fc ff ff callq 400 b10 <puts@plt> 400e32 : e8 67 06 00 00 callq 40149 e <read_line> # 读取输入 400e37 : 48 89 c7 mov %rax,%rdi 400e3 a: e8 a1 00 00 00 callq 400 ee0 <phase_1> # 调用phase_1 400e3 f: e8 80 07 00 00 callq 4015 c4 <phase_defused> # 接触phase_1 400e44 : bf a8 23 40 00 mov $0x4023a8 ,%edi 400e49 : e8 c2 fc ff ff callq 400 b10 <puts@plt> 400e4 e: e8 4 b 06 00 00 callq 40149 e <read_line> 400e53 : 48 89 c7 mov %rax,%rdi 400e56 : e8 a1 00 00 00 callq 400 efc <phase_2> 400e5 b: e8 64 07 00 00 callq 4015 c4 <phase_defused> 400e60 : bf ed 22 40 00 mov $0x4022ed ,%edi 400e65 : e8 a6 fc ff ff callq 400 b10 <puts@plt> 400e6 a: e8 2 f 06 00 00 callq 40149 e <read_line> 400e6 f: 48 89 c7 mov %rax,%rdi 400e72 : e8 cc 00 00 00 callq 400 f43 <phase_3> 400e77 : e8 48 07 00 00 callq 4015 c4 <phase_defused> 400e7 c: bf 0 b 23 40 00 mov $0x40230b ,%edi 400e81 : e8 8 a fc ff ff callq 400 b10 <puts@plt> 400e86 : e8 13 06 00 00 callq 40149 e <read_line> 400e8 b: 48 89 c7 mov %rax,%rdi 400e8 e: e8 79 01 00 00 callq 40100 c <phase_4> 400e93 : e8 2 c 07 00 00 callq 4015 c4 <phase_defused> 400e98 : bf d8 23 40 00 mov $0x4023d8 ,%edi 400e9 d: e8 6 e fc ff ff callq 400 b10 <puts@plt> 400 ea2: e8 f7 05 00 00 callq 40149 e <read_line> 400 ea7: 48 89 c7 mov %rax,%rdi 400 eaa: e8 b3 01 00 00 callq 401062 <phase_5> 400 eaf: e8 10 07 00 00 callq 4015 c4 <phase_defused> 400 eb4: bf 1 a 23 40 00 mov $0x40231a ,%edi 400 eb9: e8 52 fc ff ff callq 400 b10 <puts@plt> 400 ebe: e8 db 05 00 00 callq 40149 e <read_line> 400 ec3: 48 89 c7 mov %rax,%rdi 400 ec6: e8 29 02 00 00 callq 4010 f4 <phase_6> 400 ecb: e8 f4 06 00 00 callq 4015 c4 <phase_defused> 400 ed0: b8 00 00 00 00 mov $0x0 ,%eax 400 ed5: 5 b pop %rbx 400 ed6: c3 retq
说明主要还是看phase_1~phase_6函数。
phase_1 0000000000400 ee0 <phase_1>: 400 ee0: 48 83 ec 08 sub $0x8 ,%rsp # 栈指针 0x8 400 ee4: be 00 24 40 00 mov $0x402400 ,%esi # 传参 400 ee9: e8 4 a 04 00 00 callq 401338 <strings_not_equal> # 判断字符串是否相等 400 eee: 85 c0 test %eax,%eax # 测试返回结果 400 ef0: 74 05 je 400 ef7 <phase_1+0x17 > 400 ef2: e8 43 05 00 00 callq 40143 a <explode_bomb> # 如果结果不对,调用炸弹爆炸 400 ef7: 48 83 c4 08 add $0x8 ,%rsp # 释放空间 400 efb: c3 retq
在内存为0x402400的地方存储的就是程序期望我们输入的字符串,那么利用GDB工具调试下代码,打印0x402400处的值看下。得到正解。
Border relations with Canada have never been better.
phase_2 0000000000400 efc <phase_2>: 400 efc: 55 push %rbp 400 efd: 53 push %rbx 400 efe: 48 83 ec 28 sub $0x28 ,%rsp # 栈指针 0x28 400 f02: 48 89 e6 mov %rsp,%rsi 400 f05: e8 52 05 00 00 callq 40145 c <read_six_numbers> # 调用 读入六个数字 400 f0a: 83 3 c 24 01 cmpl $0x1 ,(%rsp) # 比较1和(%rsp)的值 400 f0e: 74 20 je 400 f30 <phase_2+0x34 > # 相等就继续400f30 400 f10: e8 25 05 00 00 callq 40143 a <explode_bomb> # explode_bomb 400 f15: eb 19 jmp 400 f30 <phase_2+0x34 > # 跳到400f30 400 f17: 8 b 43 fc mov -0x4 (%rbx),%eax 400 f1a: 01 c0 add %eax,%eax 400 f1c: 39 03 cmp %eax,(%rbx) 400 f1e: 74 05 je 400 f25 <phase_2+0x29 > # 相等就继续400f25 400 f20: e8 15 05 00 00 callq 40143 a <explode_bomb> # explode_bomb 400 f25: 48 83 c3 04 add $0x4 ,%rbx 400 f29: 48 39 eb cmp %rbp,%rbx 400 f2c: 75 e9 jne 400 f17 <phase_2+0x1b > # 不相等就跳到400f17 400 f2e: eb 0 c jmp 400 f3c <phase_2+0x40 > # 跳到400f3c,结束phase_2 400 f30: 48 8 d 5 c 24 04 lea 0x4 (%rsp),%rbx 400 f35: 48 8 d 6 c 24 18 lea 0x18 (%rsp),%rbp 400 f3a: eb db jmp 400 f17 <phase_2+0x1b > # 跳到400f17 400 f3c: 48 83 c4 28 add $0x28 ,%rsp # 释放空间 400 f40: 5 b pop %rbx 400 f41: 5 d pop %rbp 400 f42: c3 retq
伪代码如下:
phase_2: if ((%rsp)==1 ) { goto 400f 30; 400f 17: %eax = (%rbx - 4 ); %eax *= 2 ; if ((%rbx) == %eax) %rbx += 0x4 ; if (%rbx == %rbp) return ; else goto 400f 17; } else explode_bomb; } else explode_bomb;400f 30:%rbx = %rsp + 0x4 ; %rbp = %rsp + 0x18 ; #结束条件存放在%rsp + 0x18 goto 400f 17;
程序执行顺序大概是:
设%rsp = 0x400000 , 0x400000 的值是1 则%rbx = 0x400004 , %rbp = 0x400018 %eax = 0x400000 的值 = 0x1 %eax = 0x2 0x400004 的数据 == 0x2 %rbx = 0x400008 0x400008 != 0x400018 继续%eax = 0x400004 的值 = 0x2 %eax = 0x4 0x400008 的数据 == 0x4 %rbx = 0x40000c ...... 继续 (第六次)0x400018 == 0x400018 return
所以一共输入六个数,第一个数为1,后面的数依次为前一个数的两倍。
1 2 4 8 16 32
phase_3 0000000000400 f43 <phase_3>: 400 f43: 48 83 ec 18 sub $0x18 ,%rsp # 栈指针 0x18 400 f47: 48 8 d 4 c 24 0 c lea 0xc (%rsp),%rcx 400 f4c: 48 8 d 54 24 08 lea 0x8 (%rsp),%rdx 400 f51: be cf 25 40 00 mov $0x4025cf ,%esi 400 f56: b8 00 00 00 00 mov $0x0 ,%eax 400 f5b: e8 90 fc ff ff callq 400 bf0 <__isoc99_sscanf@plt> 400 f60: 83 f8 01 cmp $0x1 ,%eax 400 f63: 7 f 05 jg 400 f6a <phase_3+0x27 > # 大于 跳到400f6a 400 f65: e8 d0 04 00 00 callq 40143 a <explode_bomb> 400 f6a: 83 7 c 24 08 07 cmpl $0x7 ,0x8 (%rsp) # 比较7和(%rdx) 400 f6f: 77 3 c ja 400 fad <phase_3+0x6a > # explode_bomb 400 f71: 8 b 44 24 08 mov 0x8 (%rsp),%eax 400 f75: ff 24 c5 70 24 40 00 jmpq *0x402470 (,%rax,8 ) # 间接跳转 400 f7c: b8 cf 00 00 00 mov $0xcf ,%eax 400 f81: eb 3 b jmp 400 fbe <phase_3+0x7b > # 跳到400fbe 400 f83: b8 c3 02 00 00 mov $0x2c3 ,%eax 400 f88: eb 34 jmp 400 fbe <phase_3+0x7b > 400 f8a: b8 00 01 00 00 mov $0x100 ,%eax 400 f8f: eb 2 d jmp 400 fbe <phase_3+0x7b > 400 f91: b8 85 01 00 00 mov $0x185 ,%eax 400 f96: eb 26 jmp 400 fbe <phase_3+0x7b > 400 f98: b8 ce 00 00 00 mov $0xce ,%eax 400 f9d: eb 1 f jmp 400 fbe <phase_3+0x7b > 400 f9f: b8 aa 02 00 00 mov $0x2aa ,%eax 400 fa4: eb 18 jmp 400 fbe <phase_3+0x7b > 400 fa6: b8 47 01 00 00 mov $0x147 ,%eax 400 fab: eb 11 jmp 400 fbe <phase_3+0x7b > 400 fad: e8 88 04 00 00 callq 40143 a <explode_bomb> 400 fb2: b8 00 00 00 00 mov $0x0 ,%eax 400 fb7: eb 05 jmp 400 fbe <phase_3+0x7b > 400 fb9: b8 37 01 00 00 mov $0x137 ,%eax 400 fbe: 3 b 44 24 0 c cmp 0xc (%rsp),%eax 400 fc2: 74 05 je 400 fc9 <phase_3+0x86 > 400 fc4: e8 71 04 00 00 callq 40143 a <explode_bomb> 400 fc9: 48 83 c4 18 add $0x18 ,%rsp 400 fcd: c3 retq 0000000000400 fce <func4>: 400 fce: 48 83 ec 08 sub $0x8 ,%rsp 400 fd2: 89 d0 mov %edx,%eax 400 fd4: 29 f0 sub %esi,%eax 400 fd6: 89 c1 mov %eax,%ecx 400 fd8: c1 e9 1 f shr $0x1f ,%ecx 400 fdb: 01 c8 add %ecx,%eax 400 fdd: d1 f8 sar %eax 400 fdf: 8 d 0 c 30 lea (%rax,%rsi,1 ),%ecx 400 fe2: 39 f9 cmp %edi,%ecx 400 fe4: 7 e 0 c jle 400 ff2 <func4+0x24 > 400 fe6: 8 d 51 ff lea -0x1 (%rcx),%edx 400 fe9: e8 e0 ff ff ff callq 400 fce <func4> 400 fee: 01 c0 add %eax,%eax 400 ff0: eb 15 jmp 401007 <func4+0x39 > # 结束 400 ff2: b8 00 00 00 00 mov $0x0 ,%eax 400 ff7: 39 f9 cmp %edi,%ecx 400 ff9: 7 d 0 c jge 401007 <func4+0x39 > # 结束 400 ffb: 8 d 71 01 lea 0x1 (%rcx),%esi 400 ffe: e8 cb ff ff ff callq 400 fce <func4> 401003 : 8 d 44 00 01 lea 0x1 (%rax,%rax,1 ),%eax 401007 : 48 83 c4 08 add $0x8 ,%rsp 40100 b: c3 retq
伪代码如下:
phase_3: %rcx = (%rsp) + 0xc %rdx = (%rsp) + 0x8 if (%eax > 1 ){ if ((%rdx) <= 7 ) { %eax = (%rdx); goto 0x402470 + %rax * 8 所指向的地址; %eax = 某个值; if (%eax == (%rcx)) return ; else explode_bomb; } else explode_bomb; } else explode_bomb;
(gdb) x/6a 0x402470 0x402470: 0x400f7c <phase_3+57> 0x400fb9 <phase_3+118> 0x402480: 0x400f83 <phase_3+64> 0x400f8a <phase_3+71> 0x402490: 0x400f91 <phase_3+78> 0x400f98 <phase_3+85>
使用gdb检查 0x402470处的值为0x400f7c
说明第一个参数为0时,程序跳转到0x400f7c,%eax = 0xcf = 207
因此,0 207为一组解。
0 207(非唯一解,还有1 311, 2 707, 3 256, 4 389, 5 206, 6 682, 7 327)
phase_4 000000000040100 c <phase_4>: 40100 c: 48 83 ec 18 sub $0x18 ,%rsp 401010 : 48 8 d 4 c 24 0 c lea 0xc (%rsp),%rcx # 第二个参数 401015 : 48 8 d 54 24 08 lea 0x8 (%rsp),%rdx # 第一个参数 40101 a: be cf 25 40 00 mov $0x4025cf ,%esi 40101 f: b8 00 00 00 00 mov $0x0 ,%eax 401024 : e8 c7 fb ff ff callq 400 bf0 <__isoc99_sscanf@plt> 401029 : 83 f8 02 cmp $0x2 ,%eax 40102 c: 75 07 jne 401035 <phase_4+0x29 > # %eax!=2则explode_bomb 40102 e: 83 7 c 24 08 0 e cmpl $0xe ,0x8 (%rsp) 401033 : 76 05 jbe 40103 a <phase_4+0x2e > 401035 : e8 00 04 00 00 callq 40143 a <explode_bomb> 40103 a: ba 0 e 00 00 00 mov $0xe ,%edx 40103 f: be 00 00 00 00 mov $0x0 ,%esi 401044 : 8 b 7 c 24 08 mov 0x8 (%rsp),%edi 401048 : e8 81 ff ff ff callq 400 fce <func4> 40104 d: 85 c0 test %eax,%eax 40104 f: 75 07 jne 401058 <phase_4+0x4c > 401051 : 83 7 c 24 0 c 00 cmpl $0x0 ,0xc (%rsp) 401056 : 74 05 je 40105 d <phase_4+0x51 > 401058 : e8 dd 03 00 00 callq 40143 a <explode_bomb> 40105 d: 48 83 c4 18 add $0x18 ,%rsp 401061 : c3 retq
伪代码:
phase_4: %rcx = (%rsp) + 0xc #第二个参数 %rdx = (%rsp) + 0x8 #第一个参数 if (%eax == 0x2 ){ if ((%rdx) <= 0xe ) { %edx = 0xe ; %esi = 0x0 ; %edi = (%rdx); goto func4; if (%eax == 0 ) { if ((%rcx)==0x0 ) return ; else explode_bomb; } else explode_bomb; } else explode_bomb; } else explode_bomb;func4: int t = z - y = 14 ;int k = t >> 31 ;t = (t + k) >> 1 ; k = t + y; if (k <= x){ t = 0 ; if (k >= x) return ; y = k + 1 ; goto func4; t = 2 *t + 1 ; return ; } else { z = k - 1 ; goto func4; t *= 2 ; }
如上分析,答案为7 0。
7 0
phase_5 0000000000401062 <phase_5>: 401062 : 53 push %rbx 401063 : 48 83 ec 20 sub $0x20 ,%rsp 401067 : 48 89 fb mov %rdi,%rbx 40106 a: 64 48 8 b 04 25 28 00 mov %fs:0x28 ,%rax 401071 : 00 00 401073 : 48 89 44 24 18 mov %rax,0x18 (%rsp) 401078 : 31 c0 xor %eax,%eax # %eax=0 40107 a: e8 9 c 02 00 00 callq 40131 b <string_length> 40107 f: 83 f8 06 cmp $0x6 ,%eax # 字符串长度为6 401082 : 74 4 e je 4010 d2 <phase_5+0x70 > 401084 : e8 b1 03 00 00 callq 40143 a <explode_bomb> 401089 : eb 47 jmp 4010 d2 <phase_5+0x70 > 40108 b: 0 f b6 0 c 03 movzbl (%rbx,%rax,1 ),%ecx 40108 f: 88 0 c 24 mov %cl,(%rsp) 401092 : 48 8 b 14 24 mov (%rsp),%rdx 401096 : 83 e2 0 f and $0xf ,%edx 401099 : 0 f b6 92 b0 24 40 00 movzbl 0x4024b0 (%rdx),%edx 4010 a0: 88 54 04 10 mov %dl,0x10 (%rsp,%rax,1 ) 4010 a4: 48 83 c0 01 add $0x1 ,%rax 4010 a8: 48 83 f8 06 cmp $0x6 ,%rax 4010 ac: 75 dd jne 40108 b <phase_5+0x29 > 4010 ae: c6 44 24 16 00 movb $0x0 ,0x16 (%rsp) 4010 b3: be 5 e 24 40 00 mov $0x40245e ,%esi 4010 b8: 48 8 d 7 c 24 10 lea 0x10 (%rsp),%rdi 4010 bd: e8 76 02 00 00 callq 401338 <strings_not_equal> 4010 c2: 85 c0 test %eax,%eax 4010 c4: 74 13 je 4010 d9 <phase_5+0x77 > 4010 c6: e8 6 f 03 00 00 callq 40143 a <explode_bomb> 4010 cb: 0 f 1 f 44 00 00 nopl 0x0 (%rax,%rax,1 ) 4010 d0: eb 07 jmp 4010 d9 <phase_5+0x77 > 4010 d2: b8 00 00 00 00 mov $0x0 ,%eax 4010 d7: eb b2 jmp 40108 b <phase_5+0x29 > 4010 d9: 48 8 b 44 24 18 mov 0x18 (%rsp),%rax 4010 de: 64 48 33 04 25 28 00 xor %fs:0x28 ,%rax 4010e5 : 00 00 4010e7 : 74 05 je 4010 ee <phase_5+0x8c > # return 4010e9 : e8 42 fa ff ff callq 400 b30 <__stack_chk_fail@plt> 4010 ee: 48 83 c4 20 add $0x20 ,%rsp 4010 f2: 5 b pop %rbx 4010 f3: c3 retq
伪代码:
if (strlen (str)==6 ){ %eax = 0 ; do { %ecx = (%rbx + %rax); (%rsp) = %cl; %rdx = (%rsp); %edx ^= 0xf ; %edx = (%rdx + 0x4024b0 ); (%rsp + %rax + 0x10 ) = %dl; %rax += 1 ; } while (%rax != 6 ); (%rsp+0x16 ) = 0x0 ; %esi = 0x40245e ; %rdi = (%rsp + 0x10 ); if (strings_not_equal) explode_bomb; else { %rax = (%rsp + 18 ); %rax ^fs:0x28 ; if (0 ) return ; } } else explode_bomb;
字符串六个字符保存在%rsp + 0x10 ~ %rsp + 0x15。
(gdb) x/32xb 0x4024b0 0x4024b0 <array.3449>: 0x6d 0x61 0x64 0x75 0x69 0x65 0x72 0x73 0x4024b8 <array.3449+8>: 0x6e 0x66 0x6f 0x74 0x76 0x62 0x79 0x6c # 0x40245e处是flyers字符串 (gdb) x/7c 0x40245e 0x40245e: 102 'f' 108 'l' 121 'y' 101 'e' 114 'r' 115 's' 0 '\000' (gdb) x/7xb 0x40245e 0x40245e: 0x66 0x6c 0x79 0x65 0x72 0x73 0x00 # flyers对应的ascii值 0x66 0x6c 0x79 0x65 0x72 0x73
基于0x4024b0的对应偏移量为0x9 0xF 0xE 0x5 0x6 0x7。
即六个字符的低4bit分别为0x9 0xF 0xE 0x5 0x6 0x7。
若输入为大写字母,将低4bit的值加上0x40,获得输入字符串IONEFG。
若输入为小写字母,将低4bit的值加上0x60,获得输入字符串ionefg。
IONEFG(或ionefg)
phase_6 00000000004010 f4 <phase_6>: 4010 f4: 41 56 push %r14 4010 f6: 41 55 push %r13 4010 f8: 41 54 push %r12 4010 fa: 55 push %rbp 4010 fb: 53 push %rbx 4010 fc: 48 83 ec 50 sub $0x50 ,%rsp # 开辟空间 401100 : 49 89 e5 mov %rsp,%r13 401103 : 48 89 e6 mov %rsp,%rsi 401106 : e8 51 03 00 00 callq 40145 c <read_six_numbers> # 读入6个值,%rdi %rsi %rdx %rcx %r8 %r9 ################################################################# 40110 b: 49 89 e6 mov %rsp,%r14 40110 e: 41 bc 00 00 00 00 mov $0x0 ,%r12d 401114 : 4 c 89 ed mov %r13 ,%rbp 401117 : 41 8 b 45 00 mov 0x0 (%r13 ),%eax 40111 b: 83 e8 01 sub $0x1 ,%eax 40111 e: 83 f8 05 cmp $0x5 ,%eax # 数字<=6 401121 : 76 05 jbe 401128 <phase_6+0x34 > 401123 : e8 12 03 00 00 callq 40143 a <explode_bomb> 401128 : 41 83 c4 01 add $0x1 ,%r12d 40112 c: 41 83 fc 06 cmp $0x6 ,%r12d 401130 : 74 21 je 401153 <phase_6+0x5f > # 程序出口 401132 : 44 89 e3 mov %r12d,%ebx 401135 : 48 63 c3 movslq %ebx,%rax 401138 : 8 b 04 84 mov (%rsp,%rax,4 ),%eax 40113 b: 39 45 00 cmp %eax,0x0 (%rbp) 40113 e: 75 05 jne 401145 <phase_6+0x51 > 401140 : e8 f5 02 00 00 callq 40143 a <explode_bomb> 401145 : 83 c3 01 add $0x1 ,%ebx 401148 : 83 fb 05 cmp $0x5 ,%ebx 40114 b: 7 e e8 jle 401135 <phase_6+0x41 > 40114 d: 49 83 c5 04 add $0x4 ,%r13 401151 : eb c1 jmp 401114 <phase_6+0x20 > ################################################################# 401153 : 48 8 d 74 24 18 lea 0x18 (%rsp),%rsi # %rsi = (%rsp+0x18)的地址 第六个数的地址 401158 : 4 c 89 f0 mov %r14 ,%rax # %rax = %r14; 40115 b: b9 07 00 00 00 mov $0x7 ,%ecx # 401160 : 89 ca mov %ecx,%edx # 401162 : 2 b 10 sub (%rax),%edx # 401164 : 89 10 mov %edx,(%rax) # (%rax) = 7-(%rax); 401166 : 48 83 c0 04 add $0x4 ,%rax # %rax += 4; 40116 a: 48 39 f0 cmp %rsi,%rax 40116 d: 75 f1 jne 401160 <phase_6+0x6c > ################################################################# 40116 f: be 00 00 00 00 mov $0x0 ,%esi 401174 : eb 21 jmp 401197 <phase_6+0xa3 > 401176 : 48 8 b 52 08 mov 0x8 (%rdx),%rdx # 指向链表的下一个节点的首地址 40117 a: 83 c0 01 add $0x1 ,%eax 40117 d: 39 c8 cmp %ecx,%eax 40117 f: 75 f5 jne 401176 <phase_6+0x82 > # 不相等,继续遍历链表,最终 %rdx 指向链表的第 %ecx 个节点 401181 : eb 05 jmp 401188 <phase_6+0x94 > 401183 : ba d0 32 60 00 mov $0x6032d0 ,%edx 401188 : 48 89 54 74 20 mov %rdx,0x20 (%rsp,%rsi,2 ) 40118 d: 48 83 c6 04 add $0x4 ,%rsi 401191 : 48 83 fe 18 cmp $0x18 ,%rsi 401195 : 74 14 je 4011 ab <phase_6+0xb7 > 401197 : 8 b 0 c 34 mov (%rsp,%rsi,1 ),%ecx 40119 a: 83 f9 01 cmp $0x1 ,%ecx # %ecx是从第1个数到第6个数(7-之后) 40119 d: 7 e e4 jle 401183 <phase_6+0x8f > 40119 f: b8 01 00 00 00 mov $0x1 ,%eax 4011 a4: ba d0 32 60 00 mov $0x6032d0 ,%edx # 进入链表 4011 a9: eb cb jmp 401176 <phase_6+0x82 > ################################################################# 4011 ab: 48 8 b 5 c 24 20 mov 0x20 (%rsp),%rbx # 第一个节点的地址 4011 b0: 48 8 d 44 24 28 lea 0x28 (%rsp),%rax # 第二个节点的地址 4011 b5: 48 8 d 74 24 50 lea 0x50 (%rsp),%rsi # 结束循环的地址 4011 ba: 48 89 d9 mov %rbx,%rcx # %rcx = 前一个节点的首地址 4011 bd: 48 8 b 10 mov (%rax),%rdx # %rdx = 后一个节点的首地址 4011 c0: 48 89 51 08 mov %rdx,0x8 (%rcx) # pNext重新链接 4011 c4: 48 83 c0 08 add $0x8 ,%rax 4011 c8: 48 39 f0 cmp %rsi,%rax 4011 cb: 74 05 je 4011 d2 <phase_6+0xde > # 出口 4011 cd: 48 89 d1 mov %rdx,%rcx # 继续循环 4011 d0: eb eb jmp 4011 bd <phase_6+0xc9 > ################################################################# 4011 d2: 48 c7 42 08 00 00 00 movq $0x0 ,0x8 (%rdx) 4011 d9: 00 4011 da: bd 05 00 00 00 mov $0x5 ,%ebp # 固定五次循环 4011 df: 48 8 b 43 08 mov 0x8 (%rbx),%rax # pNext 4011e3 : 8 b 00 mov (%rax),%eax # 第二个节点的值 4011e5 : 39 03 cmp %eax,(%rbx) # 比较两个节点中第一个字段值的大小 4011e7 : 7 d 05 jge 4011 ee <phase_6+0xfa > 4011e9 : e8 4 c 02 00 00 callq 40143 a <explode_bomb> # <就爆炸 4011 ee: 48 8 b 5 b 08 mov 0x8 (%rbx),%rbx # >= 4011 f2: 83 ed 01 sub $0x1 ,%ebp 4011 f5: 75 e8 jne 4011 df <phase_6+0xeb > ################################################################# 4011 f7: 48 83 c4 50 add $0x50 ,%rsp 4011 fb: 5 b pop %rbx 4011 fc: 5 d pop %rbp 4011 fd: 41 5 c pop %r12 4011 ff: 41 5 d pop %r13 401201 : 41 5 e pop %r14 401203 : c3 retq
Tips movl是以寄存器运算结果为游标,访问内存获得该地址的值,然后给目标寄存器赋值。
leal是获得寄存器的运算结果,很多时候会遇到用来完成类似于mips或者arm上三元运算的工作,一步完成加法。
代码分段 0x4010f4 -> 0x401106 这段代码是把输入的6个数字读到栈上,存在rsp到rsp+0x18(6个数字共占了24个字节)。
0x40110b -> 0x401151 这段代码是循环检查6个数字的初值,两个条件:1. 每个数字都小于6,2. 每个数字都不相等。
0x401153 -> 0x40116d 这段代码是将每个数字都用7减一遍,6个数由a[i]变成了7-a[i],例如:
a[i]: 4, 1, 2, 5, 6, 3 7-a[i]: 3, 6, 5, 2, 1, 4
0x40116f -> 0x4011a9 这段代码十分复杂,可以通过代入特定值的方式来了解其行为,其功能是根据7-a[i]的值,将链表的node的首地址在栈中排序,例如:
7-a[i]: 3, 6, 5, 2, 1, 4
node的排序: node3, node6, node5, node2, node1, node4,也就是赋值给了0x20+%rsp开始的一片区域。
这里值得注意的地方是0x20(%rsp,%rsi,2), 输入6个数是4字节, 现在每个数对应1个8字节的. 如果是1, 存的内容是$0x6032d0.
0x4011ab -> 0x4011d2 基于上一步在栈中的node顺序,将链表中的各个node重新链接起来,通过更改node.pNext来完成,最后一个node的pNext置为0;
0x4011d9 -> 结束 检查在上一步中完成排序的链表,是否是按照值从大到小排列的,如果不是的话,bomb就炸了。
(gdb) x/24xw 0x6032d0 0x6032d0 <node1>: 0x0000014c 0x00000001 0x006032e0 0x00000000 0x6032e0 <node2>: 0x000000a8 0x00000002 0x006032f0 0x00000000 0x6032f0 <node3>: 0x0000039c 0x00000003 0x00603300 0x00000000 0x603300 <node4>: 0x000002b3 0x00000004 0x00603310 0x00000000 0x603310 <node5>: 0x000001dd 0x00000005 0x00603320 0x00000000 0x603320 <node6>: 0x000001bb 0x00000006 0x00000000 0x00000000 # 节点值 下个节点地址值
排序是 3 4 5 6 1 2,7-之前是 4 3 2 1 6 5。
4 3 2 1 6 5
测试结果