Search code examples
assemblybinaryx86-64reverse-engineering

How can I solve the binary bomblab phase 6?


This is my phase_6 disassembled code please help me to solve this. I know that the answer should be the number between 1 and 6.

     0x000055555555553e <+0>:   push   %r13
   0x0000555555555540 <+2>: push   %r12
   0x0000555555555542 <+4>: push   %rbp
   0x0000555555555543 <+5>: push   %rbx
   0x0000555555555544 <+6>: sub    $0x68,%rsp
   0x0000555555555548 <+10>:    mov    %fs:0x28,%rax
   0x0000555555555551 <+19>:    mov    %rax,0x58(%rsp)
   0x0000555555555556 <+24>:    xor    %eax,%eax
   0x0000555555555558 <+26>:    mov    %rsp,%r12
   0x000055555555555b <+29>:    mov    %r12,%rsi
   0x000055555555555e <+32>:    callq  0x5555555558c8 <read_six_numbers>
   0x0000555555555563 <+37>:    mov    $0x0,%r13d
   0x0000555555555569 <+43>:    jmp    0x555555555590 <phase_6+82>
   0x000055555555556b <+45>:    callq  0x5555555558a2 <explode_bomb>
   0x0000555555555570 <+50>:    jmp    0x55555555559f <phase_6+97>
   0x0000555555555572 <+52>:    add    $0x1,%ebx
   0x0000555555555575 <+55>:    cmp    $0x5,%ebx
   0x0000555555555578 <+58>:    jg     0x55555555558c <phase_6+78>
   0x000055555555557a <+60>:    movslq %ebx,%rax
   0x000055555555557d <+63>:    mov    (%rsp,%rax,4),%eax
   0x0000555555555580 <+66>:    cmp    %eax,0x0(%rbp)
   0x0000555555555583 <+69>:    jne    0x555555555572 <phase_6+52>
   0x0000555555555585 <+71>:    callq  0x5555555558a2 <explode_bomb>
   0x000055555555558a <+76>:    jmp    0x555555555572 <phase_6+52>
   0x000055555555558c <+78>:    add    $0x4,%r12
   0x0000555555555590 <+82>:    mov    %r12,%rbp
   0x0000555555555593 <+85>:    mov    (%r12),%eax
--Type <RET> for more, q to quit, c to continue without paging--c
   0x0000555555555597 <+89>:    sub    $0x1,%eax
   0x000055555555559a <+92>:    cmp    $0x5,%eax
   0x000055555555559d <+95>:    ja     0x55555555556b <phase_6+45>
   0x000055555555559f <+97>:    add    $0x1,%r13d
   0x00005555555555a3 <+101>:   cmp    $0x6,%r13d
   0x00005555555555a7 <+105>:   je     0x5555555555de <phase_6+160>
   0x00005555555555a9 <+107>:   mov    %r13d,%ebx
   0x00005555555555ac <+110>:   jmp    0x55555555557a <phase_6+60>
   0x00005555555555ae <+112>:   mov    0x8(%rdx),%rdx
   0x00005555555555b2 <+116>:   add    $0x1,%eax
   0x00005555555555b5 <+119>:   cmp    %ecx,%eax
   0x00005555555555b7 <+121>:   jne    0x5555555555ae <phase_6+112>
   0x00005555555555b9 <+123>:   mov    %rdx,0x20(%rsp,%rsi,8)
   0x00005555555555be <+128>:   add    $0x1,%rsi
   0x00005555555555c2 <+132>:   cmp    $0x6,%rsi
   0x00005555555555c6 <+136>:   je     0x5555555555e5 <phase_6+167>
   0x00005555555555c8 <+138>:   mov    (%rsp,%rsi,4),%ecx
   0x00005555555555cb <+141>:   mov    $0x1,%eax
   0x00005555555555d0 <+146>:   lea    0x202c39(%rip),%rdx        # 0x555555758210 <node1>
   0x00005555555555d7 <+153>:   cmp    $0x1,%ecx
   0x00005555555555da <+156>:   jg     0x5555555555ae <phase_6+112>
   0x00005555555555dc <+158>:   jmp    0x5555555555b9 <phase_6+123>
   0x00005555555555de <+160>:   mov    $0x0,%esi
   0x00005555555555e3 <+165>:   jmp    0x5555555555c8 <phase_6+138>
   0x00005555555555e5 <+167>:   mov    0x20(%rsp),%rbx
   0x00005555555555ea <+172>:   mov    0x28(%rsp),%rax
   0x00005555555555ef <+177>:   mov    %rax,0x8(%rbx)
   0x00005555555555f3 <+181>:   mov    0x30(%rsp),%rdx
   0x00005555555555f8 <+186>:   mov    %rdx,0x8(%rax)
   0x00005555555555fc <+190>:   mov    0x38(%rsp),%rax
   0x0000555555555601 <+195>:   mov    %rax,0x8(%rdx)
   0x0000555555555605 <+199>:   mov    0x40(%rsp),%rdx
   0x000055555555560a <+204>:   mov    %rdx,0x8(%rax)
   0x000055555555560e <+208>:   mov    0x48(%rsp),%rax
   0x0000555555555613 <+213>:   mov    %rax,0x8(%rdx)
   0x0000555555555617 <+217>:   movq   $0x0,0x8(%rax)
   0x000055555555561f <+225>:   mov    $0x5,%ebp
   0x0000555555555624 <+230>:   jmp    0x55555555562f <phase_6+241>
   0x0000555555555626 <+232>:   mov    0x8(%rbx),%rbx
   0x000055555555562a <+236>:   sub    $0x1,%ebp
   0x000055555555562d <+239>:   je     0x555555555640 <phase_6+258>
   0x000055555555562f <+241>:   mov    0x8(%rbx),%rax
   0x0000555555555633 <+245>:   mov    (%rax),%eax
   0x0000555555555635 <+247>:   cmp    %eax,(%rbx)
   0x0000555555555637 <+249>:   jle    0x555555555626 <phase_6+232>
   0x0000555555555639 <+251>:   callq  0x5555555558a2 <explode_bomb>
   0x000055555555563e <+256>:   jmp    0x555555555626 <phase_6+232>
   0x0000555555555640 <+258>:   mov    0x58(%rsp),%rax
   0x0000555555555645 <+263>:   xor    %fs:0x28,%rax
   0x000055555555564e <+272>:   jne    0x55555555565b <phase_6+285>
   0x0000555555555650 <+274>:   add    $0x68,%rsp
   0x0000555555555654 <+278>:   pop    %rbx
   0x0000555555555655 <+279>:   pop    %rbp
   0x0000555555555656 <+280>:   pop    %r12
   0x0000555555555658 <+282>:   pop    %r13
   0x000055555555565a <+284>:   retq   
   0x000055555555565b <+285>:   callq  0x555555554e50 <__stack_chk_fail@plt>

I have been working for days. I solved phase_1 ğhase_2, phase_3 and so on that is the phase_6. I know that the answer should be the numbers between 1-6. But ı could not find the answer. Please help me to solve this.


Solution

  • TL;DR - convert it to C, the either debug it, or refine it until it is readable. If you don't like C, you could convert it to a flow chart, or any other language that supports goto and flexible pointer manipulation.

    Convert to C:

    The first step is to identify the basic blocks of code; that is runs of instructions which execute without branching into or out of. I like C, so I would c+p the disassembly into an editor, and look for every jump instruction; like 0x000055555555558a <+76>: jmp 0x555555555572 <phase_6+52>. I would change it to:

    0x000055555555558a <+76>:    jmp    0x555555555572 <phase_6+52>
    */
        goto L_572;
    

    then I would find the corresponding instruction 0x0000555555555572 <+52>: add $0x1,%ebx and change it to:

    L_572:
    /*
     0x0000555555555572 <+52>:    add    $0x1,%ebx
    

    Once you have completed this, plus adding some extras for the prologue and epilogue, you should have a first approximation of it separated into blocks.

    Next, refine the areas around conditional branches -- as a general rule, if they jump backwards they are loops; forwards they are if's or loop tests.

    Now start translating the code into something more comprehensible; like C. From the prologue, work out what the local variables are:

       void f(long rdi) {
           struct localvars {
                 union {
                      long  l[13];
                      int   w[26];
                      /* ... */
                 };
           } sp;  /* because the stack pointer points here... */
           sp.l[10] = ((long *)tls())[5];  /* peculiar... */
           /* create some fake C variables for some registers: */
           long  rax, rdx, rsi, rbp, r12, r13, ... ;
           r12 = &sp;
        /*
           0x0000555555555544 <+6>: sub    $0x68,%rsp
           0x0000555555555548 <+10>:    mov    %fs:0x28,%rax
           0x0000555555555551 <+19>:    mov    %rax,0x58(%rsp)
           0x0000555555555556 <+24>:    xor    %eax,%eax
           0x0000555555555558 <+26>:    mov    %rsp,%r12
        */
    

    At each label, then examine the code, and convert it into a pseudo-high level langauge. Again, inserting equivalent C code:

       read_six_numbers(rdi, r12);
       r13 = 0;
       goto L_590;
    /*
       0x000055555555555b <+29>:    mov    %r12,%rsi
       0x000055555555555e <+32>:    callq  0x5555555558c8 <read_six_numbers>
       0x0000555555555563 <+37>:    mov    $0x0,%r13d
       0x0000555555555569 <+43>:    jmp    0x555555555590 <phase_6+82>
    */
    L_56b:
    

    and continue this process until you have translated the assembly into an equivalent C function. At that point, you should be able to feed the inputs from your assignment into the C function and observe it behaving like the example one. It is important to get to this step without prematurely optimising or generating fancy structures; there is time for that once you have a valid translation. If you make something that doesn't work, all you have accomplished is making more work!

    Once you reach this point, you can either refine the program until it is something readable, or step through it in a debugger to observe what it does.