Assembly C组装回路

Assembly C组装回路,assembly,x86-64,att,Assembly,X86 64,Att,我对大会上发生的事情有点困惑。我可以看到基本情况,如果不输入六个数字,炸弹就会爆炸并结束程序。逐行检查输入,如果六个数字不是负数,则进入循环。我在这里迷路了0x0000000000400f29:add-0x4(%rbp),%eax 这看起来很简单,但我真的不明白这里添加了什么。是否将-4相加,然后将其与0进行比较?如果相等,那么跳跃 我基本上是在寻找关于循环的澄清,以及什么样的输入模式将在循环中继续 转储 0x0000000000400f0c <+0>: push

我对大会上发生的事情有点困惑。我可以看到基本情况,如果不输入六个数字,炸弹就会爆炸并结束程序。逐行检查输入,如果六个数字不是负数,则进入循环。我在这里迷路了
0x0000000000400f29:add-0x4(%rbp),%eax

这看起来很简单,但我真的不明白这里添加了什么。是否将-4相加,然后将其与0进行比较?如果相等,那么跳跃

我基本上是在寻找关于循环的澄清,以及什么样的输入模式将在循环中继续

转储

   0x0000000000400f0c <+0>:     push   %rbp
   0x0000000000400f0d <+1>:     push   %rbx
   0x0000000000400f0e <+2>:     sub    $0x28,%rsp
   0x0000000000400f12 <+6>:     mov    %rsp,%rsi
   0x0000000000400f15 <+9>:     callq  0x40165a <read_six_numbers>
   0x0000000000400f1a <+14>:    cmpl   $0x0,(%rsp)
   0x0000000000400f1e <+18>:    jns    0x400f44 <phase_2+56>
   0x0000000000400f20 <+20>:    callq  0x401624 <explode_bomb>
   0x0000000000400f25 <+25>:    jmp    0x400f44 <phase_2+56>
   0x0000000000400f27 <+27>:    mov    %ebx,%eax
=> 0x0000000000400f29 <+29>:    add    -0x4(%rbp),%eax
   0x0000000000400f2c <+32>:    cmp    %eax,0x0(%rbp)
   0x0000000000400f2f <+35>:    je     0x400f36 <phase_2+42>
   0x0000000000400f31 <+37>:    callq  0x401624 <explode_bomb>
   0x0000000000400f36 <+42>:    add    $0x1,%ebx
   0x0000000000400f39 <+45>:    add    $0x4,%rbp
   0x0000000000400f3d <+49>:    cmp    $0x6,%ebx
   0x0000000000400f40 <+52>:    jne    0x400f27 <phase_2+27>
   0x0000000000400f42 <+54>:    jmp    0x400f50 <phase_2+68>
   0x0000000000400f44 <+56>:    lea    0x4(%rsp),%rbp
   0x0000000000400f49 <+61>:    mov    $0x1,%ebx
   0x0000000000400f4e <+66>:    jmp    0x400f27 <phase_2+27>
   0x0000000000400f50 <+68>:    add    $0x28,%rsp
   0x0000000000400f54 <+72>:    pop    %rbx
   0x0000000000400f55 <+73>:    pop    %rbp
   0x0000000000400f56 <+74>:    retq
End of assembler dump.
0x0000000000400f0c:推送%rbp
0x0000000000400f0d:推送%rbx
0x0000000000400f0e:子$0x28,%rsp
0x0000000000400f12:mov%rsp,%rsi
0x0000000000400f15:callq 0x40165a
0x0000000000400f1a:cmpl$0x0,(%rsp)
0x0000000000400f1e:jns 0x400f44
0x0000000000400f20:callq 0x401624
0x0000000000400f25:jmp 0x400f44
0x0000000000400f27:mov%ebx,%eax
=>0x0000000000400f29:添加-0x4(%rbp),%eax
0x0000000000400f2c:cmp%eax,0x0(%rbp)
0x0000000000400f2f:je 0x400f36
0x0000000000400f31:callq 0x401624
0x0000000000400f36:添加$0x1,%ebx
0x0000000000400f39:添加$0x4,%rbp
0x0000000000400f3d:cmp$0x6,%ebx
0x0000000000400f40:jne 0x400f27
0x0000000000400f42:jmp 0x400f50
0x0000000000400f44:lea 0x4(%rsp),%rbp
0x0000000000400f49:mov$0x1,%ebx
0x0000000000400f4e:jmp 0x400f27
0x0000000000400f50:添加$0x28,%rsp
0x0000000000400f54:弹出%rbx
0x0000000000400f55:弹出%rbp
0x0000000000400f56:retq
汇编程序转储结束。
读取六个数字

Dump of assembler code for function read_six_numbers:
=> 0x000000000040165a <+0>:     sub    $0x18,%rsp
   0x000000000040165e <+4>:     mov    %rsi,%rdx
   0x0000000000401661 <+7>:     lea    0x4(%rsi),%rcx
   0x0000000000401665 <+11>:    lea    0x14(%rsi),%rax
   0x0000000000401669 <+15>:    mov    %rax,0x8(%rsp)
   0x000000000040166e <+20>:    lea    0x10(%rsi),%rax
   0x0000000000401672 <+24>:    mov    %rax,(%rsp)
   0x0000000000401676 <+28>:    lea    0xc(%rsi),%r9
   0x000000000040167a <+32>:    lea    0x8(%rsi),%r8
   0x000000000040167e <+36>:    mov    $0x402871,%esi
   0x0000000000401683 <+41>:    mov    $0x0,%eax
   0x0000000000401688 <+46>:    callq  0x400c30 <__isoc99_sscanf@plt>
   0x000000000040168d <+51>:    cmp    $0x5,%eax
   0x0000000000401690 <+54>:    jg     0x401697 <read_six_numbers+61>
   0x0000000000401692 <+56>:    callq  0x401624 <explode_bomb>
   0x0000000000401697 <+61>:    add    $0x18,%rsp
   0x000000000040169b <+65>:    retq
End of assembler dump.
函数读取六个数字的汇编程序代码转储: =>0x000000000040165a:sub$0x18,%rsp 0x000000000040165e:mov%rsi,%rdx 0x0000000000401661:lea 0x4(%rsi),%rcx 0x0000000000401665:lea 0x14(%rsi),%rax 0x0000000000401669:mov%rax,0x8(%rsp) 0x000000000040166e:lea 0x10(%rsi),%rax 0x0000000000401672:mov%rax,(%rsp) 0x0000000000401676:lea 0xc(%rsi),%r9 0x000000000040167a:lea 0x8(%rsi),%r8 0x000000000040167e:mov$0x402871,%esi 0x0000000000401683:mov$0x0,%eax 0x0000000000401688:callq 0x400c30 0x000000000040168d:cmp$0x5,%eax 0x0000000000401690:jg 0x401697 0x0000000000401692:callq 0x401624 0x0000000000401697:添加$0x18,%rsp 0x000000000040169b:retq 汇编程序转储结束。
粗略地看,整个过程归结为:

void read_six_numbers(const char *sz, int numbers[6]) {
    // the format string is inferred from the context,
    // to see its actual value you should look at 0x402871
    if(sscanf(sz, "%d %d %d %d %d %d", &numbers[0], &numbers[1], &numbers[2], &numbers[3], &numbers[4], &numbers[5])<6) explode_bomb();
}

void phase_2(const char *sz) {
    int numbers[6];
    read_six_numbers(sz, numbers);
    if(numbers[0] < 0) explode_bomb();
    for(int i=1; i!=6; ++i) {
        int a = i + numbers[i-1];
        if(numbers[i]!=a) explode_bomb();
    }
}
它在rsi中接收一个参数,它是指向调用方堆栈上某个内容的指针。常规SystemV调用约定使用
rsi
作为第二个参数,并且
read_six_numbers
reads
rdi
(隐式地,我们将在后面看到)。因此,我们可以假设
phase_2
确实在
rdi
中接收到一个参数,并将其留在那里,直接传递给
读取六个数字

在“经典”的开场白之后,它为当地人保留了一堆

<+0>:     sub    rsp,0x18
rsi
现在被设置为某个固定地址1,考虑到它的地址和它在
rsi
中的事实,它看起来真的像一个字符串文字或一个全局值(因此它将成为下面的
sscanf
的第二个参数)

因此,这是到
sscanf
2-它需要将参数(按顺序)传递到
rdi
rsi
rdx
rcx
r8
r9
,以及堆栈上的其余参数,并将
ax
设置为存储在XMM寄存器中的浮点参数的数量(此处为零,因此为
mov
3)

如果我们把迄今为止收集到的数据放在一起,我们可以推断:

  • rdi
    是一个
    const char*
    ,是要读取的六个数字的源字符串
  • rsi
    包含一个指向至少六个
    int
    数组的指针(请参阅它加载偏移量为4的寄存器-AKA
    sizeof(int)
    ?),这些寄存器是从
    rdi
    中的字符串读取的
  • 最可能的情况是,如果我们查看
    0x402871
    ,我们会看到类似于
    %d%d%d%d%d“
因此,我们可以开始编写此函数的暂定定义:

void read_six_numbers(const char *sz, int numbers[6]) {
    int eax = sscanf(sz, "%d %d %d %d %d %d",
            &numbers[0], &numbers[1], &numbers[2],
            &numbers[3], &numbers[4], &numbers[5]);
    ...
}
注意,这里我写了
数字[6]
仅作为提醒,对于C语言,数组参数中的大小被忽略-它只是一个常规指针;此外,我将
void
写为返回类型,因为我看到调用此函数后,在调用代码中似乎没有人对
rax
eax
感兴趣

<+68>:    add    rsp,0x28
<+72>:    pop    rbx
<+73>:    pop    rbp
<+74>:    ret
然后:

然后,在+61和+65处有标准函数尾声(修复堆栈并返回)

所以,总而言之,我们可以把整个事情写成

void read_six_numbers(const char *sz, int numbers[6]) {
    if(sscanf(sz, "%d %d %d %d %d %d",
            &numbers[0], &numbers[1], &numbers[2],
            &numbers[3], &numbers[4], &numbers[5] < 6) {
        explode_bomb();
    }
}
通常的开场白;40字节的局部变量,保存
rbp
rbx
,因为它们将被使用(两者都是被调用方保存的寄存器);注意这里
rbp
不是用作堆栈帧指针,而是用作“常规”寄存器

<+6>:     mov    rsi,rsp
<+9>:     call   0x40165a <read_six_numbers>
检查第一个号码(
[rsp]
*numbers
numbers[0]
)是否为负数;如果是,请跳过对
爆炸炸弹的呼叫

(要了解它的工作原理,请记住
cmp
执行减法而不保存结果,但只执行与之对应的标志,因此
[rsp]-0
是普通的
[rsp]
,而
jns
表示如果不是符号位则跳转,因此如果
cmp
的结果为非负,则跳转)

让我们试着推测一下到目前为止我们所拥有的:

ret_type? phase_2(const char *sz) {
    int numbers[6];
    read_six_numbers(sz, numbers);
    if(numbers[0]<0) explode_bomb();
    ...
}
如果您快速查看一下跳跃,您将看到:

  • 小街区
    <+51>:    cmp    eax,0x5
    <+54>:    jg     0x401697 <read_six_numbers+61>
    <+56>:    call   0x401624 <explode_bomb>
    <+61>:    add    rsp,0x18
    <+65>:    ret
    
    if(eax<6) explode_bomb();
    
    void read_six_numbers(const char *sz, int numbers[6]) {
        if(sscanf(sz, "%d %d %d %d %d %d",
                &numbers[0], &numbers[1], &numbers[2],
                &numbers[3], &numbers[4], &numbers[5] < 6) {
            explode_bomb();
        }
    }
    
    <+0>:     push   rbp
    <+1>:     push   rbx
    <+2>:     sub    rsp,0x28
    
    <+6>:     mov    rsi,rsp
    <+9>:     call   0x40165a <read_six_numbers>
    
    <+14>:    cmp    [rsp],0
    <+18>:    jns    0x400f44 <phase_2+56>
    <+20>:    call  0x401624 <explode_bomb>
    <+25>:    jmp    0x400f44 <phase_2+56>
    
    ret_type? phase_2(const char *sz) {
        int numbers[6];
        read_six_numbers(sz, numbers);
        if(numbers[0]<0) explode_bomb();
        ...
    }
    
    <+56>:    lea    rbp,[rsp+4]
    <+61>:    mov    ebx,1
    <+66>:    jmp    0x400f27 <phase_2+27>
    
    <+27>:    mov    eax,ebx
    <+29>:    add    eax,[rbp-4]
    <+32>:    cmp    [rbp],eax
    <+35>:    je     0x400f36 <phase_2+42>
    <+37>:    call   0x401624 <explode_bomb>
    <+42>:    add    ebx,1
    <+45>:    add    rbp,4
    <+49>:    cmp    ebx,6
    <+52>:    jne    0x400f27 <phase_2+27>
    <+54>:    jmp    0x400f50 <phase_2+68>
    
    <+27>:    mov    eax,ebx
    <+29>:    add    eax,[rbp-4]
    <+32>:    cmp    [rbp],eax
    <+35>:    je     0x400f36 <phase_2+42>
    <+37>:    call   0x401624 <explode_bomb>
    
    <+42>:    add    ebx,1
    <+45>:    add    rbp,4
    
    <+49>:    cmp    ebx,6
    <+52>:    jne    0x400f27 <phase_2+27>
    <+54>:    jmp    0x400f50 <phase_2+68>
    
    <+68>:    add    rsp,0x28
    <+72>:    pop    rbx
    <+73>:    pop    rbp
    <+74>:    ret
    
    void phase_2(const char *sz) {
        int numbers[6];
        read_six_numbers(sz, numbers);
        if(numbers[0]<0) explode_bomb();
        int ebx = 1;
        int *rbp = &numbers[1];
        do {
            int eax = ebx + rbp[-1];
            if(eax != rbp[0]) explode_bomb();
            ++ebx;
            ++rbp;
        } while(ebx!=6);
    }
    
    void phase_2(const char *sz) {
        int numbers[6];
        read_six_numbers(sz, numbers);
        if(numbers[0]<0) explode_bomb();
        for(int i=1; i<6; ++i) {
            int n = i + numbers[i-1];
            if(n != numbers[i]) explode_bomb();
        }
    }