BombLab experiment of CSAPP

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.

Keywords: C C++

Added by nsbrown on Wed, 05 Jan 2022 05:10:05 +0200