# act1

依次输入如下指令:

(gdb) disassemble main // show the assembly instructions in main

main 函数反汇编结果如下:

(gdb) disassemble main
Dump of assembler code for function main:
   0x0000000000401730 <+0>:     endbr64
   0x0000000000401734 <+4>:     push   %rbx
   0x0000000000401735 <+5>:     movslq %edi,%rdi
   0x0000000000401738 <+8>:     mov    %rsi,%rbx
   0x000000000040173b <+11>:    xor    %eax,%eax
   0x000000000040173d <+13>:    mov    -0x8(%rsi,%rdi,8),%rdx
   0x0000000000401742 <+18>:    lea    0xa8eb6(%rip),%rsi        # 0x4aa5ff
   0x0000000000401749 <+25>:    mov    $0x1,%edi
   0x000000000040174e <+30>:    callq  0x44b9a0 <__printf_chk>
   0x0000000000401753 <+35>:    movq   $0x0,(%rbx)
   0x000000000040175a <+42>:    pop    %rbx
   0x000000000040175b <+43>:    retq
End of assembler dump.

该汇编代码的过程如下:

int main(int argc, char*argv[])
{
    int ret = printf("%s\n",argv[argc-1]);
    return ret;
}

# act2

反汇编 main 函数:

(gdb) disas main
Dump of assembler code for function main:
   0x00000000004016c0 <+0>:     endbr64
   0x00000000004016c4 <+4>:     push   %rbx
   0x00000000004016c5 <+5>:     movslq %edi,%rdi
   0x00000000004016c8 <+8>:     mov    %rsi,%rbx
   0x00000000004016cb <+11>:    mov    -0x8(%rsi,%rdi,8),%rsi
   0x00000000004016d0 <+16>:    lea    0x9392d(%rip),%rdi        # 0x495004
   0x00000000004016d7 <+23>:    callq  0x401c75 <stc>
   0x00000000004016dc <+28>:    movq   $0x0,(%rbx)
   0x00000000004016e3 <+35>:    pop    %rbx
   0x00000000004016e4 <+36>:    retq
End of assembler dump.

输入下列指令用 16 进制打印 %rsi%rdi 的值:

(gdb) print /x $rsi
$1 = 0x7fffffffdf18
(gdb) print /x $rdi
$2 = 0x1

由此看 $rdi 应该是 argc , 而 $rsi 应该是 argv

再输入 disas stc ,获得如下结果:

(gdb) disas stc
Dump of assembler code for function stc:
   0x0000000000401c75 <+0>:     push   %rbx
   0x0000000000401c76 <+1>:     mov    %rsi,%rbx
   0x0000000000401c79 <+4>:     callq  0x4115d0 <puts>
   0x0000000000401c7e <+9>:     mov    %rbx,%rdi
   0x0000000000401c81 <+12>:    callq  0x4115d0 <puts>
   0x0000000000401c86 <+17>:    pop    %rbx
   0x0000000000401c87 <+18>:    retq
   0x0000000000401c88 <+19>:    nopl   0x0(%rax,%rax,1)
End of assembler dump.

将其转化为 C 语言函数应该是:

// a:rsi b:rdi
int stc(char* a, char* b)
{
   // 压入原 rbx
   rbx = rsi;
   puts(rbx);
   rdi = rbx;
   puts(rbx);
   // 恢复 rbx
   //nopl 是干嘛用的?
   return rax;
}

# act3

观察 main 函数的 C 代码,发现只要 compare 函数返回 1 即可通关。对 compare 函数进行反汇编可得:

(gdb) disas compare
Dump of assembler code for function compare:
   0x0000000000401c95 <+0>:     push   %rbx
   0x0000000000401c96 <+1>:     mov    %rdi,%rbx
   0x0000000000401c99 <+4>:     add    $0x5,%rbx
   0x0000000000401c9d <+8>:     add    %rsi,%rbx
   0x0000000000401ca0 <+11>:    cmp    $0x3b6d,%rbx
   0x0000000000401ca7 <+18>:    sete   %al
   0x0000000000401caa <+21>:    movzbq %al,%rax
   0x0000000000401cae <+25>:    pop    %rbx
   0x0000000000401caf <+26>:    retq
End of assembler dump.

还原其 C 代码如下:

// rdi:a rsi:b
int compare(int a,int b)
{
   int ret;
   // 压入原 rbx
   rbx = a;
   rbx += 0x5;
   rbx += b;
   rbx = a + b + 0x5;
   if(rbx == 0x3b6d) {
      ret = 1;
   }
   else {
      ret = 0;
   }
   // 恢复原 Rbx
   return ret;
}

发现 compare 就要求 a + b = 0x3b68(10进制15208) 。在 gdb 中只能输入 10 进制数字,如果输入 16 进制数字的话会被当成字符串。运行 ./act3 15208 0 ,成功!

gwen@gwen-virtual-machine:~/Documents/report/code/15213/Ch3 Machine Level Programming/rec4$ ./act3 15208 0
good args!

# act4

先查看 act4.c 的代码,看看是干嘛的:

void compute(int inArg)
{
    int st;
    inArg *= 3;
    st = inArg & 0xf;
    do {
        switch(st)
        {
            case 0: printf("Finish\n"); inArg = -1; break;
            case 1: inArg *= 2; break;
            case 2: inArg >>= 2; break;
            case 3: inArg &= 1; break;
            case 4: inArg--; break;
            default:  break;
        }
        st = inArg & 0xf;
    } while (inArg >= 0);
}
int main(int argc, char** argv)
{
    int inArg;
    if (argc == 1) {fprintf(stderr, "Please rerun with a positive number argument\n"); return 1;}
    inArg = atoi(argv[1]);
    if (inArg < 0) {fprintf(stderr, "Argument was not a positive integer\n"); return 1;}
    compute(inArg);
    return 0;
}

所以我们要让 gdb 打印 Finish ,st 就得为 0, inArg 就得为 0,那么就输入 1 个参数 0 即可。

gwen@gwen-virtual-machine:~/Documents/report/code/15213/Ch3 Machine Level Programming/rec4$ ./act4 0
Finish

我们要对汇编代码进行分析,就要使用 gdb 。输入 gdb act4 ,输入 disas main ,得到如下结果:

(gdb) disas main
Dump of assembler code for function main:
   0x0000000000401730 <+0>:     endbr64
   0x0000000000401734 <+4>:     sub    $0x8,%rsp
   0x0000000000401738 <+8>:     lea    0x938e1(%rip),%rdx        # 0x495020
   0x000000000040173f <+15>:    cmp    $0x1,%edi
   0x0000000000401742 <+18>:    je     0x40176d <main+61>
   0x0000000000401744 <+20>:    mov    0x8(%rsi),%rdi
   0x0000000000401748 <+24>:    mov    $0xa,%edx
   0x000000000040174d <+29>:    xor    %esi,%esi
   0x000000000040174f <+31>:    callq  0x410310 <strtoq>
   0x0000000000401754 <+36>:    test   %eax,%eax
   0x0000000000401756 <+38>:    js     0x401766 <main+54>
   0x0000000000401758 <+40>:    mov    %eax,%edi
   0x000000000040175a <+42>:    callq  0x401d20 <compute>
   0x000000000040175f <+47>:    xor    %eax,%eax
   0x0000000000401761 <+49>:    add    $0x8,%rsp
   0x0000000000401765 <+53>:    retq
   0x0000000000401766 <+54>:    lea    0x938e3(%rip),%rdx        # 0x495050
   0x000000000040176d <+61>:    mov    0xbef54(%rip),%rdi        # 0x4c06c8 <stderr>
   0x0000000000401774 <+68>:    mov    $0x1,%esi
   0x0000000000401779 <+73>:    xor    %eax,%eax
   0x000000000040177b <+75>:    callq  0x44c080 <__fprintf_chk>
   0x0000000000401780 <+80>:    mov    $0x1,%eax
   0x0000000000401785 <+85>:    jmp    0x401761 <main+49>
End of assembler dump.

这里代码分析如下:

// 初始化 $rdi = 0x1, $rsi = 0x7fffffffdf18。因此 rdi = argc, rsi = argv
int main(int argc, char* argv[])
{
   // 预留 8 个字节的栈空间
   rdx = rip + 0x938e1 -> 这句是干嘛的?
   if(edi == 1) {
      rdi = rip + 0xbef54; -> stderr
      esi = 0x1;
      eax = 0;
      call fprintf
      eax = 1;
      //rsp 恢复
      return eax;
   }
   else {
      rdi = *(rsi + 0x8);
      ecx = 0xa;
      esi = 0;
      call strtoq;
      if(eax < 0) {
         rdx = 0x938e3 + rip;
         rdi = *(rip + 0xbef54); -> stderr
         esi = 0x1;
         eax = 0;
         call fprintf
         eax = 1;
         //rsp 恢复
         return eax;
      }
      else {
         edi = eax;
         call compute
         eax = 0;
         //rsp 恢复
         return eax;
      }
   }
}

这里涉及到三个函数: fprintfstrtoq , computefprintf 通常和 stderr 结合在一起。

strtoq 进行反汇编得到如下结果:

(gdb) disas strtoq
Dump of assembler code for function strtoq:
   0x0000000000410310 <+0>:     endbr64
   0x0000000000410314 <+4>:     mov    $0xffffffffffffffa8,%rax
   0x000000000041031b <+11>:    xor    %ecx,%ecx
   0x000000000041031d <+13>:    mov    %fs:(%rax),%r8
   0x0000000000410321 <+17>:    jmpq   0x410370 <____strtoll_l_internal>
End of assembler dump.

compute 进行反汇编得到如下结果:

(gdb) disas compute
Dump of assembler code for function compute:
   0x0000000000401d20 <+0>:     endbr64
   0x0000000000401d24 <+4>:     lea    (%rdi,%rdi,2),%edx
   0x0000000000401d27 <+7>:     lea    0x932de(%rip),%rcx        # 0x49500c
   0x0000000000401d2e <+14>:    mov    %edx,%eax
   0x0000000000401d30 <+16>:    and    $0xf,%eax
   0x0000000000401d33 <+19>:    cmp    $0x4,%eax
   0x0000000000401d36 <+22>:    ja     0x401d53 <compute+51>
   0x0000000000401d38 <+24>:    movslq (%rcx,%rax,4),%rax
   0x0000000000401d3c <+28>:    add    %rcx,%rax
   0x0000000000401d3f <+31>:    notrack jmpq *%rax
   0x0000000000401d42 <+34>:    nopw   0x0(%rax,%rax,1)
   0x0000000000401d48 <+40>:    and    $0x1,%edx
   0x0000000000401d4b <+43>:    mov    %edx,%eax
   0x0000000000401d4d <+45>:    jmp    0x401d33 <compute+19>
   0x0000000000401d4f <+47>:    nop
   0x0000000000401d50 <+48>:    sar    $0x2,%edx
   0x0000000000401d53 <+51>:    mov    %edx,%eax
   0x0000000000401d55 <+53>:    and    $0xf,%eax
   0x0000000000401d58 <+56>:    test   %edx,%edx
   0x0000000000401d5a <+58>:    jns    0x401d33 <compute+19>
   0x0000000000401d5c <+60>:    retq
   0x0000000000401d5d <+61>:    nopl   (%rax)
   0x0000000000401d60 <+64>:    add    %edx,%edx
   0x0000000000401d62 <+66>:    jmp    0x401d53 <compute+51>
   0x0000000000401d64 <+68>:    nopl   0x0(%rax)
   0x0000000000401d68 <+72>:    sub    $0x1,%edx
   0x0000000000401d6b <+75>:    jmp    0x401d53 <compute+51>
   0x0000000000401d6d <+77>:    nopl   (%rax)
   0x0000000000401d70 <+80>:    lea    0x9328d(%rip),%rsi        # 0x495004
   0x0000000000401d77 <+87>:    mov    $0x1,%edi
   0x0000000000401d7c <+92>:    xor    %eax,%eax
   0x0000000000401d7e <+94>:    jmpq   0x44bfb0 <__printf_chk>
End of assembler dump.