scanf 如何与我的汇编代码交互

逆向工程 拆卸 C 异或 程序分析 扫描
2021-06-25 03:54:01

我最近开始为了逆向工程而进行组装。我从了解基本数据类型开始,但我想继续了解更复杂的数据类型和函数。我试图了解这两种方法requestMaxPowcomputePowers

这是我使用来源

#include <stdio.h>

int requestMaxPow();
int computerPowers(int);
int main(){
    int max = requestMaxPow();
    computePowers(max);
    return 0;
}

int requestMaxPow(){
    int maxPow;

    scanf ("%d", &maxPow);
    return maxPow;
}

int computePowers(int MaxPow){
    int currentVal = 0;
    int currentPow = 0;

    for(;currentPow < MaxPow; ++currentPow){
        currentVal = currentPow * currentPow;
    }
}
Compiled with GCC with the following arguments "gcc -g -O0 morecomplex.c -o morecoplex"

下面的程序集是针对requestMaxPow方法的,这是我最难理解的。具体来说,我不明白 0xc5 处的“gs”是什么意思,我也不知道 0xce - 0x50 之间发生了什么。精通的人可以逐行解释发生了什么吗?

(gdb) disassemble 
Dump of assembler code for function requestMaxPow:
   0x080484bf <+0>: push   ebp
   0x080484c0 <+1>: mov    ebp,esp
   0x080484c2 <+3>: sub    esp,0x18
=> 0x080484c5 <+6>: mov    eax,gs:0x14
   0x080484cb <+12>:    mov    DWORD PTR [ebp-0xc],eax
   0x080484ce <+15>:    xor    eax,eax
   0x080484d0 <+17>:    sub    esp,0x8
   0x080484d3 <+20>:    lea    eax,[ebp-0x10]
   0x080484d6 <+23>:    push   eax
   0x080484d7 <+24>:    push   0x80485b0
   0x080484dc <+29>:    call   0x8048380 <__isoc99_scanf@plt>
   0x080484e1 <+34>:    add    esp,0x10
   0x080484e4 <+37>:    mov    eax,DWORD PTR [ebp-0x10]
   0x080484e7 <+40>:    mov    edx,DWORD PTR [ebp-0xc]
   0x080484ea <+43>:    xor    edx,DWORD PTR gs:0x14
   0x080484f1 <+50>:    je     0x80484f8 <requestMaxPow+57>
   0x080484f3 <+52>:    call   0x8048350 <__stack_chk_fail@plt>
   0x080484f8 <+57>:    leave  
   0x080484f9 <+58>:    ret    
End of assembler dump.

computePowers方法的程序集更容易理解。我把它包括在内,以防它与我的主要问题有关。

(gdb) disassemble 
Dump of assembler code for function computePowers:
   0x080484fa <+0>: push   ebp
   0x080484fb <+1>: mov    ebp,esp
   0x080484fd <+3>: sub    esp,0x10
=> 0x08048500 <+6>: mov    DWORD PTR [ebp-0x4],0x0
   0x08048507 <+13>:    mov    DWORD PTR [ebp-0x8],0x0
   0x0804850e <+20>:    jmp    0x804851e <computePowers+36>
   0x08048510 <+22>:    mov    eax,DWORD PTR [ebp-0x8]
   0x08048513 <+25>:    imul   eax,DWORD PTR [ebp-0x8]
   0x08048517 <+29>:    mov    DWORD PTR [ebp-0x4],eax
   0x0804851a <+32>:    add    DWORD PTR [ebp-0x8],0x1
   0x0804851e <+36>:    mov    eax,DWORD PTR [ebp-0x8]
   0x08048521 <+39>:    cmp    eax,DWORD PTR [ebp+0x8]
   0x08048524 <+42>:    jl     0x8048510 <computePowers+22>
   0x08048526 <+44>:    leave  
   0x08048527 <+45>:    ret    
End of assembler dump.

编辑 1 在查看代码一段时间后,我意识到 xor 发生在 eax 上以将其“0”输出,是否发生这种情况以便可以将返回值存储到 eax 中?

1个回答

之间的代码80484c580484ce建立的栈金丝雀,并80484e780484f3检查它。gcc 省略了第二个函数的堆栈检查,因为它可以确定(不使用指针,不调用子例程)这里无法覆盖堆栈。xor eax, eax本身并不是必需的(在将某些内容存储到寄存器之前您不需要将寄存器归零),只是编译器希望尽快使 Canary 值未知。

省略堆栈检查会导致:

0x080484d0 <+17>:    sub    esp,0x8            // adjust stack alignment 
0x080484d3 <+20>:    lea    eax,[ebp-0x10]     // move the address of maxRow into eax
0x080484d6 <+23>:    push   eax                // and push it on the stack as the 2nd function argument
0x080484d7 <+24>:    push   0x80485b0          // Push the address of your format string as first function argument
0x080484dc <+29>:    call   0x8048380 <__isoc99_scanf@plt>  // call scanf
0x080484e1 <+34>:    add    esp,0x10           // remove the added bytes from the stack
0x080484e4 <+37>:    mov    eax,DWORD PTR [ebp-0x10]  // get the content of maxRow into eax to return it as function return value

3 件事可能需要进一步解释:

  • lea指令计算一个地址,同时mov加载一个值。因此,lea eax,[ebp-0x10]就像 eax=&maxRow,并且mov eax, DWORD PTR [ebp-0x10]就像eax=maxRow
  • 在 C 中,函数参数从后面推送,即最后一个参数首先推送。这确保了第一个参数总是在相同的位置,为此,重要的是varargs功能,如printfscanf
  • 由于传递了 2 个参数,需要 8 个字节,因此省略第一个参数sub esp,0x8并替换add esp,0x10withadd esp, 0x8会更直接。gcc花费这些额外字节的原因可能是它希望堆栈指针对齐到 16 字节的倍数,这会加速某些事情。不知道这但是,由于之间的总距离esp在你的函数的开始和esp在一开始scanf似乎并没有为16的倍数。