X86 二元炸弹拆除。解码转储-第2阶段 0x08048b94:推送%esi 0x08048b95:推送%ebx 0x08048b96:子$0x34,%esp 0x08048b99:lea 0x18(%esp),%eax 0x08048b9d:mov%eax,0x4(%esp) 0x08048ba1:mov 0x40(%esp),%eax 0x08048ba5:mov%eax,(%esp) 0x08048ba8:调用0x804920a 0x08048bad:cmpl$0x0,0x18(%esp) 0x08048bb2:jne 0x8048bbb 0x08048bb4:cmpl$0x1,0x1c(%esp) 0x08048bb9:je 0x8048bda 0x08048bbb:调用0x80490d7 0x08048bc0:jmp 0x8048bda 0x08048bc2:mov-0x8(%ebx),%eax 0x08048bc5:添加-0x4(%ebx),%eax 0x08048bc8:cmp%eax,(%ebx) 0x08048bca:je 0x8048bd1 0x08048bcc:调用0x80490d7 0x08048bd1:添加$0x4,%ebx 0x08048bd4:cmp%esi,%ebx 0x08048bd6:jne 0x8048bc2 ---键入以继续,或键入q以退出--- 0x08048bd8:jmp 0x8048be4 0x08048bda:lea 0x20(%esp),%ebx 0x08048bde:lea 0x30(%esp),%esi 0x08048be2:jmp 0x8048bc2 0x08048be4:添加$0x34,%esp 0x08048be7:弹出%ebx 0x08048be8:弹出%esi 0x08048be9:ret

X86 二元炸弹拆除。解码转储-第2阶段 0x08048b94:推送%esi 0x08048b95:推送%ebx 0x08048b96:子$0x34,%esp 0x08048b99:lea 0x18(%esp),%eax 0x08048b9d:mov%eax,0x4(%esp) 0x08048ba1:mov 0x40(%esp),%eax 0x08048ba5:mov%eax,(%esp) 0x08048ba8:调用0x804920a 0x08048bad:cmpl$0x0,0x18(%esp) 0x08048bb2:jne 0x8048bbb 0x08048bb4:cmpl$0x1,0x1c(%esp) 0x08048bb9:je 0x8048bda 0x08048bbb:调用0x80490d7 0x08048bc0:jmp 0x8048bda 0x08048bc2:mov-0x8(%ebx),%eax 0x08048bc5:添加-0x4(%ebx),%eax 0x08048bc8:cmp%eax,(%ebx) 0x08048bca:je 0x8048bd1 0x08048bcc:调用0x80490d7 0x08048bd1:添加$0x4,%ebx 0x08048bd4:cmp%esi,%ebx 0x08048bd6:jne 0x8048bc2 ---键入以继续,或键入q以退出--- 0x08048bd8:jmp 0x8048be4 0x08048bda:lea 0x20(%esp),%ebx 0x08048bde:lea 0x30(%esp),%esi 0x08048be2:jmp 0x8048bc2 0x08048be4:添加$0x34,%esp 0x08048be7:弹出%ebx 0x08048be8:弹出%esi 0x08048be9:ret,x86,reverse-engineering,X86,Reverse Engineering,这是我的汇编程序转储,用于二进制炸弹拆除实验室的特定阶段。我必须输入六个数字来破解代码并进入下一阶段。我一直在理解这一点,特别是在以下部分: 0x08048b94 <+0>:push %esi 0x08048b95 <+1>: push %ebx 0x08048b96 <+2>: sub $0x34,%esp 0x08048b99 <+5>: lea 0x18(%esp),%eax 0x08048b9d <

这是我的汇编程序转储,用于二进制炸弹拆除实验室的特定阶段。我必须输入六个数字来破解代码并进入下一阶段。我一直在理解这一点,特别是在以下部分:

0x08048b94 <+0>:push   %esi
0x08048b95 <+1>:    push   %ebx
0x08048b96 <+2>:    sub    $0x34,%esp
0x08048b99 <+5>:    lea    0x18(%esp),%eax
0x08048b9d <+9>:    mov    %eax,0x4(%esp)
0x08048ba1 <+13>:   mov    0x40(%esp),%eax
0x08048ba5 <+17>:   mov    %eax,(%esp)
0x08048ba8 <+20>:   call   0x804920a <read_six_numbers>
0x08048bad <+25>:   cmpl   $0x0,0x18(%esp)
0x08048bb2 <+30>:   jne    0x8048bbb <phase_2+39>
0x08048bb4 <+32>:   cmpl   $0x1,0x1c(%esp)
0x08048bb9 <+37>:   je     0x8048bda <phase_2+70>
0x08048bbb <+39>:   call   0x80490d7 <explode_bomb>
0x08048bc0 <+44>:   jmp    0x8048bda <phase_2+70>
0x08048bc2 <+46>:   mov    -0x8(%ebx),%eax
0x08048bc5 <+49>:   add    -0x4(%ebx),%eax
0x08048bc8 <+52>:   cmp    %eax,(%ebx)
0x08048bca <+54>:   je     0x8048bd1 <phase_2+61>
0x08048bcc <+56>:   call   0x80490d7 <explode_bomb>
0x08048bd1 <+61>:   add    $0x4,%ebx
0x08048bd4 <+64>:   cmp    %esi,%ebx
0x08048bd6 <+66>:   jne    0x8048bc2 <phase_2+46>
---Type <return> to continue, or q <return> to quit---<return>
0x08048bd8 <+68>:   jmp    0x8048be4 <phase_2+80>
0x08048bda <+70>:   lea    0x20(%esp),%ebx
0x08048bde <+74>:   lea    0x30(%esp),%esi
0x08048be2 <+78>:   jmp    0x8048bc2 <phase_2+46>
0x08048be4 <+80>:   add    $0x34,%esp
0x08048be7 <+83>:   pop    %ebx
0x08048be8 <+84>:   pop    %esi
0x08048be9 <+85>:   ret 
0x08048bc2:mov-0x8(%ebx),%eax
0x08048bc5:添加-0x4(%ebx),%eax

如果有人能帮我执行这个汇编代码,那就太好了。谢谢

好的,这将是一个很长的答案,但你可以这样回答:

让我从你关于+46/+49行的具体问题开始。他们所做的是:

  • 将地址[ebx-8]处的内存值写入eax
  • 将地址[ebx-4]处的内存值添加到eax
  • 因此,它有效地设置了
    eax=[ebx-8]+[ebx-4]
    ,其中
    []
    表示解除引用/内存访问
您的代码正在使用AT&T语法。我个人觉得它非常混乱,很难阅读。从现在起,我将使用英特尔语法。很抱歉,如果我们留在AT&T,我可能无法进一步给出我的答案

主要区别在于:

  • AT&T将源代码放在末尾,目标代码放在开头,而Intel则相反。例如,AT&T中的
    mov%ebx,%eax
    将是英特尔中的
    mov-eax,ebx
  • 英特尔没有大多数前缀,如
    %
    *
  • 对于内存访问(或使用
    lea
    进行有效地址计算),AT&T使用
    offset(base)
    ,而Intel使用
    [base+offset]
    (我个人认为这更容易阅读),因此
    mov-0x8(%ebx),%eax
    从您的示例中变成英特尔语法中的
    mov-eax[ebx-8]
  • 它不是像
    l
    这样的命令后缀,而是在需要时使用
    dword ptr
    等语法指定操作数大小(例如
    cmpl
    变成
    cmp dword ptr
好的,所以我试着整理一下整个代码

首先,我将所有内容转换为Intel语法并创建了标签:

0x08048bc2 <+46>:   mov    -0x8(%ebx),%eax
0x08048bc5 <+49>:   add    -0x4(%ebx),%eax
然后我实际编译了代码,然后将其加载到IDA(一个很好的反汇编程序)中。我为变量设置了一些标签,并创建了一个结构
SIX\u NUMBERS\u STRUCT
,它基本上是一个由6个DWORD数字组成的数组。对我来说,它看起来像是
phase_2
函数接受一个参数,可能是您输入的数字的某种字符串。然后,它调用
read_six_numbers
,并将上述输入指针与指向6个DWORD值结构的指针一起传递(该结构保持在[esp+3C-24]=[esp+18],因此可以使用[esp+18]、[esp+1C]、[esp+20]、[esp+24]、[esp+28]和[esp+2C]访问6个数字)。然后,对该数据进行几次检查和计算

对以下部分的一点解释:通常,在带有局部变量的函数中使用一个基指针(
ebp
),以使指针始终引用堆栈上的同一点,即使在推送新值时也是如此。在这里,您没有-相反,0x34在开始时从
esp
中减去。此外,以前有两个
push
es,这意味着堆栈指针现在比函数开始时的点低0x3C字节。这就是为什么IDA正在计算相对于
esp+3C
的所有堆栈偏移量

首先,这里是可视化的汇编代码(您应该能够更容易地理解程序流):

我还对其运行了反编译器,以生成代码的类似C的表示形式,并对其进行了一些注释:

因此,我的见解如下:

  • 首先,代码读取六个数字,方法是将
    参数u传递到\u phase_2
    ([esp+40],作为堆栈上参数列表的一部分保存在[esp+0]中)到
    读取六个数字
    ,并将指向保存数字的6*DWORD结构的指针(该结构从[esp+18]开始),指向该结构的指针存储在[esp+4]中作为堆栈上参数列表的一部分)
  • 然后,代码验证第一个数字(
    numbers[0]
    =[esp+18])是否为0,第二个数字(
    numbers[1]
    =[esp+1C])是否为1
  • 然后,代码循环遍历其余的数字(第三到第六个),验证每个数字
    numbers[n]
    是否等于前两个
    numbers[n-1]+numbers[n-2]
    的总和。循环在超出数字列表末尾时终止,这是通过将指向当前要检查的数字的指针与数字之后的
    伪变量([esp+30])进行比较来完成的,该伪变量正好位于数字列表之后(类似于“第七个数字”)
如果你有任何数学背景,这个逻辑会立即在你的脑海中引发一个想法:斐波那契!是的,基本上你必须输入
phase_2:
    push esi
    push ebx
    sub esp, 34
    lea eax, [esp+18]
    mov [esp+4], eax
    mov eax, [esp+40]
    mov [esp], eax
    call read_six_numbers
    cmp dword ptr [esp+18], 0
    jne phase_2_39

phase_2_32:
    cmp dword ptr [esp+1c], 1
    je phase_2_70

phase_2_39:
    call explode_bomb
    jmp phase_2_70

phase_2_46:
    mov eax, [ebx-8]
    add eax, [ebx-4]
    cmp [ebx], eax
    je phase_2_61

phase_2_56:
    call explode_bomb

phase_2_61:
    add ebx, 4
    cmp ebx, esi
    jne phase_2_46

phase_2_68:
    jmp phase_2_80

phase_2_70:
    lea ebx, [esp+20]
    lea esi, [esp+30]
    jmp phase_2_46

phase_2_80:
    add esp, 34
    pop ebx
    pop esi
    retn

read_six_numbers:
    int3

explode_bomb:
    int3
void __cdecl phase_2(void *argument_to_phase_2)
{
    int *currentNumber; // ebx@6
    SIX_NUMBERS_STRUCT numbers_struct; // [sp+18h] [bp-24h]@1
    int dummy_var_after_numbers; // [sp+30h] [bp-Ch]@6

    read_six_numbers(argument_to_phase_2, &numbers_struct);
    if ( numbers_struct.numbers[0] || numbers_struct.numbers[1] != 1 )
        explode_bomb();
    currentNumber = &numbers_struct.numbers[2];
    do
    {
        if ( *currentNumber != *(currentNumber - 1) + *(currentNumber - 2) )
            explode_bomb();
        ++currentNumber;
    }
    while ( currentNumber != &dummy_var_after_numbers );
}
|-------------------------------------------------------------------------------------|
| rel. to current esp | rel. to esp at start | usage                                  |
|-------------------------------------------------------------------------------------|
|                  +0 |                  -3C | First parameter to read_six_numbers    |
|                  +4 |                  -38 | Second parameter to read_six_numbers   |
|                  +8 |                  -34 | (unused)                               |
|                  +C |                  -30 | (unused)                               |
|                 +10 |                  -2C | (unused)                               |
|                 +14 |                  -28 | (unused)                               |
|                 +18 |                  -24 | First number                           |
|                 +1C |                  -20 | Second number                          |
|                 +20 |                  -1C | Third number                           |
|                 +24 |                  -18 | Fourth number                          |
|                 +28 |                  -14 | Fifth number                           |
|                 +2C |                  -10 | Sixth number                           |
|                 +30 |                   -C | Dummy, compared against to end loop    |
|                 +34 |                   -8 | Original esi register from "push esi"  |
|                 +38 |                   -4 | Original ebx register from "push ebx"  |
|                 +3C |                    0 | Return address from function "phase_2" |
|                 +40 |                   +4 | Argument to function "phase_2"         |
|-------------------------------------------------------------------------------------|