os version: gcc version 7.3.0 (Ubuntu 7.3.0-16ubuntu3)
Before this lab, we need read some articles:
PAY ATTENTION: because we just self-study, so we just need find the result of bomb, rather than care scores mentioned by README.
Initially, The directory only had three files:
- README: An introduce file, although it help nothing.
- bomb: A .exe file
- bomb.c: Source code for the main bomb routine
Then, we should execute objdump -d bomb > bomb.asm and get a assembly code file called bomb.asm. And execute objdump -t bomb > bomb-symbol-table.asm to get symbol table of bomb.
One of the aim of bomb lab is teach us how to debug c file. So let us learn the basics tools:
- gdb: the GUN debugger, it can trace through a program line by line, examine memory and register, look at both the source code and assembly code, set breakpoints, set memory watch points, and write scripts.
- objdump: A command-line program for displaying various information about object files on Unix-like os.
Compare two strings, and in the file bomb.asm, we can find a function called phase_1 which code is following:
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
And we can infer the compared data is in 0x402400, so we need view it's content.
Execute command x/s 0x402400, we get the result of phase_1:
Border relations with Canada have never been better.
Input above strings, then get the info:
Phase 1 defused. How about the next one?
Ok, Let's go!
Then we check function phase_2.
And the code is:
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
Firstly, from 400f05: e8 52 05 00 00 callq 40145c <read_six_numbers> we can infer that we need input six number, otherwise we will trigger bomb in read_six_number.
Here, I think we need read the function read_six_number, we can infer that the %rsp point the first number lastly.
Secondly, the first number should be 1(400f0a to 400f0e), because it not will meet bomb.
And then, because we can know that the second number should be more than twice of first number, so it's 2(and store in M[%rbx]).
After, we know that %rbp is the number which not belong input(we can't control it value)! So there is a loop during 0x400f17 - 0x400f2c, and infer that we know the latter number is twice as large as the former.
So, the result is:
1 2 4 8 16 32
Input above strings, then get the info:
That's number 2. Keep going!
Ok, Let's go!
From the code we can infer that it's a swtich statement.
Firstly, we should know function __isoc99_sscanf return the number of input items successfully matched and assigned(reference)
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>
So, we should input two chars at least.
And then, 0x8(%rsp) point at the third arguments. We know that it must less or equal 7.(because the number of branch is 7)
And then:
- input command
x/a 0x402470in GDB( now,%raxis 0), we know*0x402470's value is0x400f7c. - input command
x/a 0x402478in GDB( now,%raxis 1), we know*0x402478's value is0x400fb9. - input command
x/a 0x402480in GDB( now,%raxis 2), we know*0x402480's value is0x400f83. - input command
x/a 0x402488in GDB( now,%raxis 3), we know*0x402488's value is0x400f8a. - input command
x/a 0x402490in GDB( now,%raxis 4), we know*0x402490's value is0x400f91. - input command
x/a 0x402498in GDB( now,%raxis 5), we know*0x402498's value is0x400f98. - input command
x/a 0x4024a0in GDB( now,%raxis 6), we know*0x4024a0's value is0x400f9f. - input command
x/a 0x4024a8in GDB( now,%raxis 7), we know*0x4024a8's value is0x400fa6.
so, the result is one of the following:
- 0 0xcf -> 0 207
- 1 0x137 -> 1 311
- 2 0x2c3 -> 2 707
- 3 0x100 -> 3 256
- 4 0x185 -> 4 389
- 5 0xce -> 5 206
- 6 0x2aa -> 6 682
- 7 0x147 -> 7 327
So, we can input:
0 207
Input above strings, then get the info:
Halfway there!
Firstly, we should input two chars and the reason is same as phase_3.
And because:
40102e: 83 7c 24 08 0e cmpl $0xe,0x8(%rsp)
401033: 76 05 jbe 40103a <phase_4+0x2e>
we know the first input is 14.
And then:
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>
means execute func4(a,0,14); where a <= 14, meanwhile, because
40104d: 85 c0 test %eax,%eax
40104f: 75 07 jne 401058 <phase_4+0x4c>
401051: 83 7c 24 0c 00 cmpl $0x0,0xc(%rsp)
401056: 74 05 je 40105d <phase_4+0x51>
401058: e8 dd 03 00 00 callq 40143a <explode_bomb
we know the func4 return value should be 0.
Look at fun4(a,b,c), it's a recursive function.
and a is %edi, b is %esi, c is %edx, and the pseudo code is:
// pay attention, it's reference value
func4( a, b ,c) {
t1 = c - b; // %eax
t2 = t1>>31; // %ecx, and it's logical right shift
t1 = (t1 + t2) >> 1; // %eax, and it's arithmetic right shift
t2 = b + t1; // %ecx
if( t2 == a) {
return 0;
}
if( t2 < a) {
a = t2 + 1; // %esi
func(a,b,c);
t1 = 1 + 2 * t1; // %eax;
return t1;
}
else {
c = t2 - 1; // %edx
func4(a,b,c);
t1 = t1 * 2; // %eax
return t1;
}
}And we can infer that a is 7, so the result of phase_4 is:
7 0
And feedback:
So you got that one. Try this one.
First, from code:
40107f: 83 f8 06 cmp $0x6,%eax
401082: 74 4e je 4010d2 <phase_5+0x70>
401084: e8 b1 03 00 00 callq 40143a <explode_bomb>
We know that input string length is 6.
And the function can write:
phase_5(s){
if( s.length != 6) {
bomb();
return ;
}
t1 = 0; // %eax
while( t1 != 6) {
t2 = &s; // %rbx
t3 = *(t2 + t1) & 0xff; // %ecx
t4 = &%rsp; // (%rsp)
*t4 = t3;
t5 = *t4 & 0xf; // %rdx
t5 = *(t5 + 0x4024b0) & 0xff // %edx
t1 + 0x10 + t4 = t5;
t1 = t1 + 1;
}
*(&%rsp + 0x16) = 0;
if( strings_not_equal( *(&%rsp + 16), 'flyers' )) {
bomb();
return ;
}
}And we print the 0x4024b0 content by input command: x/s 0x4024b0, the output is: maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?
And the table is :
| b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8 | b9 | ba | bb | bc | bd | be | bf |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| m | a | d | u | i | e | r | s | n | f | o | t | v | b | y | l |
The flyers is correspnding to 9fe567.
And because of f and e ascii code are 0x66 and 0x65, which result of &0xf is 6 and 5 respectively.
We can use char ? and > (ascii code is 3f and 3e, which result of &0xf is f and e respectively), rather than f and e.
So the result is:
9?>567
And the output is:
Good work! On to the next...
First, from the code 401106: e8 51 03 00 00 callq 40145c <read_six_numbers> we know that need input six numbers.
And the flowchar is similar that:
// input %rsi
phase_6(input) {
a,b,c,d,e,f = read_six_numbers(input);
// a (%rsp), should less or equal 5
// b 0x4(%rsp)
// c 0x8(%rsp)
// d 0xc(%rsp)
// e 0x10(%rsp)
// f 0x14(%rsp)
//%rsp: 0x7fffffffe2d0
t9 = %rsp; // %r13
t0 = &a; // %r14
t2 = 0; // %r12d
t8 = t9; // %rbp, line3
t1 = a - 1; // %eax
if( t1 > 5) {
bomb();
return ;
}
t2 = t2 + 1; // %12d
if( t2 == 6) {
t3 = (%rsp) + 0x18 // %rsi
t4 = t0; // %rax
t5 = 7; // %ecx
do {
// means:
// a = 7 - a
// b = 7 - b
// c = 7 - c
// d = 7 - d
// e = 7 - e
// f = 7 - f
t6 = t5 - *t4; // %edx
*t4 = t6; // (%rax) <-> a
t4 = t4 + 4; // %rax <-> &b
} while (t4 != t3);
t3 = 0; // %esi
t5 = *(t3 + %rsp); // %ecx
if( t5 <= 1) {
t6 = 0x6032d0; // %edx
*(%rsp + 0x20 + 2 * t3) = t6;
t3 = t3 + 4; // %rsi
if( t3 === 0x18) { // line 4
t7 = M[(%rsp) + 0x20] // %rbx
t4 = (%rsp) + 0x28 // %rax
t3 = (%rsp) + 0x50 // %rsi
t5 = t7; // %rcx
t6 = *t4; // %rdx
M[t5 + 8] = t6;
t4 = t4 + 8 // %rax
if( t4 == t3) {
M[t6 + 8] = 0;
t8 = 5; // %ebp
t4 = M[t7 + 0x8]; // %rax, line1
t4 = *t4; // %eax
if( *t7 >= t4) {
t7 = M[t7 + 0x8]; // %rbx
t8 = t8 - 1; // %ebp
if( t8 != 0) {
goto line1;
}
}
else {
bomb();
return ;
}
}
}
}
else { // t5 > 1
t4 = 1; // %eax
t6 = 0x6032d0; // %edx, 0x6032d0 is the begin of node
do {
t6 = *(t6 + 0x8); // %rdx
t4 = t4 + 1; // %eax
} while (t4 != t5)
*(%rsp + 0x20 + 2 * t3) = t6;
t3 = t3 + 1; // %rsi
goto line4;
}
}
else { // t2 != 6
// means:
// a != b
// a != c
// a != d
// a != e
// a != f
// b != c
// b != d
// b != e
// b != f
// c != d
// c != e
// c != f
// d != e
// d != f
t7 = t2; // %ebx
t4 = t7; // %rax, line2
t4 = *(4 * t4 + %rsp) // %rax
if( *(%rsp + 4 * t4) == *t8) {
bomb();
return ;
}
t7 = t7 + 1; // %ebx
if( t7 <= 5) {
goto line2;
}
t9 = t9 + 4;
goto line3;
}
}In the line 0x41176, and the value of $rdx, we can infer there has a link. And the node length is 16.
And link infos:
| node name | address | first 4 bit | second 4 bit | second 8 bit |
|---|---|---|---|---|
| node1 | 0x6032d0 | 0x14c | 1 | 0x6032e0 |
| node2 | 0x6032e0 | 0x0a8 | 2 | 0x6032f0 |
| node3 | 0x6032f0 | 0x39c | 3 | 0x603300 |
| node4 | 0x603300 | 0x2b3 | 4 | 0x603310 |
| node5 | 0x603310 | 0x1dd | 5 | 0x603320 |
| node6 | 0x603320 | 0x1bb | 6 | 0x0 |
So, the structure of node is:
struct LinkNode{
int data;
int num;
long* node;
};And then 0x4011d0 reverse the list. Run after the line 0x40115e, from following code, we know the node is order by the largest to the smallest.
4011e7: 7d 05 jge 4011ee <phase_6+0xfa>
And from above table we can infer that:
node3.data > node4.data > node5.data > node6.data > node1.data > node2.data.
And because
4 3 2 1 6 5
And feedback information is:
Congratulations! You've defused the bomb!
But we don't completed all.
In the function phase_defused, we can find entry of secret_phase.
And set breakpoints b *0x4015e1, so that we can entry this function.
And look at following code:
4015f0: be 19 26 40 00 mov $0x402619,%esi
4015f5: bf 70 38 60 00 mov $0x603870,%edi
4015fa: e8 f1 f5 ff ff callq 400bf0 <__isoc99_sscanf@plt>
and execute step by step(command si), we can get info:
s=0x603870 <input_strings+240> "7 0 ", format=0x402619 "%d %d %s"
so, the phase_4 input need add a new string argument.
And in line 0x401609, there has a string compare, we should look the value of ($rdi), and using command x/s $rdi get the value DrEvil.
so, modify input 7 0 to 7 0 DrEvil.
Then output is:
Curses, you've found the secret phase! But finding it and solving it are quite different...
In the function secret_phase, we need input a string which included a number infer from function strtol, and the number must greater than or equal to 1 and less or equal to 0x3e8.( line 0x40125a ~ 0x401267).
Secondly, the address of 0x6030f0 pointer a tree struct because if we input command x/60a 0x6030f0, we can get:
0x6030f0 <n1>: 0x24 0x603110 <n21>
0x603100 <n1+16>: 0x603130 <n22> 0x0
0x603110 <n21>: 0x8 0x603190 <n31>
0x603120 <n21+16>: 0x603150 <n32> 0x0
0x603130 <n22>: 0x32 0x603170 <n33>
0x603140 <n22+16>: 0x6031b0 <n34> 0x0
0x603150 <n32>: 0x16 0x603270 <n43>
0x603160 <n32+16>: 0x603230 <n44> 0x0
0x603170 <n33>: 0x2d 0x6031d0 <n45>
0x603180 <n33+16>: 0x603290 <n46> 0x0
0x603190 <n31>: 0x6 0x6031f0 <n41>
0x6031a0 <n31+16>: 0x603250 <n42> 0x0
0x6031b0 <n34>: 0x6b 0x603210 <n47>
0x6031c0 <n34+16>: 0x6032b0 <n48> 0x0
0x6031d0 <n45>: 0x28 0x0
0x6031e0 <n45+16>: 0x0 0x0
0x6031f0 <n41>: 0x1 0x0
0x603200 <n41+16>: 0x0 0x0
0x603210 <n47>: 0x63 0x0
0x603220 <n47+16>: 0x0 0x0
0x603230 <n44>: 0x23 0x0
0x603240 <n44+16>: 0x0 0x0
0x603250 <n42>: 0x7 0x0
0x603260 <n42+16>: 0x0 0x0
0x603270 <n43>: 0x14 0x0
0x603280 <n43+16>: 0x0 0x0
0x603290 <n46>: 0x2f 0x0
0x6032a0 <n46+16>: 0x0 0x0
0x6032b0 <n48>: 0x3e9 0x0
0x6032c0 <n48+16>: 0x0 0x0
And the struct is:
And from the code:
401273: e8 8c ff ff ff callq 401204 <fun7>
401278: 83 f8 02 cmp $0x2,%eax
40127b: 74 05 je 401282 <secret_phase+0x40>
40127d: e8 b8 01 00 00 callq 40143a <explode_bomb>
we know the result of fun7 should be 2.
And then from fun7, the code can translate to:
func7(a, b){
// a %rdi
// b %rsi, input number
if( a == 0) {
return -1;
}
t1 = *a; // %edx;
if( t1 - b <= 0) {
t2 = 0; // %eax
if( t1 - b == 0) { // we can infer that we must input some lead node value
return t2;
}
else {
a = *(a + 16); // %rdi, a = a.right
fun7(a,b);
t2 = 1 + 2 * t2; // %eax
return t2;
}
t2 = t2 + t2; // %eax
return t2;
}
else {
a = *(a + 8); // %eax, a = a.left
fun7(a,b);
t2 = t2 + t2;
return t2;
}
}In order to get return value 2, the path should be left -> right -> right.
Ok, the result if 0x14, namely 20.
20
output:
Wow! You've defused the secret stage! Congratulations! You've defused the bomb!
