The Report of the Bomb Lab
The Lab2 in CS:APP
毒液哥 Fudan University
The CS:APP Bomb Lab teaches students principles of
machine-level programs, as well as general debugger and reverse
engineering skills.
Preface
This Lab is done in the WSL(Windows Subsystem for Linux) experiment, cost about 20 hours.
First, I used objdump -d bomb > assembly code.s
to print the assembly code of bomb.
Then, I read the disassembly code with GDB debugging, including:
r
to run the bomb.
x /s address
to read the string starting from the address.
x /64xw address
to read some integers from the address.
b *address
to set the breakpoint in a statement of disassembly code.
c
to continue the bomb till next breakpoint.
Finally, I updated the code with my annotation to better understand the code.
Solution
Phase_1
0000000000400ee0 <phase_1>:
400ee0: 48 83 ec 08 sub $0x8,%rsp
400ee4: be 00 24 40 00 mov $0x402400,%esi
400ee9: e8 4a 04 00 00 callq 401338 <strings_not_equal>
400eee: 85 c0 test %eax,%eax
400ef0: 74 05 je 400ef7 <phase_1+0x17>
400ef2: e8 43 05 00 00 callq 40143a <explode_bomb>
400ef7: 48 83 c4 08 add $0x8,%rsp
400efb: c3 retq
The piece of code above checks is the input string is equal to the givin string, which starts from 0x402400
. Using GDB with x /s address
, we have:
Hence, we figured out the first password:
"Border relations with Canada have never been better."
Phase_2
0000000000400efc <phase_2>:
400efc: 55 push %rbp
400efd: 53 push %rbx
400efe: 48 83 ec 28 sub $0x28,%rsp
400f02: 48 89 e6 mov %rsp,%rsi
400f05: e8 52 05 00 00 callq 40145c <read_six_numbers>
400f0a: 83 3c 24 01 cmpl $0x1,(%rsp)
400f0e: 74 20 je 400f30 <phase_2+0x34>
400f10: e8 25 05 00 00 callq 40143a <explode_bomb>
400f15: eb 19 jmp 400f30 <phase_2+0x34>
400f17: 8b 43 fc mov -0x4(%rbx),%eax
400f1a: 01 c0 add %eax,%eax
400f1c: 39 03 cmp %eax,(%rbx)
400f1e: 74 05 je 400f25 <phase_2+0x29>
400f20: e8 15 05 00 00 callq 40143a <explode_bomb>
400f25: 48 83 c3 04 add $0x4,%rbx
400f29: 48 39 eb cmp %rbp,%rbx
400f2c: 75 e9 jne 400f17 <phase_2+0x1b>
400f2e: eb 0c jmp 400f3c <phase_2+0x40>
400f30: 48 8d 5c 24 04 lea 0x4(%rsp),%rbx
400f35: 48 8d 6c 24 18 lea 0x18(%rsp),%rbp
400f3a: eb db jmp 400f17 <phase_2+0x1b>
400f3c: 48 83 c4 28 add $0x28,%rsp
400f40: 5b pop %rbx
400f41: 5d pop %rbp
400f42: c3 retq
Before callq 40145c <read_six_numbers>
, we notice that mov %rsp,%rsi
pushes the address in %rsp
into %rsi
as the second argument of the function we call. We can infer that <read_six_numbers>
read 6 numbers from the givin string and saved them in an array startinng from %rsp
. Thus, the following loop simply checks if the geometric array is starting with 1.
Consequently, we figured out the second password:
"1 2 4 8 16 32"
Phase_3
0000000000400f43 <phase_3>:
400f43: 48 83 ec 18 sub $0x18,%rsp
400f47: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx # 0xc(%rsp) : the second input interger.
400f4c: 48 8d 54 24 08 lea 0x8(%rsp),%rdx # 0x8(%rsp) : the first input interger.
400f51: be cf 25 40 00 mov $0x4025cf,%esi
400f56: b8 00 00 00 00 mov $0x0,%eax
400f5b: e8 90 fc ff ff callq 400bf0 <__isoc99_sscanf@plt>
400f60: 83 f8 01 cmp $0x1,%eax
400f63: 7f 05 jg 400f6a <phase_3+0x27>
400f65: e8 d0 04 00 00 callq 40143a <explode_bomb>
400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp) # Obviously 0x8(%rsp) should greater than 6.
400f6f: 77 3c ja 400fad <phase_3+0x6a>
400f71: 8b 44 24 08 mov 0x8(%rsp),%eax
400f75: ff 24 c5 70 24 40 00 jmpq *0x402470(,%rax,8)
400f7c: b8 cf 00 00 00 mov $0xcf,%eax
400f81: eb 3b jmp 400fbe <phase_3+0x7b>
400f83: b8 c3 02 00 00 mov $0x2c3,%eax
400f88: eb 34 jmp 400fbe <phase_3+0x7b>
400f8a: b8 00 01 00 00 mov $0x100,%eax
400f8f: eb 2d jmp 400fbe <phase_3+0x7b>
400f91: b8 85 01 00 00 mov $0x185,%eax
400f96: eb 26 jmp 400fbe <phase_3+0x7b>
400f98: b8 ce 00 00 00 mov $0xce,%eax
400f9d: eb 1f jmp 400fbe <phase_3+0x7b>
400f9f: b8 aa 02 00 00 mov $0x2aa,%eax
400fa4: eb 18 jmp 400fbe <phase_3+0x7b>
400fa6: b8 47 01 00 00 mov $0x147,%eax # We'll be here is the first input interger is 7.
400fab: eb 11 jmp 400fbe <phase_3+0x7b>
400fad: e8 88 04 00 00 callq 40143a <explode_bomb>
400fb2: b8 00 00 00 00 mov $0x0,%eax
400fb7: eb 05 jmp 400fbe <phase_3+0x7b>
400fb9: b8 37 01 00 00 mov $0x137,%eax
400fbe: 3b 44 24 0c cmp 0xc(%rsp),%eax
400fc2: 74 05 je 400fc9 <phase_3+0x86>
400fc4: e8 71 04 00 00 callq 40143a <explode_bomb>
400fc9: 48 83 c4 18 add $0x18,%rsp
400fcd: c3 retq
Using GDB with x /s address
, we get the string starting from 0x4025cf
.
Similarly to Phase_2, we can infer that 0x8(%rsp)
and 0xc(%rsp)
is the first and the second integer we input, respectively.
The following piece of code obviously represent a switch-case. From the piece of code with annotation, we infer that the first integer should be 7 and the second integer should be 0x147
.
Hence, we figured out the third password:
"7 327"
Phase_4
000000000040100c <phase_4>:
40100c: 48 83 ec 18 sub $0x18,%rsp
401010: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx # 0xc(%rsp) : the second input integer.
401015: 48 8d 54 24 08 lea 0x8(%rsp),%rdx # 0x8(%rsp) : the first input integer.
40101a: be cf 25 40 00 mov $0x4025cf,%esi
40101f: b8 00 00 00 00 mov $0x0,%eax
401024: e8 c7 fb ff ff callq 400bf0 <__isoc99_sscanf@plt>
401029: 83 f8 02 cmp $0x2,%eax
40102c: 75 07 jne 401035 <phase_4+0x29>
40102e: 83 7c 24 08 0e cmpl $0xe,0x8(%rsp)
401033: 76 05 jbe 40103a <phase_4+0x2e>
401035: e8 00 04 00 00 callq 40143a <explode_bomb>
40103a: ba 0e 00 00 00 mov $0xe,%edx
40103f: be 00 00 00 00 mov $0x0,%esi
401044: 8b 7c 24 08 mov 0x8(%rsp),%edi
401048: e8 81 ff ff ff callq 400fce <func4> # %rdi %rsi %rdx
40104d: 85 c0 test %eax,%eax
40104f: 75 07 jne 401058 <phase_4+0x4c> # the return value shoule be 0.
401051: 83 7c 24 0c 00 cmpl $0x0,0xc(%rsp) # the second input integer shoule be 0.
401056: 74 05 je 40105d <phase_4+0x51>
401058: e8 dd 03 00 00 callq 40143a <explode_bomb>
40105d: 48 83 c4 18 add $0x18,%rsp
401061: c3 retq
0000000000400fce <func4>: # arguments: input x 0 14 -- %rdi %rsi %rdx
400fce: 48 83 ec 08 sub $0x8,%rsp
400fd2: 89 d0 mov %edx,%eax
400fd4: 29 f0 sub %esi,%eax
400fd6: 89 c1 mov %eax,%ecx
400fd8: c1 e9 1f shr $0x1f,%ecx # %ecx = %eax = %edx - %esi = 14 - 0 = 14. 14 >> 31 == 0
400fdb: 01 c8 add %ecx,%eax # %eax = 14 + 0 = 14;
400fdd: d1 f8 sar %eax # %eax >> 1 = 7.
400fdf: 8d 0c 30 lea (%rax,%rsi,1),%ecx # %ecx = 7. %rcx = (%rdx - %rsi) / 2.
400fe2: 39 f9 cmp %edi,%ecx # if(%rcx <= x) jump
400fe4: 7e 0c jle 400ff2 <func4+0x24>
400fe6: 8d 51 ff lea -0x1(%rcx),%edx # %rdx = %rcx - 1
400fe9: e8 e0 ff ff ff callq 400fce <func4>
400fee: 01 c0 add %eax,%eax
400ff0: eb 15 jmp 401007 <func4+0x39>
400ff2: b8 00 00 00 00 mov $0x0,%eax
400ff7: 39 f9 cmp %edi,%ecx # if(%rcx >= x) jump
400ff9: 7d 0c jge 401007 <func4+0x39>
400ffb: 8d 71 01 lea 0x1(%rcx),%esi # %rsi = %rcx + 1
400ffe: e8 cb ff ff ff callq 400fce <func4>
401003: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax # %eax = 1 + %eax * 2.
401007: 48 83 c4 08 add $0x8,%rsp
40100b: c3 retq
Distinctly, the second input integer and the return value of fun4(x, 14, 0)
should be both 0, where x is the first input integer.
Turn to fun4. In fact, I didn’t really understand what this function do after a quick-view, but I soon found that if x = 7 x = 7 x=7, the return value is 0.
Hence, we figured out the fourth password:
"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 # Now %rbx denotes the begin address of the input string.
40106a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
401071: 00 00
401073: 48 89 44 24 18 mov %rax,0x18(%rsp) # 0x18(%rsp) = %fs:0x28
401078: 31 c0 xor %eax,%eax
40107a: e8 9c 02 00 00 callq 40131b <string_length>
40107f: 83 f8 06 cmp $0x6,%eax # The length of input string should be 6.
401082: 74 4e je 4010d2 <phase_5+0x70>
401084: e8 b1 03 00 00 callq 40143a <explode_bomb>
401089: eb 47 jmp 4010d2 <phase_5+0x70>
40108b: 0f b6 0c 03 movzbl (%rbx,%rax,1),%ecx # The address of the i-th charactor in the input string have been saved in %ecx.
40108f: 88 0c 24 mov %cl,(%rsp)
401092: 48 8b 14 24 mov (%rsp),%rdx
401096: 83 e2 0f and $0xf,%edx
401099: 0f b6 92 b0 24 40 00 movzbl 0x4024b0(%rdx),%edx
4010a0: 88 54 04 10 mov %dl,0x10(%rsp,%rax,1) # It seems that 6 chars will be moved into an array by some rules from the input.
4010a4: 48 83 c0 01 add $0x1,%rax # From this, we know that %rax is the accumulator i.
4010a8: 48 83 f8 06 cmp $0x6,%rax
4010ac: 75 dd jne 40108b <phase_5+0x29>
4010ae: c6 44 24 16 00 movb $0x0,0x16(%rsp) # The end of the string is set to 0.
4010b3: be 5e 24 40 00 mov $0x40245e,%esi
4010b8: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi # 9 f e 5 6 7 : the least nibbles of the chars in the string we need, respectively.
4010bd: e8 76 02 00 00 callq 401338 <strings_not_equal> # Check if the string we created and the given string are equal.
4010c2: 85 c0 test %eax,%eax
4010c4: 74 13 je 4010d9 <phase_5+0x77>
4010c6: e8 6f 03 00 00 callq 40143a <explode_bomb>
4010cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
4010d0: eb 07 jmp 4010d9 <phase_5+0x77>
4010d2: b8 00 00 00 00 mov $0x0,%eax
4010d7: eb b2 jmp 40108b <phase_5+0x29>
4010d9: 48 8b 44 24 18 mov 0x18(%rsp),%rax
4010de: 64 48 33 04 25 28 00 xor %fs:0x28,%rax #Check if 0x18(%rsp) = %fs:0x28.
4010e5: 00 00
4010e7: 74 05 je 4010ee <phase_5+0x8c>
4010e9: e8 42 fa ff ff callq 400b30 <__stack_chk_fail@plt>
4010ee: 48 83 c4 20 add $0x20,%rsp
4010f2: 5b pop %rbx
4010f3: c3 retq
Apparently, the length of the input string should be 6. With the annotation, we found that the function will construct a string according to our input.
40108f: 88 0c 24 mov %cl,(%rsp)
401092: 48 8b 14 24 mov (%rsp),%rdx
401096: 83 e2 0f and $0xf,%edx
The piece of code above shows that the i-th char will be C[A[i] & 0xf] where C is a char array starting from 0x4024b0
, A is the string we input.
And then the string we construct will be compared with the string starting from 0x40245e
. Using GDB with x /s address
and x /16cb address
, we get the string starting from 0x40245e
and the char array starting from 0x4024b0
.
Thus, the least neebles of the chars in the input string should be 9, f, e, 5, 6 and 7, respectively.
Consequently, we figured out the fifth password:
")/.%&'"
Phase_6
00000000004010f4 <phase_6>:
4010f4: 41 56 push %r14
4010f6: 41 55 push %r13
4010f8: 41 54 push %r12
4010fa: 55 push %rbp
4010fb: 53 push %rbx
4010fc: 48 83 ec 50 sub $0x50,%rsp
401100: 49 89 e5 mov %rsp,%r13 # %rsp is the starting address of the array.
401103: 48 89 e6 mov %rsp,%rsi
401106: e8 51 03 00 00 callq 40145c <read_six_numbers>
40110b: 49 89 e6 mov %rsp,%r14
40110e: 41 bc 00 00 00 00 mov $0x0,%r12d
401114: 4c 89 ed mov %r13,%rbp # %rbp = %r13 = the address of the current integer.
401117: 41 8b 45 00 mov 0x0(%r13),%eax
40111b: 83 e8 01 sub $0x1,%eax
40111e: 83 f8 05 cmp $0x5,%eax
401121: 76 05 jbe 401128 <phase_6+0x34>
401123: e8 12 03 00 00 callq 40143a <explode_bomb>
401128: 41 83 c4 01 add $0x1,%r12d # It seems that %r12d is an accumulator i.
40112c: 41 83 fc 06 cmp $0x6,%r12d
401130: 74 21 je 401153 <phase_6+0x5f>
401132: 44 89 e3 mov %r12d,%ebx # j = i.
401135: 48 63 c3 movslq %ebx,%rax
401138: 8b 04 84 mov (%rsp,%rax,4),%eax # Push the i-th input interger into %eax.
40113b: 39 45 00 cmp %eax,0x0(%rbp) # Check if the j-th is equal to the current integer.
40113e: 75 05 jne 401145 <phase_6+0x51>
401140: e8 f5 02 00 00 callq 40143a <explode_bomb>
401145: 83 c3 01 add $0x1,%ebx # %ebx is also an accumulator j.
401148: 83 fb 05 cmp $0x5,%ebx # Another loop in the first loop?
40114b: 7e e8 jle 401135 <phase_6+0x41>
40114d: 49 83 c5 04 add $0x4,%r13
401151: eb c1 jmp 401114 <phase_6+0x20>
401153: 48 8d 74 24 18 lea 0x18(%rsp),%rsi # The ending address.
401158: 4c 89 f0 mov %r14,%rax # Now %rax = %r14 = %rsp, the starting address.
40115b: b9 07 00 00 00 mov $0x7,%ecx
401160: 89 ca mov %ecx,%edx
401162: 2b 10 sub (%rax),%edx
401164: 89 10 mov %edx,(%rax) # (%rax) = 0x7 - (%rax)
401166: 48 83 c0 04 add $0x4,%rax
40116a: 48 39 f0 cmp %rsi,%rax # Obviously let each integer A[i] = 7 - A[i];
40116d: 75 f1 jne 401160 <phase_6+0x6c>
40116f: be 00 00 00 00 mov $0x0,%esi
401174: eb 21 jmp 401197 <phase_6+0xa3>
401176: 48 8b 52 08 mov 0x8(%rdx),%rdx
40117a: 83 c0 01 add $0x1,%eax
40117d: 39 c8 cmp %ecx,%eax # %ecx denote the number we selected.
40117f: 75 f5 jne 401176 <phase_6+0x82>
401181: eb 05 jmp 401188 <phase_6+0x94>
401183: ba d0 32 60 00 mov $0x6032d0,%edx # %edx denotes the head of the list.
401188: 48 89 54 74 20 mov %rdx,0x20(%rsp,%rsi,2) # %rdx denotes the address of the A[i]-th node in the list
40118d: 48 83 c6 04 add $0x4,%rsi
401191: 48 83 fe 18 cmp $0x18,%rsi
401195: 74 14 je 4011ab <phase_6+0xb7>
401197: 8b 0c 34 mov (%rsp,%rsi,1),%ecx # It shows that %rsi denotes the accumulator i and %ecx = A[i];
40119a: 83 f9 01 cmp $0x1,%ecx # if %ecx <= 1, jump. That means that we don't need to find the node in the list using loop.
40119d: 7e e4 jle 401183 <phase_6+0x8f>
40119f: b8 01 00 00 00 mov $0x1,%eax
4011a4: ba d0 32 60 00 mov $0x6032d0,%edx
4011a9: eb cb jmp 401176 <phase_6+0x82>
4011ab: 48 8b 5c 24 20 mov 0x20(%rsp),%rbx # %rbx : the head.
4011b0: 48 8d 44 24 28 lea 0x28(%rsp),%rax # %rax : the first node.
4011b5: 48 8d 74 24 50 lea 0x50(%rsp),%rsi # %rsi : the end. Thus, the listnodes have been arranged by the given order.
4011ba: 48 89 d9 mov %rbx,%rcx
4011bd: 48 8b 10 mov (%rax),%rdx
4011c0: 48 89 51 08 mov %rdx,0x8(%rcx)
4011c4: 48 83 c0 08 add $0x8,%rax
4011c8: 48 39 f0 cmp %rsi,%rax
4011cb: 74 05 je 4011d2 <phase_6+0xde>
4011cd: 48 89 d1 mov %rdx,%rcx
4011d0: eb eb jmp 4011bd <phase_6+0xc9>
4011d2: 48 c7 42 08 00 00 00 movq $0x0,0x8(%rdx)
4011d9: 00
4011da: bd 05 00 00 00 mov $0x5,%ebp # we have changed the node address to the array address.
4011df: 48 8b 43 08 mov 0x8(%rbx),%rax
4011e3: 8b 00 mov (%rax),%eax
4011e5: 39 03 cmp %eax,(%rbx)
4011e7: 7d 05 jge 4011ee <phase_6+0xfa>
4011e9: e8 4c 02 00 00 callq 40143a <explode_bomb> # If the array is not in ascending order, Bomb!
4011ee: 48 8b 5b 08 mov 0x8(%rbx),%rbx
4011f2: 83 ed 01 sub $0x1,%ebp
4011f5: 75 e8 jne 4011df <phase_6+0xeb>
4011f7: 48 83 c4 50 add $0x50,%rsp
4011fb: 5b pop %rbx
4011fc: 5d pop %rbp
4011fd: 41 5c pop %r12
4011ff: 41 5d pop %r13
401201: 41 5e pop %r14
401203: c3 retq
The first paragraph above read in 6 numbers and then check if they are identical.
The second paragraph replace each elements x with 7 - x.
When looking at the third paragraph first time, we will be quite confused what the loop functions. But notice that there is a immediate address in the piece of code. Using x /32xw address
, we find that there are some nodes of some class whose size is 16 bytes, and the third number is the address of a certain node.
Thus, we infer that this paragraph fill A[i] with the address of the C[i]-th node, where A is an array starting from 0x20(%rsp)
and C is the input array(remember that the array has been changed).
The fourth paragraph simply replace the address of each node with the value of it, respectively.
Finally the value array should be in acsending order. From the figure above we knew the value of each node, we can infer the sixth password:
"4 3 2 1 6 5"
Secret_Phase
00000000004015c4 <phase_defused>:
4015c4: 48 83 ec 78 sub $0x78,%rsp
4015c8: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
4015cf: 00 00
4015d1: 48 89 44 24 68 mov %rax,0x68(%rsp)
4015d6: 31 c0 xor %eax,%eax
4015d8: 83 3d 81 21 20 00 06 cmpl $0x6,0x202181(%rip) # 603760 <num_input_strings>
4015df: 75 5e jne 40163f <phase_defused+0x7b>
4015e1: 4c 8d 44 24 10 lea 0x10(%rsp),%r8
4015e6: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx
4015eb: 48 8d 54 24 08 lea 0x8(%rsp),%rdx
4015f0: be 19 26 40 00 mov $0x402619,%esi # "%d%d%s"
4015f5: bf 70 38 60 00 mov $0x603870,%edi # the forth input string.
4015fa: e8 f1 f5 ff ff callq 400bf0 <__isoc99_sscanf@plt>
4015ff: 83 f8 03 cmp $0x3,%eax
401602: 75 31 jne 401635 <phase_defused+0x71>
401604: be 22 26 40 00 mov $0x402622,%esi # "DrEvil"
401609: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi # %rdi denotes the string following the forth input line.
40160e: e8 25 fd ff ff callq 401338 <strings_not_equal>
401613: 85 c0 test %eax,%eax
401615: 75 1e jne 401635 <phase_defused+0x71>
401617: bf f8 24 40 00 mov $0x4024f8,%edi
40161c: e8 ef f4 ff ff callq 400b10 <puts@plt>
401621: bf 20 25 40 00 mov $0x402520,%edi
401626: e8 e5 f4 ff ff callq 400b10 <puts@plt>
40162b: b8 00 00 00 00 mov $0x0,%eax
401630: e8 0d fc ff ff callq 401242 <secret_phase>
0000000000401242 <secret_phase>:
401242: 53 push %rbx
401243: e8 56 02 00 00 callq 40149e <read_line>
401248: ba 0a 00 00 00 mov $0xa,%edx
40124d: be 00 00 00 00 mov $0x0,%esi
401252: 48 89 c7 mov %rax,%rdi
401255: e8 76 f9 ff ff callq 400bd0 <strtol@plt> # Input an integer x.
40125a: 48 89 c3 mov %rax,%rbx
40125d: 8d 40 ff lea -0x1(%rax),%eax
401260: 3d e8 03 00 00 cmp $0x3e8,%eax # if x - 1 <= 0x3e8, jump.
401265: 76 05 jbe 40126c <secret_phase+0x2a>
401267: e8 ce 01 00 00 callq 40143a <explode_bomb>
40126c: 89 de mov %ebx,%esi # %esi denotes x.
40126e: bf f0 30 60 00 mov $0x6030f0,%edi # 36
401273: e8 8c ff ff ff callq 401204 <fun7>
401278: 83 f8 02 cmp $0x2,%eax # The return value should be 2.
40127b: 74 05 je 401282 <secret_phase+0x40>
40127d: e8 b8 01 00 00 callq 40143a <explode_bomb>
401282: bf 38 24 40 00 mov $0x402438,%edi
401287: e8 84 f8 ff ff callq 400b10 <puts@plt>
40128c: e8 33 03 00 00 callq 4015c4 <phase_defused>
401291: 5b pop %rbx
401292: c3 retq
0000000000401204 <fun7>:
401204: 48 83 ec 08 sub $0x8,%rsp
401208: 48 85 ff test %rdi,%rdi # %rdi == 0, return INF.
40120b: 74 2b je 401238 <fun7+0x34>
40120d: 8b 17 mov (%rdi),%edx #
40120f: 39 f2 cmp %esi,%edx # if (%rdi) <= %esi, the input integer x, jump
401211: 7e 0d jle 401220 <fun7+0x1c>
401213: 48 8b 7f 08 mov 0x8(%rdi),%rdi # if (%rdi) > x, x = x -> left child.
401217: e8 e8 ff ff ff callq 401204 <fun7>
40121c: 01 c0 add %eax,%eax
40121e: eb 1d jmp 40123d <fun7+0x39> # We should return here.
401220: b8 00 00 00 00 mov $0x0,%eax
401225: 39 f2 cmp %esi,%edx # if (%rdi) == x, return 0.
401227: 74 14 je 40123d <fun7+0x39>
401229: 48 8b 7f 10 mov 0x10(%rdi),%rdi # x = x -> right child.
40122d: e8 d2 ff ff ff callq 401204 <fun7>
401232: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax
401236: eb 05 jmp 40123d <fun7+0x39>
401238: b8 ff ff ff ff mov $0xffffffff,%eax
40123d: 48 83 c4 08 add $0x8,%rsp
401241: c3 retq
First, we find that the <secret_phase>
will be called in the <phase_defused>
function only. We’ll read two integers and a string from 0x603870
which is the starting address of the fourth input password and compared it with the string starting from 0x402622
, which can be exposed using x /s address
.
Thus, in order to enter the secret_phase, the fourth line should be "7 0 DrEvil"
.
Turn to <secret_phase>
. This function simply read an integer x and check if the return value of fun7(0x6030f0, x)
is 2.
Now we are at <fun7>
. From the figure above, we know that it’s a tree starting from 0x6030f0
and the size fo the node is 32 bytes where the first 8 byte is the integer value of this node, and, the second and the third value is the address of the left and the right child, respectively.
If we illustrate the tree by the figure above, we surprisingly find the tree is a BST(binary-search-tree), and then the return value of fun7 can be easily calculate.
If x equals to the value of current node, return 0.
If x is less than the value, find x in the left subtree and then double the return value.
if x is greater then the value, find x in the right subtree and then double the return value with a increment of 1.
Thus, two value, 0x16
and 0x14
, can meet the condition.
Finally, we get a solution to the whole bomb:
Border relations with Canada have never been better. 1 2 4 8 16 32 7 327 7 0 DrEvil )/.%&' 4 3 2 1 6 5 22
Summary
本次Lab大大巩固了CSAPP第三章的学习内容,加深了我对汇编语言,寄存器,CPU与内存之间的数据交换等知识的理解。更让我体会到汇编作为一门面向机器的语言,和如今我们编写程序直接使用的高级语言之间有多么大的差异。
在积累一些技巧之前,我看代码的唯一方式似乎就只能是将人脑当作CPU使用,从上到下一步一步地跟着代码来。它跳我也跳,反复模拟很多遍之后才能猜出这段代码到底要干嘛。到后面几个关卡时,自己积累了一些经验,标记出哪些语句会被跳转,再找出累加器和终止条件,才能不那么费力地将每个循环语句分开来。
总的来说这次Lab真是太爽了,虽然花了非常多的时间,但最终完成的喜悦感也是无以伦比,令人感动的。