【问题标题】:What is happening in this Assembly code?这个汇编代码中发生了什么?
【发布时间】:2015-06-22 11:35:01
【问题描述】:

背景:我正在做一个“二元炸弹”任务,我必须通过 6 个不同的阶段来“化解”炸弹。我可以使用调试器来帮助跟踪值并逐步执行程序集的每一行。我被困在第二阶段。

目标:找到满足以下程序的此阶段的“密码”。正确的密码会让我进入下一阶段

我想我知道的:它看起来就像是在尝试读取 6 个数字。当我拆解整个炸弹的 main() 函数时,我注意到一行带有 "%d %d %d %d %d %d" 所以这个阶段必须需要 6 个数字。此外,看起来有一个从 1 开始并在计数器大于 5 后结束的循环。它似乎在内存的 -0x4(%rbp) 部分中被跟踪。它看起来像在线,它将 %eax 中跟踪的内容与用户输入的内容进行比较(之前存储在 %edx 中以查看它们是否相等。如果不是,炸弹就会爆炸。如果它们是,它将继续循环。

0x400ec9 <phase_2>:     push   %rbp
0x400eca <phase_2+1>:   mov    %rsp,%rbp
0x400ecd <phase_2+4>:   sub    $0x30,%rsp
0x400ed1 <phase_2+8>:   mov    %rdi,-0x28(%rbp)
0x400ed5 <phase_2+12>:  lea    -0x20(%rbp),%rsi
0x400ed9 <phase_2+16>:  mov    -0x28(%rbp),%rdi
0x400edd <phase_2+20>:  callq  0x4013e9 <read_six_numbers>
0x400ee2 <phase_2+25>:  mov    -0x20(%rbp),%eax
0x400ee5 <phase_2+28>:  test   %eax,%eax
0x400ee7 <phase_2+30>:  jns    0x400eee <phase_2+37>
0x400ee9 <phase_2+32>:  callq  0x401983 <explode_bomb>
0x400eee <phase_2+37>:  movl   $0x1,-0x4(%rbp)
0x400ef5 <phase_2+44>:  jmp    0x400f1f <phase_2+86>
0x400ef7 <phase_2+46>:  mov    -0x4(%rbp),%eax
0x400efa <phase_2+49>:  cltq
0x400efc <phase_2+51>:  mov    -0x20(%rbp,%rax,4),%edx
0x400f00 <phase_2+55>:  mov    -0x4(%rbp),%eax
0x400f03 <phase_2+58>:  sub    $0x1,%eax
0x400f06 <phase_2+61>:  cltq
0x400f08 <phase_2+63>:  mov    -0x20(%rbp,%rax,4),%eax
0x400f0c <phase_2+67>:  add    -0x4(%rbp),%eax
0x400f0f <phase_2+70>:  add    $0x1,%eax
0x400f12 <phase_2+73>:  cmp    %eax,%edx
0x400f14 <phase_2+75>:  je     0x400f1b <phase_2+82>
0x400f16 <phase_2+77>:  callq  0x401983 <explode_bomb>
0x400f1b <phase_2+82>:  addl   $0x1,-0x4(%rbp)
0x400f1f <phase_2+86>:  cmpl   $0x5,-0x4(%rbp)
0x400f23 <phase_2+90>:  jle    0x400ef7 <phase_2+46>
0x400f25 <phase_2+92>:  leaveq
0x400f26 <phase_2+93>:  retq

我不知道的事情:这里有一些我不熟悉的新说明。 'cltq' 显然执行“将 %eax 符号扩展为 %rax”,但我不确定在这种情况下这意味着什么。另外,我不明白 'mov -0x20(%rbp, %rax, 4), %edx' 行以及它的确切作用。我知道它将存储在 0x20 中的内容移动到 %rdx 寄存器,但我不知道当时内存中的那个位置应该是什么。我知道在进行调试时,-0x20 处的 %rbp 包含我输入的 6 个值之一。但我不太确定 %rax 和 4 究竟做了什么。

如果有人能看到所需的 6 个数字是什么,那就太好了!如果没有,我希望有人至少可以阐明这段代码以及我必须帮助推动我朝着正确方向前进的问题!谢谢

我测试的一些错误解决方案: 1 2 3 4 5 6; 10 20 30 40 50 60

(我不想继续尝试,因为每次我引爆炸弹,我的作业成绩都会得到一分)

【问题讨论】:

  • 参见this link 以了解-0x20(%rbp, %rax, 4) 语法的解释。基本上它相当于一个数组访问,其中每个成员有 4 个字节(因此是 4 个字节),%rax 是数组的索引,而%rbp 是地址。我不确定-0x20 做了什么,但我认为它只是添加到最终地址的某种偏移量......
  • @nonsensickle 我刚刚使用调试器进入该行并在寄存器上显示信息。 %rax 表示它包含“0x1”或仅包含“1”。所以这可能是一个愚蠢的问题,但这是否意味着它是“1 * 4”值 = 4 或者它仍然只是 1 但存储在该数组中?
  • 是的,你是对的。但请记住,在 C 中,这是自动为您完成的。整数往往被存储为 32 位数字(意思是 4 个字节),所以如果你有一个它们的数组并且你有第一个整数的地址,那么你必须加 4 才能得到下一个整数,再加 4 才能得到后面的整数那...
  • 这个符号可以让你说出你的 array 中的每个项目有多大。所以你说这是我的起始地址,我想要第三个元素,每个元素长 4 个字节。所以你去计算你需要为我获取它的地址(这将是地址 + 3 * 4 = 地址 + 12)......

标签: assembly x86-64 reverse-engineering att sign-extension


【解决方案1】:

让我看看我能不能破解这段代码……

我试图把它变成 C 代码,所以这里是:

void read_six_numbers(int *array);
void explode();

int phase_2()
{
    int array[6];

    read_six_numbers(array);

    if (array[0] < 0)
    {
        explode();
    }

    for (int i = 1; i <= 5; ++i)
    {
        int cur  = array[i];   // %edx
        int prev = array[i-1]; // %eax

        prev += i;
        prev++;

        if (prev != cur)
        {
            explode();
        }
    }
}

因此,要通过测试,您将需要以下数字序列:

n, n + 2, (n + 2) + 3, ((n + 2) + 3) + 4, (((n + 2) + 3) + 4) + 5, ((((n + 2) + 3) + 4) + 5) + 6

n, n + 2, n + 5, n + 9, n + 14, n + 20

其中n 是正数或零。

这是我对你的汇编代码的注释:

0x400ec9 <phase_2>:     push   %rbp
0x400eca <phase_2+1>:   mov    %rsp,%rbp
0x400ecd <phase_2+4>:   sub    $0x30,%rsp          // Reserve some space on the stack...
0x400ed1 <phase_2+8>:   mov    %rdi,-0x28(%rbp)
0x400ed5 <phase_2+12>:  lea    -0x20(%rbp),%rsi   // Compute the address of -0x20(%rbp) and store it in %rsi (I'm assuming this is an argument for read_six_numbers below)
0x400ed9 <phase_2+16>:  mov    -0x28(%rbp),%rdi
0x400edd <phase_2+20>:  callq  0x4013e9 <read_six_numbers>
0x400ee2 <phase_2+25>:  mov    -0x20(%rbp),%eax       // From the code this looks like an array. Load its first element.
0x400ee5 <phase_2+28>:  test   %eax,%eax              // bitwise and that sets the flags.
0x400ee7 <phase_2+30>:  jns    0x400eee <phase_2+37>  // Jump not signed (jump if positive), so we can deduce Rule 1: First number is positive!
0x400ee9 <phase_2+32>:  callq  0x401983 <explode_bomb> // Explode!
0x400eee <phase_2+37>:  movl   $0x1,-0x4(%rbp)         // -0x4(%rbp) looks like a counter that starts at one so I'll call it `int i = 0;`
0x400ef5 <phase_2+44>:  jmp    0x400f1f <phase_2+86>   // Goto loop condition!
0x400ef7 <phase_2+46>:  mov    -0x4(%rbp),%eax         // Loop body:
0x400efa <phase_2+49>:  cltq   // Extend sign for %eax (converts %eax [int32] to %rax [int64])
0x400efc <phase_2+51>:  mov    -0x20(%rbp,%rax,4),%edx // Our read values are in an array located at -0x20(%rbp) I'll call it array from now on. So this line ends up being %edx = array[i]
0x400f00 <phase_2+55>:  mov    -0x4(%rbp),%eax         // reset i back to it's last value...
0x400f03 <phase_2+58>:  sub    $0x1,%eax               // Subtract 1 from i (are we checking our values backwards?)
0x400f06 <phase_2+61>:  cltq
0x400f08 <phase_2+63>:  mov    -0x20(%rbp,%rax,4),%eax // Load array[i] into %eax
0x400f0c <phase_2+67>:  add    -0x4(%rbp),%eax         // Add i to this value? Nice trick...
0x400f0f <phase_2+70>:  add    $0x1,%eax
0x400f12 <phase_2+73>:  cmp    %eax,%edx
0x400f14 <phase_2+75>:  je     0x400f1b <phase_2+82>
0x400f16 <phase_2+77>:  callq  0x401983 <explode_bomb>
0x400f1b <phase_2+82>:  addl   $0x1,-0x4(%rbp)
0x400f1f <phase_2+86>:  cmpl   $0x5,-0x4(%rbp)         // Loop condition: Check i against 5.
0x400f23 <phase_2+90>:  jle    0x400ef7 <phase_2+46>   // If i <= 5 goto Loop body
0x400f25 <phase_2+92>:  leaveq
0x400f26 <phase_2+93>:  retq

【讨论】:

  • 由于jle 应该是i &lt;= 5,因此最后一个数字也会被检查。您的程序集中的注释是正确的,但是 C 代码和解决方案是错误的。
  • 所以我可以尝试一个解决方案,例如:1 3 8 17 31 50 ?我知道上面的@Jester 说它不是正确的 C 代码,所以我害怕尝试这个,因为可能会受到惩罚。
  • 这是一个逆向工程挑战,第一步应该是禁用炸弹本身......这不是一件非常困难的事情。只需在explode_bomb 上放一个断点,不要让它继续。然后,您可以尝试任意数量的输入。当然,您也可以禁用网络。您也可以只组装这个单个函数并注释掉 explode_bomb 调用或使用占位符。
  • 那么如果我在explode_bomb 处设置了一个断点,如果在我尝试输入后断点,是否意味着输入失败?然后不要从那里继续并退出(q)并重试?
  • @Jester 感谢您的更正,您是对的!我已经更新了。
猜你喜欢
  • 1970-01-01
  • 2018-11-12
  • 1970-01-01
  • 2015-11-28
  • 1970-01-01
  • 1970-01-01
  • 2016-06-09
  • 1970-01-01
相关资源
最近更新 更多