CSAPP BOMB Lab

栏目: C · 发布时间: 5年前

内容简介:最近复习 CSAPP,BOMB Lab 作为 CSAPP 第三章的配套练习。做完这个练习也是花了较多时间,学到了很多。(好气啊)搞了半天没搞好汇编的高亮,只能先将就着把博客发出来,没高亮有点恶心,还请多见谅整个 Lab 主要用到了 objdump 和 gdb(lldb) 进行分析。

最近复习 CSAPP,BOMB Lab 作为 CSAPP 第三章的配套练习。做完这个练习也是花了较多时间,学到了很多。

(好气啊)搞了半天没搞好汇编的高亮,只能先将就着把博客发出来,没高亮有点恶心,还请多见谅

整个 Lab 主要用到了 objdump 和 gdb(lldb) 进行分析。

直接利用 objdump -d 获得汇编代码开始分析。一共有 6 个 phase,我们先从第一个开始。

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 

  </explode_bomb>
 </phase_1>
</strings_not_equal>

$0x402400 传给 %esi 寄存器,并调用 strings_not_equal 函数,然后判断 %eax 中是否为 0,为 0 则返回,非 0 则 BOMB!!

因此关键在于找出需要比较的 strings 是啥。这里就比较简单了,%esi 寄存器用于保存传递给调用函数的第二个参数,因此需要比对的字符串保存在 $0x402400 地址处。利用 gdb(这里使用 Mac 的 lldb 作为替代) 的 x 指令可以查看指定内存地址的值。

(lldb) x/s 0x402400
0x00402400: "Border relations with Canada have never been better."

关于第一个参数,是在调用 phase_1 函数时就传递进来的(%rdi 寄存器保存第一个参数),然后两个参数一起传入 strings_not_equal 函数

400e32:    e8 67 06 00 00           callq  40149e 
<read_line>
 
  400e37:    48 89 c7                 mov    %rax,%rdi
  400e3a:    e8 a1 00 00 00           callq  400ee0 
 <phase_1>
  
  400e3f:    e8 80 07 00 00           callq  4015c4 
  <phase_defused>

  </phase_defused>
 </phase_1>
</read_line>

利用 ida 反编译结果如下:

__int64 __fastcall phase_1(__int64 a1)
{
  __int64 result; // rax

  result = strings_not_equal(a1, "Border relations with Canada have never been better.");
  if ( (_DWORD)result )
    explode_bomb(a1, "Border relations with Canada have never been better.");
  return result;
}

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   

        </phase_2>
       </phase_2>
      </phase_2>
     </explode_bomb>
    </phase_2>
   </phase_2>
  </explode_bomb>
 </phase_2>
</read_six_numbers>

phase_2 中,可以看到函数先调用了 read_six_number 函数,并将当前栈指针( %rsp 保存的内容)作为参数( $rsi )传递进去,跟进该函数

40145c:    48 83 ec 18              sub    $0x18,%rsp
  401460:    48 89 f2                 mov    %rsi,%rdx
  401463:    48 8d 4e 04              lea    0x4(%rsi),%rcx
  401467:    48 8d 46 14              lea    0x14(%rsi),%rax
  40146b:    48 89 44 24 08           mov    %rax,0x8(%rsp)
  401470:    48 8d 46 10              lea    0x10(%rsi),%rax
  401474:    48 89 04 24              mov    %rax,(%rsp)
  401478:    4c 8d 4e 0c              lea    0xc(%rsi),%r9
  40147c:    4c 8d 46 08              lea    0x8(%rsi),%r8
  401480:    be c3 25 40 00           mov    $0x4025c3,%esi
  401485:    b8 00 00 00 00           mov    $0x0,%eax
  40148a:    e8 61 f7 ff ff           callq  400bf0 <__isoc99_sscanf@plt>
  40148f:    83 f8 05                 cmp    $0x5,%eax
  401492:    7f 05                    jg     401499 
<read_six_numbers 0x3d="">
 
  401494:    e8 a1 ff ff ff           callq  40143a 
 <explode_bomb>
  

  401499:    48 83 c4 18              add    $0x18,%rsp
  40149d:    c3                       retq  

 </explode_bomb>
</read_six_numbers>

与 phase_1 类似,查看 0x4025c3 处的内容

(lldb) x/s 0x4025c3
0x004025c3: "%d %d %d %d %d %d"

结合前面的汇编代码,得处函数的作用就是读取 6 个整数,保存在 (%rsi ~ %rsi+18)这个区间里,而 %rsi 中保存的地址其实就是 phase_2 函数中栈指针 %rsp 的内容。所以实际读取的数据已经保存在了 phase_2 的函数栈中

反编译结果如下:

__int64 __fastcall read_six_numbers(__int64 a1, __int64 a2)
{
  __int64 result; // rax

  result = __isoc99_sscanf(a1, &unk_4025C3, a2, a2 + 4, a2 + 8, a2 + 12, a2 + 16, a2 + 20);
  if ( (signed int)result <= 5 )
    explode_bomb();
  return result;
}

回到 phase_2 继续分析,读取整数后的下一行 cmpl $0x1,(%rsp) ,而 (%$rsp) 对应的值正式读取的第一个数,可以看出该数必须为 1,然后跳至 400f30 ,进入函数的核心,也就是一个循环。

汇编代码中利用空行隔离出了循环代码,逻辑比较简单,就是每次都在上一个的数的基础上翻倍,即以 2 为公比的等比数列,因此 phase_2 的答案就是 1 2 4 8 16 32

反编译结果:

__int64 __fastcall phase_2(__int64 a1)
{
  __int64 result; // rax
  __int64 *v2; // rbx
  __int64 v3; // [rsp-38h] [rbp-38h]
  __int64 v4; // [rsp-20h] [rbp-20h]

  read_six_numbers(a1, &v3);
  if ( (_DWORD)v3 != 1 )
    explode_bomb();
  v2 = (__int64 *)((char *)&v3 + 4);
  do {
    result = (unsigned int)(2 * *((_DWORD *)v2 - 1));
    if ( *(_DWORD *)v2 != (_DWORD)result )
      explode_bomb();
    v2 = (__int64 *)((char *)v2 + 4);
  } while ( v2 != &v4 );
  return result;
}

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           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>
  
  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     jmpq   *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           callq  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           callq  40143a 
             <explode_bomb>
              
  400fc9:    48 83 c4 18              add    $0x18,%rsp
  400fcd:    c3                       retq

             </explode_bomb>
            </phase_3>
           </phase_3>
          </explode_bomb>
         </phase_3>
        </phase_3>
       </phase_3>
      </phase_3>
     </phase_3>
    </phase_3>
   </phase_3>
  </phase_3>
 </explode_bomb>
</phase_3>

函数接受两个整数输入

(lldb) x/s 0x4025cf
0x004025cf: "%d %d"

从后面一连串的相似结构可以看出函数中包含一个 switch 语句,跳转到 0x402470+ %rax * 8 的位置

通过如下方式获得跳转表:

(lldb)  p/x *(int *)(0x402470)
(int) $1 = 0x00400f7c
(lldb)  p/x *(int *)(0x402470+8)
(int) $2 = 0x00400fb9
...

通过跳转表可获得 8 对解,分别对应应该传入的两个参数

反编译结果:

signed __int64 __fastcall phase_3(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6)
{
  signed __int64 result; // rax
  __int64 v7; // [rsp-10h] [rbp-10h]

  if ( (signed int)__isoc99_sscanf(a1, "%d %d", &v7, (char *)&v7 + 4, a5, a6) <= 1 )
    explode_bomb(a1, "%d %d");
  switch ( (_DWORD)v7 )
  {
    case 0:
      result = 207LL;
      break;
    case 1:
      result = 311LL;
      break;
    case 2:
      result = 707LL;
      break;
    case 3:
      result = 256LL;
      break;
    case 4:
      result = 389LL;
      break;
    case 5:
      result = 206LL;
      break;
    case 6:
      result = 682LL;
      break;
    case 7:
      result = 327LL;
      break;
    default:
      explode_bomb(a1, "%d %d");
      return result;
  }
  if ( (_DWORD)result != HIDWORD(v7) )
    explode_bomb(a1, "%d %d");
  return result;
}

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           callq  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           callq  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           callq  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           callq  40143a 
      <explode_bomb>
       
  40105d:    48 83 c4 18              add    $0x18,%rsp
  401061:    c3                       retq   

      </explode_bomb>
     </phase_4>
    </phase_4>
   </func4>
  </explode_bomb>
 </phase_4>
</phase_4>

第一部分逻辑与 phase_3 一样,读取两个整数设为 a1(a1 < 15), a2

第二部分对 a1 进行校验,将 a1, 0, 15, 作为参数传入 func4

第三部分校验 a2,只需要 a2=0 即可

func4 如下:

# 设 y in %eax, v1 in edi, v2 in %esi, v3 in %edx, v4 in %ecx
  400fce:    48 83 ec 08              sub    $0x8,%rsp 
  400fd2:    89 d0                    mov    %edx,%eax    # y = v3
  400fd4:    29 f0                    sub    %esi,%eax    # y -= v2
  400fd6:    89 c1                    mov    %eax,%ecx    # v4 = y
  400fd8:    c1 e9 1f                 shr    $0x1f,%ecx   # 右移 31 位取得 v4 的符号位
  400fdb:    01 c8                    add    %ecx,%eax    # y+= v4
  400fdd:    d1 f8                    sar    %eax         # y /= 2
  400fdf:    8d 0c 30                 lea    (%rax,%rsi,1),%ecx   #v4 = y+v2
  400fe2:    39 f9                    cmp    %edi,%ecx    v1 < v4?
  400fe4:    7e 0c                    jle    400ff2 
<func4 0x24="">
 
  400fe6:    8d 51 ff                 lea    -0x1(%rcx),%edx          # v3 = v4-1
400fe9:    e8 e0 ff ff ff           callq  400fce 
 <func4>
           # func4(v1, v2, v3)
  400fee:    01 c0                    add    %eax,%eax            # y *= 2
  400ff0:    eb 15                    jmp    401007 
  <func4 0x39="">
     # return
  400ff2:    b8 00 00 00 00           mov    $0x0,%eax        # y = 0
  400ff7:    39 f9                    cmp    %edi,%ecx        # v1 > v4?
  400ff9:    7d 0c                    jge    401007 
   <func4 0x39="">
    
  400ffb:    8d 71 01                 lea    0x1(%rcx),%esi   # v4 += 1
  400ffe:    e8 cb ff ff ff           callq  400fce 
    <func4>
        #func4(v1, v2, v3)
  401003:    8d 44 00 01              lea    0x1(%rax,%rax,1),%eax  # y = 2y+1

  401007:    48 83 c4 08              add    $0x8,%rsp
  40100b:    c3                       retq   

    </func4>
   </func4>
  </func4>
 </func4>
</func4>

func4 比较清晰,就是函数迭代,因此直接在源码加以注释

大致作用通过二分搜索逼近,使得 v1(传入的值即 a1) 与 v4([v3-v2+sign(v3-v2)]/2+v2 即 v2 与 v3 的中间值) 相等返回 0

__int64 __fastcall func4(__int64 a1, __int64 a2, int a3)
{
  signed int v3; // ecx
  __int64 result; // rax

  v3 = (a3 - (signed int)a2) / 2 + a2;
  if ( v3 > (signed int)a1 )
    return 2 * (unsigned int)func4(a1, a2, v3 - 1);
  result = 0LL;
  if ( v3 < (signed int)a1 )
    result = 2 * (unsigned int)func4(a1, (unsigned int)(v3 + 1), a3) + 1;
  return result;
}

由于当 v4 < v1 时 y = 2y + 1一定不会为 0,因此需要 v4 始终大于等于 v1 ,等于 v1 后返回 0,因此可选值为 7,3,1,0 均可

__int64 __fastcall phase_4(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6)
{
  __int64 v6; // rdi
  __int64 result; // rax
  __int64 v8; // [rsp-10h] [rbp-10h]

  if ( (unsigned int)__isoc99_sscanf(a1, "%d %d", &v8, (char *)&v8 + 4, a5, a6) != 2 || (unsigned int)v8 > 0xE )
    explode_bomb(a1, "%d %d");
  v6 = (unsigned int)v8;
  result = func4((unsigned int)v8, 0LL, 14);
  if ( (_DWORD)result || HIDWORD(v8) )
    explode_bomb(v6, 0LL);
  return result;
}

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           callq  40131b 
<string_length>
 
  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>
   
  401089:    eb 47                    jmp    4010d2 
   <phase_5 0x70="">
    

# LOOP:
  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           callq  401338 
     <strings_not_equal>
      
  4010c2:    85 c0                    test   %eax,%eax
  4010c4:    74 13                    je     4010d9 
      <phase_5 0x77="">
       
  4010c6:    e8 6f 03 00 00           callq  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           callq  400b30 <__stack_chk_fail@plt>
  4010ee:    48 83 c4 20              add    $0x20,%rsp
  4010f2:    5b                       pop    %rbx
  4010f3:    c3                       retq   


          </phase_5>
         </phase_5>
        </phase_5>
       </explode_bomb>
      </phase_5>
     </strings_not_equal>
    </phase_5>
   </phase_5>
  </explode_bomb>
 </phase_5>
</string_length>

代码中间有一个 LOOP 注释是我标记的,为函数的核心,其他部分比较简单就一笔带过。

前三段分配栈,canary 保护,并且要求输入的字符串长度为 6,经过循环变化后,字符串需要跟 0x40245e 位置处的字符 flyers 相等

我们把循环代码单独拎出来,此时各个寄存器的状态:

%eax=0, %rbx=%rdi(即输入的字符串的起始位置)

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="">

</phase_5>

由于 %rbx 存储字符串其实地址, %rax 起下标作用,因此 movzbl (%rbx,%rax,1),%ecx 的作用是将输入字符串的第 %rax 个字符放入 %ecx 中并做零扩展。

mov %cl,(%rsp); (%rsp),%rdx 取低 8 位(即一个字符)并通过内存间接放入 %rdx。

and $0xf,%edx; movzbl 0x4024b0(%rdx),%edx 将 edx 的低 4 位作为偏移量,基址为 0x4024b0 ,值重新放入 %edx 中

mov %dl,0x10(%rsp,%rax,1) 将 %edx 中的低 8 位放入 %rsp + %rax + 0x10 的位置。并在循环之后 4010b8: 0x10(%rsp),%rdi 将该内存位置保存的字符串作为参数传入 strings_not_equal 函数。

之后循环 6 次,对每个字符都做对应变换

综上, phase_5 遍历字符串,将每个字符的低 4 位作为偏移量,以 0x4024b0 为基址,获得一个新的字符串 flyers

(lldb) x/s 0x4024b0
0x004024b0: "maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"

原理理解了,直接写个脚本计算

total = 'maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?'
result = 'flyers'

offset = [total.find(result[i]) for i in range(6)]

# 因为计算出来的低 4 位,ascii 英文字母的高 4 位为 1100(小写) 或 1000(大写)
out = ''.join([chr(c+64) for c in offset])

print(out)  # IONEFG (如果为 c+64+32 即为 ionefg)

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

# 读取六个整数,第一个需要大于 5
  401100:    49 89 e5                 mov    %rsp,%r13
  401103:    48 89 e6                 mov    %rsp,%rsi
  401106:    e8 51 03 00 00           callq  40145c 
<read_six_numbers>
 
  40110b:    49 89 e6                 mov    %rsp,%r14
  40110e:    41 bc 00 00 00 00        mov    $0x0,%r12d

# --------------------- section 1 -----------------------

# 嵌套循环,读取的所有数字必须互异,且均小于 6
  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           callq  40143a 
  <explode_bomb>
   
  401128:    41 83 c4 01              add    $0x1,%r12d         # 循环变量 i
  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           callq  40143a 
     <explode_bomb>
      
  401145:    83 c3 01                 add    $0x1,%ebx          # 循环变量 j
  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="">
        

# --------------------- section 2 -----------------------

  401153:    48 8d 74 24 18           lea    0x18(%rsp),%rsi
  401158:    4c 89 f0                 mov    %r14,%rax        # %rsp
  40115b:    b9 07 00 00 00           mov    $0x7,%ecx

  # 循环读取 6 个数字 num,并将 7 - num 重新放回
  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="">
          


# --------------------- section 3 -----------------------

  # 内层循环,将数组中的数字作为序号顺序,把链表每个节点地址放入栈中(%rsp + 20 起始)
  401176:    48 8b 52 08              mov    0x8(%rdx),%rdx # 获得 next 指向的下一节点
  40117a:    83 c0 01                 add    $0x1,%eax      # 循环变量 j
  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  # 取得数组中的第 %esi/4(bytes) 的元素值
  40119a:    83 f9 01                 cmp    $0x1,%ecx           
  40119d:    7e e4                    jle    401183 
             <phase_6 0x8f="">
               # 值小于等于一直接入栈
  40119f:    b8 01 00 00 00           mov    $0x1,%eax     # 内层循环变量 j
  4011a4:    ba d0 32 60 00           mov    $0x6032d0,%edx
  4011a9:    eb cb                    jmp    401176 
              <phase_6 0x82="">
               

# --------------------- section 4 -----------------------

  # 对每个节点的 next 值重新赋值,反转链表
  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 

# --------------------- section 5 -----------------------

# 检查链表是否以节点的 num1 值为依据从大到小排列
  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           callq  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                       retq   

                   </phase_6>
                  </explode_bomb>
                 </phase_6>
                </phase_6>
               </phase_6>
              </phase_6>
             </phase_6>
            </phase_6>
           </phase_6>
          </phase_6>
         </phase_6>
        </phase_6>
       </phase_6>
      </phase_6>
     </explode_bomb>
    </phase_6>
   </phase_6>
  </explode_bomb>
 </phase_6>
</read_six_numbers>

phase_6 很长,需要一定(很多很多)的耐心。我将该部分分成了 6 个 section,各个 section 都有相应功能注释。

难点主要在于 section 3 与 4,再简单提一下。

(lldb) x/24wx 0x6032d0
0x006032d0: 0x0000014c 0x00000001 0x006032e0 0x00000000
0x006032e0: 0x000000a8 0x00000002 0x006032f0 0x00000000
0x006032f0: 0x0000039c 0x00000003 0x00603300 0x00000000
0x00603300: 0x000002b3 0x00000004 0x00603310 0x00000000
0x00603310: 0x000001dd 0x00000005 0x00603320 0x00000000
0x00603320: 0x000001bb 0x00000006 0x00000000 0x00000000

通过查看 401183: mov $0x6032d0,%edx 提到的 0x6032d0 处的值,可以发现每行第四列值均等于下一行第一列的地址。因此可以猜出此处应为一个链表。结构体应如下:

struct Node {
    int num;    // %rdx
    int num2;   // %rdx + 4
    Node *next; // %rex + 8
}

弄懂这个要点之后再看 3 与 4 应该就比较好懂了。

最后在 section 5 中需要链表根据节点的 num1 从大到小排列,因此 section4 中用于作为序号的数组就是 3 4 5 6 1 2 ,由于 section 2 中求了对 7 的补数,因此输入就是 4 3 2 1 6 5

该函数通过 ida 反编译出来也有 90 多行,且命名也难懂,就不贴了。

CSAPP BOMB Lab


以上所述就是小编给大家介绍的《CSAPP BOMB Lab》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

赛博人

赛博人

[美]约翰·苏勒尔 / 刘淑华、张海会 / 中信出版集团 / 2018-7 / 88.00

随着数字时代的飞速发展,网络空间正在深深影响着我们每个人的思想、感受和网络行为,其对我们的影响甚至比在现实生活中更大。为全面解析人类在网络空间中的感知、感觉、思维以及行为方式,帮助我们应对生活中面临的各种挑战,促进个人成长和改善心理健康,网络心理学专家和学科奠基人约翰·R.苏勒尔,根据20多年在不同网络环境里进行参与-观察式的实地调查所获得的成果,综合运用了行为心理学、认知心理学、人本主义心理学和......一起来看看 《赛博人》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具