This article records the process of doing Bob experiment. The dormitory is closed these days. You can't go out except doing nucleic acid every day. It takes just a week to dismantle a bomb every day.
First, use objdump - D Bob > Bob Txt to get the disassembly code.
View Bob Phase in C file_ 1 to phase_ 6 input functions, corresponding to 6 bombs. Next, start with the first one.
phase_1
phase_1 called strings_not_equal, which in turn calls string_length, so from string_length seems to be translated into C language as follows:
/* * str in %rdi, length in %rax, p in %rdx * The function is to return the length of the string */ int string_length(char *str) { int length; if (str == NULL) return 0; char *p = str; while (p != NULL) { p++; length = p - str; } return length; }
Then look at strings_not_equal, translated into C language as follows:
/* * If two strings are different, 1 is returned; otherwise, 0 is returned * In order to make the code clearer, I made some modifications without changing the original meaning of the assembly code * str1 in %rdi, str2 in %rsi * str1_len in %r12, p1 in %rbx, p2 in %rbp */ int strings_not_equal(char *str1, char *str2) { char *p1 = str1; char *p2 = str2; int str1_len = string_length(str1); int str2_len = string_length(str2); if (str1_len != str2_len) return 1; char c1 = *p1; if (c1 == NULL) return 0; char c2 = *p2; while (c1 != NULL) { if (c1 != c2) return 1; // Next character p1++; p2++; c1 = *p1; c2 = *p2; } return 0; }
Get ready and start looking at 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 call 401338 <strings_not_equal> 400eee: 85 c0 test %eax,%eax 400ef0: 74 05 je 400ef7 <phase_1+0x17> 400ef2: e8 43 05 00 00 call 40143a <explode_bomb> 400ef7: 48 83 c4 08 add $0x8,%rsp 400efb: c3 ret
The combination of test + je means jump if% eax == 0. phase_1 is very simple. It is to compare whether the following input string is equal to a string in memory. You can see that the input string should be equal to the string at 0x402400 in the memory. Through the x/s 0x402400 of gdb, you can see that the string is Border relations with Canada have never been better
phase_2
In read_ six_ The sscanf is called in the numbers function. Note that there is a sentence mov $0x4025c3,% ESI. Through x/s 0x4025c3, the string is "% d% d% d% d% d". Therefore, the function reads 6 integers, and you can see that they are read from% rsi in turn. If the return value is less than 6, that is, less than 6 values are read, the bomb will be detonated. Therefore, it can be translated into:
/* * input in %rsi, arr in stack */ int read_six_numbers(char *input, int* arr) { int scan_num = sscanf(input, "%d %d %d %d %d %d", arr, &arr[1], &arr[2], &arr[3], &arr[4], &arr[5]); if (scan_num < 6) explode_bomb(); return scan_num; }
phase_2. The code is as follows:
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 call 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 call 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 call 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 ret
Debug analysis phase_2. First, you need to enter 6 integers separated by spaces. Then meet the first number n 1 n_{1} n1 must be 1 and the last number n i + 1 n_{i+1} ni+1 , is the previous number n i n_{i} Twice as much as ni. Translated as follows:
void phase_2(char *input) { int arr[6]; read_six_numbers(input, arr); // The first number must be 1 if (arr[0] != 1) explode_bomb(); for (int i = 1; i < 6; i++) { // The next one must be twice the previous one if (arr[i] != 2 * arr[i - 1]) explode_bomb(); } }
So the answer is 1 2 4 8 16 32
phase_3
The code is as follows:
0000000000400f43 <phase_3>: 400f43: 48 83 ec 18 sub $0x18,%rsp 400f47: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx 400f4c: 48 8d 54 24 08 lea 0x8(%rsp),%rdx 400f51: be cf 25 40 00 mov $0x4025cf,%esi 400f56: b8 00 00 00 00 mov $0x0,%eax 400f5b: e8 90 fc ff ff call 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 call 40143a <explode_bomb> 400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp) 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 jmp *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 400fab: eb 11 jmp 400fbe <phase_3+0x7b> 400fad: e8 88 04 00 00 call 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 call 40143a <explode_bomb> 400fc9: 48 83 c4 18 add $0x18,%rsp 400fcd: c3 ret
Note the fifth line. First, check that the string at 0x4025cf is "% d% d", and make sure to read in 2 integers. Through debugging, it is determined that the first parameter is at% rsp + 8 and the second parameter is at% rsp + 0xc. And the first parameter cannot be greater than 7.
For the indirect jump instruction JMP * 0x402470 (,% rax, 8), use x/wx 0x402470 to view its value as 0x400f7c, and use the same command to view the jump target address of each case of switch.
Note that the meaning of the indirect jump instruction is to jump to the address stored in 0x402470 + 8 *% rax, rather than the address stored in 0x402470 plus 8 *% rax.
Also, notice the difference between mov and lea. mov 0x0 (% rsp)% rax gives the data at% rsp to% rax, while lea 0x0 (% rsp)% rax gives the value of% rsp to% rax.
The following is the C language used for:
void phase_3(char *input) { int a, b; int scan_num = sscanf(input, "%d %d", &a, &b); if (scan_num < 2) explode_bomb(); int eax; if (a > 7 || a < 0) explode_bomb(); switch (a) { // 0x400f7c in 0x402470 case 0: eax = 0xcf; break; // 0x400fb9 in 0x402478 case 1: eax = 0x137; break; // 0x400f83 in 0x402480 case 2: eax = 0x2c3; break; // 0x400f8a in 0x402488 case 3: eax = 0x100; break; // 0x400f91 in 0x402490 case 4: eax = 0x185; break; // 0x400f98 in 0x402498 case 5: eax = 0xce; break; // 0x400f9f in 0x4024a0 case 6: eax = 0x2aa; break; // 0x400fa6 in 0x4024a8 case 7: eax = 0x147; break; } if (b != eax) explode_bomb(); }
Therefore, the following answers are correct: (0, 207) (1, 311) (2, 707) (3, 256) (4, 389) (5, 206) (6, 682) (7, 327)
phase_4
phase_ func4 called by 4:
0000000000400fce <func4>: 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 400fdb: 01 c8 add %ecx,%eax 400fdd: d1 f8 sar %eax 400fdf: 8d 0c 30 lea (%rax,%rsi,1),%ecx 400fe2: 39 f9 cmp %edi,%ecx 400fe4: 7e 0c jle 400ff2 <func4+0x24> 400fe6: 8d 51 ff lea -0x1(%rcx),%edx 400fe9: e8 e0 ff ff ff call 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 400ff9: 7d 0c jge 401007 <func4+0x39> 400ffb: 8d 71 01 lea 0x1(%rcx),%esi 400ffe: e8 cb ff ff ff call 400fce <func4> 401003: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax 401007: 48 83 c4 08 add $0x8,%rsp 40100b: c3 ret
Note that line 6 shr is a logical shift right instruction. When implemented in C language, it should first be changed to unsigned type and then shift right. In addition, SAR% eax represents SAR 1% eax. The corresponding C language functions are as follows:
/* * The function is binary search * As for the sign bit, if Lo > hi occurs, the mid will correctly represent the midpoint after processing. A very clever way to deal with it. * hi in %edx, lo in %esi, a in %edi */ int func4(int hi, int lo, int a) { int result = hi - lo; int mid = (unsigned)result >> 0x1f; // Logical shift right 31 bits to obtain symbol bits // If a-b is not negative, the result remains unchanged, otherwise + 1 result += mid; result >>= 1; // sar %eax // Mid = midpoint of B ~ A mid = lo + result; if (mid <= a) { result = 0; if (mid >= a) return result; // Update lower bound lo = mid + 1; result = func4(hi, lo, a); return (2 * result + 1); } else { // Update upper bound hi = mid - 1; result = func4(hi, lo, a); return 2 * result; } }
Now look at phase_4 Code:
000000000040100c <phase_4>: 40100c: 48 83 ec 18 sub $0x18,%rsp 401010: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx 401015: 48 8d 54 24 08 lea 0x8(%rsp),%rdx 40101a: be cf 25 40 00 mov $0x4025cf,%esi 40101f: b8 00 00 00 00 mov $0x0,%eax 401024: e8 c7 fb ff ff call 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 call 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 call 400fce <func4> 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 call 40143a <explode_bomb> 40105d: 48 83 c4 18 add $0x18,%rsp 401061: c3 ret
And phase_3, read two integers first.
The following is the corresponding C language:
void phase_4(char *input) { int a, b; int result = 0; int scan_num = sscanf(input, "%d %d", &a, &b); if (scan_num != 2) explode_bomb(); if (a >= 14 || a < 0) explode_bomb(); result = func4(14, 0, a); if (result != 0 || b != 0) explode_bomb(); }
Note that in func4, result+1 is only allowed when updating the lower bound. Therefore, on the premise that the first parameter a meets the boundary [0, 13], as long as all (including recursion) func4 called are satisfied, the lower bound will not be adjusted. The most obvious answer is 7, because it is called in phase_4: func4(14, 0, a), and the lower bound will not be adjusted once found.
Therefore, the final a optional values include: 0, 1, 3, 7; b can only take 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 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) 401078: 31 c0 xor %eax,%eax 40107a: e8 9c 02 00 00 call 40131b <string_length> 40107f: 83 f8 06 cmp $0x6,%eax 401082: 74 4e je 4010d2 <phase_5+0x70> 401084: e8 b1 03 00 00 call 40143a <explode_bomb> 401089: eb 47 jmp 4010d2 <phase_5+0x70> 40108b: 0f b6 0c 03 movzbl (%rbx,%rax,1),%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) 4010a4: 48 83 c0 01 add $0x1,%rax 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) 4010b3: be 5e 24 40 00 mov $0x40245e,%esi 4010b8: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi 4010bd: e8 76 02 00 00 call 401338 <strings_not_equal> 4010c2: 85 c0 test %eax,%eax 4010c4: 74 13 je 4010d9 <phase_5+0x77> 4010c6: e8 6f 03 00 00 call 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 4010e5: 00 00 4010e7: 74 05 je 4010ee <phase_5+0x8c> 4010e9: e8 42 fa ff ff call 400b30 <__stack_chk_fail@plt> 4010ee: 48 83 c4 20 add $0x20,%rsp 4010f2: 5b pop %rbx 4010f3: c3 ret
For the instruction at 4010b3, check that the string at memory 0x40245e is flyers.
For the instruction at 401099, check that the string at 0x4024b0 is maduiersnfotvbyl so you think you can stop the bob with ctrl-c, do you?, Since only the lower four bits are variable, only the 16 characters from the beginning, maduiers nfotvbyl, are used.
In addition, the following sentence will be displayed when using the ctrl-c command to end the process when running Bob, but Dr.evil will let us finish successfully after hesitating for a while. He will say: "Well... OK 😃”
On the whole, the value at% fs:0x28 is similar to a Canary value, that is, check whether the stack buffer overflows. No matter what the specific value is, you only need to know its function. I don't deal with this value when translating to C.
The corresponding C language is:
/* * This time, you should enter an address. Use this address as an offset address to find the matching characters in memory * input in %rdi, address in %rbx */ void phase_5(char* input) { char** address = input; int len = string_length(input); if (len != 6) explode_bomb(); char str[6]; for (int i = 0; i < 6; i++) { char* c = address[i]; // Gets the i th character entered c &= 0xf; // Keep the lower 4 bits c += 0x4024b0; // Change the 5th to 24th bits str[i] = *c; // Gets the character of the memory address } if (strings_not_equal(str, "flyers")) explode_bomb(); }
Remember 0x4024b0 as the address str. the problem is to spell flyers in the string maduiersnfotvbyl through different offset values. You can see that the address of f is str+9, and so on. You can get that the offset array of flyers is [9, f, e, 5, 6, 7]. Their corresponding binary sequences are (1001, 1111, 1110, 0101, 0110, 0111). By referring to the ASCII code table, you can find four groups of inputable answers that meet the requirements of the lower 4 bits, namely:) /.% & ' Ionefg, ionefg and Y_^UVW.
phase_6
The last question is long, but it's OK to split it into modules.
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 401103: 48 89 e6 mov %rsp,%rsi 401106: e8 51 03 00 00 call 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 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 call 40143a <explode_bomb> 401128: 41 83 c4 01 add $0x1,%r12d 40112c: 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: 8b 04 84 mov (%rsp,%rax,4),%eax 40113b: 39 45 00 cmp %eax,0x0(%rbp) 40113e: 75 05 jne 401145 <phase_6+0x51> 401140: e8 f5 02 00 00 call 40143a <explode_bomb> 401145: 83 c3 01 add $0x1,%ebx 401148: 83 fb 05 cmp $0x5,%ebx 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 401158: 4c 89 f0 mov %r14,%rax 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) 401166: 48 83 c0 04 add $0x4,%rax 40116a: 48 39 f0 cmp %rsi,%rax 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 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 401188: 48 89 54 74 20 mov %rdx,0x20(%rsp,%rsi,2) 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 40119a: 83 f9 01 cmp $0x1,%ecx 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 4011b0: 48 8d 44 24 28 lea 0x28(%rsp),%rax 4011b5: 48 8d 74 24 50 lea 0x50(%rsp),%rsi 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 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 call 40143a <explode_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 ret
Translate it into C language in modules:
/* * input in %rdi?, arr and newList in stack */ void phase_6(char* input) { // From% rsp int arr[6]; // From% rsp+0x20 int* newList[]; read_six_numbers(input, arr); /* * Each number must be in the range [1,6] and cannot be equal * Corresponding to 0x40110e ~ 0x401151 * i in %r13, j in %r12 */ for (int i = 0; i < 6; i++) { if (arr[i] > 6 || arr[i] < 1) explode_bomb(); for (int j = i + 1; j < 6; j++) if (arr[i] == arr[j]) explode_bomb(); } /* * Subtract the numbers in the array with 7 to form a new array sequence * Corresponding to 0x401153 ~ 0x40116d */ for (int i = 0; i < 6; i++) arr[i] = 7 - arr[i]; /* * In memory, a linked list is stored with the address head as the head node, and the node of the linked list stores an int pointer and a next pointer. * For the new array sequence, take each value as the sequence number of the linked list node, get the pointer to the corresponding node in turn, and store it in the newList * Corresponding to 0x40116f ~ 0x401174, 0x401183 ~ 0x401119d * num in %ecx, head in %edx, current in %rdx */ int head = 0x6032d0; int *current; // This is a wild pointer, but I wrote it first without affecting my understanding *current = head; int num; for (int i = 0; i < 6; i++) { num = arr[i]; *current = head; // In addition to using the data of the header node when num is 1, you should look down the linked list at other times if (temp > 1) { int offset = 1; // Backward traversal of linked list while (offset != temp) { current = current->next; offset++; } } newList[i] = current; } /* * Copy a copy of the linked list newList, and store its head node in% rcx * I don't understand what this code is for, but it doesn't affect the result * Corresponding to 0x4011ab ~ 0x4011d2 * cur in %rdx, pre in %rcx */ int *pre = newList[0]; int *cur = NULL; for (int i = 1; i < 6; i++) { cur = newList[i]; pre->next = cur; pre = cur; } cur->next = NULL; /* * Checks must be in descending order * cur in %rbx * Corresponding to 0x4011da ~ end */ cur = newList[0]; int n = 5; // Cycle 5 times while (n-- != 0) { int next_num = *(cur->next); // The int value of the node if (*cur < next_num) explode_bomb(); cur = cur->next; } }
You can see that there is actually a linked list in memory, and each node of it corresponds to an int value. We need to sort it in descending order again. The input string should be the serial number of the descending node.
Use the print /x *(int *) ($rsp) command to view the addresses of the six nodes in the linked list (6032d0, 6032e0, 6032f0, 603300, 603310, 603320). Then use print *(int *) 0xXXX to view the values stored in these nodes, which are (332, 168, 924, 691, 477, 443). Label them as 1 ~ 6, and the descending sequence is: (3 4 5 6 1 2). Because a new sequence is obtained by subtracting the input sequence with 7 in the program, the final answer is to subtract the previous sequence with 7, that is, 4 3 2 1 6 5.
The first five were completed in more than an hour. The sixth bomb was dismantled all afternoon. It's really complicated, T.T
secret_phase
In the disarm function phase_ Secret is called in defused_ Phase, and secret_phase calls fun7 again, so look at fun7 first. Its corresponding C language is:
int fun7(int *p, int q) { if (p == NULL) return -1; int result; int edx = *p; if (edx <= q) { if (edx == q) return 0; result = 2 * fun7(*(p+16), q) + 1; else result = 2 * fun7(*(p+8), q); return result; }
Then look at secret_phase, which calls the strtol function, which is declared as long int strtol (const char* str, char** endptr, int base), where STR is the input string, endptr is the pointer to the first character that cannot be converted. If it is NULL, it indicates that the parameter is invalid, and base indicates the base adopted by str. The code corresponding to fun7 is easy to associate with the register corresponding to each parameter.
In addition, the output string at 0x402438 is "Wow! You've defused the secret stage!".
/* * input in %rdi, endptr in %esi, base in %edx */ void secret_phase() { char *input = read_line(); int *rbx = strtol(input, NULL, 10); int eax = rbx - 1; if (eax > 1000 || eax < 0) explode_bomb(); int result = fun7(0x6030f0, (int)rbx); // 36 in 0x6030f0 if (result != 2) explode_bomb(); puts("Wow! You've defused the secret stage!"); }
Finally, translate phase_defused function. For convenience, pseudo code is written here, and Canary value is also not processed:
void phase_defused() { if (6 All the bombs were dismantled) { if (phase_4 The third parameter and"DrEvil"identical) { secret_phase();s } } }
It is not clear to judge the code where all six bombs are dismantled. I clarify the logic by debugging rather than looking at the code.
Back to fun7 function, this is a recursive call. It does not start to return layer by layer until * p and q are the same. In turn, it must be returned from 0 at the beginning.
To get 2, the return path should be
(
2
∗
0
+
1
)
∗
2
(2*0+1)*2
(2 * 0 + 1) * 2, that is, the first call enters the else block, the second call enters the if block, and the third call returns 0.
The value stored in address 0x6030f0 during the first call is 36; The address stored in the second p+8=0x6030f8 is 0x603110, and its value is 8; The address stored for the third time p+16=0x603120 is 0x603150, and its value is 22. So the final answer is 22.