使用未定义的变量进行计算?

逆向工程 拆卸 部件 二元分析
2021-06-17 15:30:09

我是逆向工程的新手,并尝试使用radare2解决crackme。

我的问题是理解以下几行:

|           0x000007a9      c745b8000000.  mov dword [var_48h], 0
|           0x000007b0      c745bc000000.  mov dword [var_44h], 0
|           0x000007cd      8b45b8         mov eax, dword [var_48h]
|           0x000007d0      4898           cdqe
|           0x000007d2      0fb64405c0     movzx eax, byte [rbp + rax - 0x40]

从围绕它们的这段代码的底部开始:

|           0x0000076a      55             push rbp
|           0x0000076b      4889e5         mov rbp, rsp
|           0x0000076e      4883ec50       sub rsp, 0x50               
|           0x00000772      64488b042528.  mov rax, qword fs:[0x28]    ; [0x28:8]=0x19c0 ; '('
|           0x0000077b      488945f8       mov qword [canary], rax
|           0x0000077f      31c0           xor eax, eax
|           0x00000781      c645b700       mov byte [var_49h], 0
|           0x00000785      488d3d580100.  lea rdi, qword str.Enter_password: ; 0x8e4 ; "Enter password:" ; const char *s
|           0x0000078c      e87ffeffff     call sym.imp.puts           ; int puts(const char *s)
|           0x00000791      488d45c0       lea rax, qword [var_40h]
|           0x00000795      4889c6         mov rsi, rax
|           0x00000798      488d3d550100.  lea rdi, qword [0x000008f4] ; "%s" ; const char *format
|           0x0000079f      b800000000     mov eax, 0
|           0x000007a4      e897feffff     call sym.imp.__isoc99_scanf ; String! ; int scanf(const char *format)
|           0x000007a9      c745b8000000.  mov dword [var_48h], 0
|           0x000007b0      c745bc000000.  mov dword [var_44h], 0
|           0x000007cd      8b45b8         mov eax, dword [var_48h]
|           0x000007d0      4898           cdqe
|           0x000007d2      0fb64405c0     movzx eax, byte [rbp + rax - 0x40]
|           0x000007d7      84c0           test al, al

我了解输出并且有想要的输入 ( scanf)。之后var_48hvar_44heax设置为0。接下来的几行我根本不明白。除了cdqe指令之外,还有一些计算(?)与变量rbprax(= var_49h?)。

也许你能帮我理解一下?

先感谢您!

2个回答

来看看你看不懂的具体代码段:

mov dword [var_48h], 0
mov dword [var_44h], 0
mov eax, dword [var_48h]
cdqe
movzx eax, byte [rbp + rax - 0x40]

一次检查几行。

前几行要么通过一次分配一个 dword 将一个大小为 qword(8 字节)的局部变量初始化为 0,要么将两个 dword 大小的变量初始化为 0,每个变量都为零:

mov dword [var_48h], 0
mov dword [var_44h], 0

将其转换为 C,这可能类似于:

long var_ab = 0;

或者

int var_a, var_b = 0;

假设上面的赋值是对单个 qword 变量的,第三行有点令人费解:

mov eax, dword [var_48h]

无论如何,这不会转换为实际的 C 语句,而是建议变量(作为var_abvar_b将在以下语句中使用。

看看下面的两行,就更清楚了:

cdqe
movzx eax, byte [rbp + rax - 0x40]

两者中的第一个,cdqe简单地符号扩展eaxrax,也有效地将 的上半部分rdx设置为零。在我们考虑了 x86 汇编语言的局限性之后,最后一个片段的第二行使这一点变得很清楚。

由于eax用作堆栈变量的索引,因此需要对其进行符号扩展,rax因为程序集限制。在 x86 64 位汇编中执行是不可能的,因为偏移计算中的所有寄存器都需要具有相同的长度。byte [rbp +eax - 0x40]

这清楚地表明变量确实是两个双字变量,并且eax符号扩展仅用于以下mov指令。

那么,上面的汇编代码可能是以下 C 代码的结果:

char buf[...];
dword idx = 0;
if (buf[idx] == 0) {
    ...
}

CDQE 将双字转换为四字

它采用 eax 符号中的任何内容扩展它并将其放入 Rax

所以只要 eax 持有任何小于 7ffffffff 且大于 0
Rax 的值,它就会是 00000000无论什么 washereineaxwhateverwashereineax
when eax holds anything from 0x80000000 to 0xffffffff
rax will become ffffffff

在您的情况下,eax 为 0(请参阅 var_48),因此执行 cdqe 后 Rax 将为 0

然后它访问一个字节(密码的第一个字母)并检查它是否为 0(test al,al)