Blog

[CS:APP] BombLab Phase3 Walkthrough

Published 6 min read

main

<+148> : Since the return value of read_line in the main function is stored in %rdi, we can infer that the input string is stored in %rdi.

0x0000555555401214 <+138>: callq 0x555555400f00 <puts@plt> 0x0000555555401219 <+143>: callq 0x555555401b0d <read_line> 0x000055555540121e <+148>: mov %rax,%rdi 0x0000555555401221 <+151>: callq 0x55555540136d <phase_3>

Testing confirmed that it matches.

(gdb) x/s $rdi 0x555555604760 <input_strings+160>: "50 100"

Analyzing __isoc99_sscanf

We already figured out a lot about the __isoc99_sscanf function in phase2.

  1. The integers we entered are converted into an array from the string stored in %rdi.
  2. A pointer to the first element is stored in %rsp.
  3. The number of integers entered is returned in %rax.

So when we enter 50 100, we get the situation on the right.

Phase_3

Dump of assembler code for function phase_3: 0x000055555540136d <+0>: sub $0x18,%rsp 0x0000555555401371 <+4>: mov %fs:0x28,%rax 0x000055555540137a <+13>: mov %rax,0x8(%rsp) 0x000055555540137f <+18>: xor %eax,%eax 0x0000555555401381 <+20>: lea 0x4(%rsp),%rcx 0x0000555555401386 <+25>: mov %rsp,%rdx 0x0000555555401389 <+28>: lea 0x1a5d(%rip),%rsi # 0x555555402ded 0x0000555555401390 <+35>: callq 0x555555400fc0 <__isoc99_sscanf@plt> 0x0000555555401395 <+40>: cmp $0x1,%eax 0x0000555555401398 <+43>: jle 0x5555554013b3 <phase_3+70> 0x000055555540139a <+45>: cmpl $0x7,(%rsp) 0x000055555540139e <+49>: ja 0x5555554013eb <phase_3+126> 0x00005555554013a0 <+51>: mov (%rsp),%eax 0x00005555554013a3 <+54>: lea 0x1796(%rip),%rdx # 0x555555402b40 0x00005555554013aa <+61>: movslq (%rdx,%rax,4),%rax 0x00005555554013ae <+65>: add %rdx,%rax 0x00005555554013b1 <+68>: jmpq *%rax 0x00005555554013b3 <+70>: callq 0x555555401a90 <explode_bomb> 0x00005555554013b8 <+75>: jmp 0x55555540139a <phase_3+45> 0x00005555554013ba <+77>: mov $0x8c,%eax 0x00005555554013bf <+82>: jmp 0x5555554013fc <phase_3+143> 0x00005555554013c1 <+84>: mov $0x3be,%eax 0x00005555554013c6 <+89>: jmp 0x5555554013fc <phase_3+143> 0x00005555554013c8 <+91>: mov $0x79,%eax 0x00005555554013cd <+96>: jmp 0x5555554013fc <phase_3+143> 0x00005555554013cf <+98>: mov $0x2c9,%eax 0x00005555554013d4 <+103>: jmp 0x5555554013fc <phase_3+143> 0x00005555554013d6 <+105>: mov $0x379,%eax 0x00005555554013db <+110>: jmp 0x5555554013fc <phase_3+143> 0x00005555554013dd <+112>: mov $0x347,%eax 0x00005555554013e2 <+117>: jmp 0x5555554013fc <phase_3+143> 0x00005555554013e4 <+119>: mov $0x39a,%eax 0x00005555554013e9 <+124>: jmp 0x5555554013fc <phase_3+143> 0x00005555554013eb <+126>: callq 0x555555401a90 <explode_bomb> 0x00005555554013f0 <+131>: mov $0x0,%eax 0x00005555554013f5 <+136>: jmp 0x5555554013fc <phase_3+143> 0x00005555554013f7 <+138>: mov $0x123,%eax 0x00005555554013fc <+143>: cmp %eax,0x4(%rsp) 0x0000555555401400 <+147>: je 0x555555401407 <phase_3+154> 0x0000555555401402 <+149>: callq 0x555555401a90 <explode_bomb> 0x0000555555401407 <+154>: mov 0x8(%rsp),%rax 0x000055555540140c <+159>: xor %fs:0x28,%rax 0x0000555555401415 <+168>: jne 0x55555540141c <phase_3+175> 0x0000555555401417 <+170>: add $0x18,%rsp 0x000055555540141b <+174>: retq 0x000055555540141c <+175>: callq 0x555555400f20 <__stack_chk_fail@plt>

<+40 ~ 49>

0x0000555555401395 <+40>: cmp $0x1,%eax 0x0000555555401398 <+43>: jle 0x5555554013b3 <phase_3+70> 0x000055555540139a <+45>: cmpl $0x7,(%rsp) 0x000055555540139e <+49>: ja 0x5555554013eb <phase_3+126>
0x00005555554013b3 <+70>: callq 0x555555401a90 <explode_bomb>
0x00005555554013eb <+126>: callq 0x555555401a90 <explode_bomb>

<+40> : Right now R[%eax] is the number of numbers entered. It compares the number of numbers entered with 1.

<+43> : If it's 1 or less, it jumps to <+70>. <+70> is the bomb, so you have to enter 2 or more numbers. (Since we assumed we entered 50, the bomb explodes.)

<+45> : (%rsp) is the first number we entered. It compares the first number with 7.

<+49> : If it's greater than 7 or less than 0 (since ja is an unsigned operation), it jumps to <+126>. That's the bomb, so the first number has to be between 0 and 7.

From here on, assume we entered 0 291.

<+51 ~ 68>

0x00005555554013a0 <+51>: mov (%rsp),%eax 0x00005555554013a3 <+54>: lea 0x1796(%rip),%rdx # 0x555555402b40 0x00005555554013aa <+61>: movslq (%rdx,%rax,4),%rax 0x00005555554013ae <+65>: add %rdx,%rax 0x00005555554013b1 <+68>: jmpq *%rax

<+51> : Stores the first number we entered in %eax.

<+54> : When the <+54> instruction runs, R[%rip] is the address of <+61>, 0x00005555554013aa. (%rip, the PC, points to the address of the next instruction to run.)

So R[%rdx] = 0x1796 + 0x00005555554013aa = 0x555555402b40.

<+61> : %rax means the first number we entered, and it takes that number, which is between 0 and 7, multiplies it by 4, adds it to %rdx, and stores the value at that address in %rax. I was able to find the referenced values of R[%rdx] + 4 * R[%rax] by testing in gdb.

First numberR[%rdx] + 4 * R[%rax]%rax = M[R[%rdx] + 4 * R[%rax]] Hex, Dec
00x555555402b400xffffffffffffe8b7, -5961
10x555555402b440xffffffffffffe87a, -6022
20x555555402b480xffffffffffffe881, -6015
30x555555402b5c0xffffffffffffe888, -6008
40x555555402b600xffffffffffffe88f, -6001
50x555555402b640xffffffffffffe896, -5994
60x555555402b680xffffffffffffe89d, -5987
70x555555402b7c0xffffffffffffe8a4, -5980

<+65> : Adds %rdx to %rax once more. ( R[%rdx] is 0x555555402b40, 93824990849856(demical) )

<+68> : Jumps to the address held in %rax. Marking where it jumps for each %rax number and organizing it gives the following.

First number%rax Hex, Decjump <+ ? >
00x5555554013F7, 93824990843895<+138>
10x5555554013BA, 93824990843834<+77>
20x5555554013C1, 93824990843841<+84>
30x5555554013C8, 93824990843848<+119>
40x5555554013CF, 93824990843855<+98>
50x5555554013D6, 93824990843862<+105>
60x5555554013DD, 93824990843869<+112>
70x5555554013E4, 93824990843876<+119>

<77 ~ 138>

0x00005555554013ba <+77>: mov $0x8c,%eax 0x00005555554013bf <+82>: jmp 0x5555554013fc <phase_3+143> 0x00005555554013c1 <+84>: mov $0x3be,%eax 0x00005555554013c6 <+89>: jmp 0x5555554013fc <phase_3+143> 0x00005555554013c8 <+91>: mov $0x79,%eax 0x00005555554013cd <+96>: jmp 0x5555554013fc <phase_3+143> 0x00005555554013cf <+98>: mov $0x2c9,%eax 0x00005555554013d4 <+103>: jmp 0x5555554013fc <phase_3+143> 0x00005555554013d6 <+105>: mov $0x379,%eax 0x00005555554013db <+110>: jmp 0x5555554013fc <phase_3+143> 0x00005555554013dd <+112>: mov $0x347,%eax 0x00005555554013e2 <+117>: jmp 0x5555554013fc <phase_3+143> 0x00005555554013e4 <+119>: mov $0x39a,%eax 0x00005555554013e9 <+124>: jmp 0x5555554013fc <phase_3+143> 0x00005555554013eb <+126>: callq 0x555555401a90 <explode_bomb> 0x00005555554013f0 <+131>: mov $0x0,%eax 0x00005555554013f5 <+136>: jmp 0x5555554013fc <phase_3+143> 0x00005555554013f7 <+138>: mov $0x123,%eax

Depending on the first number, it jumps and stores a value in %eax.

Organized by number, it looks like this.

First numberjump <+ ? >Demical : R[%eax]
0<+138>291
1<+77>140
2<+84>958
3<+119>922
4<+98>713
5<+105>889
6<+112>839
7<+119>922

<143 ~ 174>

0x00005555554013fc <+143>: cmp %eax,0x4(%rsp) 0x0000555555401400 <+147>: je 0x555555401407 <phase_3+154> 0x0000555555401402 <+149>: callq 0x555555401a90 <explode_bomb> 0x0000555555401407 <+154>: mov 0x8(%rsp),%rax 0x000055555540140c <+159>: xor %fs:0x28,%rax 0x0000555555401415 <+168>: jne 0x55555540141c <phase_3+175> 0x0000555555401417 <+170>: add $0x18,%rsp 0x000055555540141b <+174>: retq

<+143> : 0x4(%rsp) means the second number we entered. It's comparing the second number with R[%eax].

<+147> : If R[%eax] from the table above equals the second number, it jumps to <+154>.

<+149> : If the second number is different from R[%eax], the bomb explodes, so it turns out the second number has to equal R[%eax].

So the answer is the following.

First numberSecond number
0291
1140
2958
3922
4713
5889
6839
7922

Comments

Leaving a comment stores your nickname (or GitHub profile info). Privacy policy

GitHub