Wapiflapi 逆向工程练习

逆向工程 反编译 料斗
2021-06-11 10:58:57

现在我正在尝试从这里解决 r5

我已经尝试理解 Hooper 的伪代码

function check_password {
    var_28 = arg0;
    if (strlen(var_28) != strlen("this_is_not_even_interesting_its_garbage")) {
            rax = 0xffffffff;
    }
    else {
            strcpy("this_is_not_even_interesting_its_garbage", var_28);
            var_18 = 0x1;
            while (var_18 != 0x0) {
                    var_18 = 0x0;
                    for (var_14 = 0x0; var_14 <= 0x27; var_14 = var_14 + 0x1) {
                            if ((*(int8_t *)(sign_extend_32(var_14) + 0x6010a0) & 0xff) != 0x0) {
                                    rax = random();
                                    rcx = *(int8_t *)(sign_extend_64(var_14) + 0x6010a0) & 0xff & 0xff;
                                    temp_3 = rax % rcx;
                                    *(int8_t *)(sign_extend_32(var_14) + 0x6010a0) = (*(int8_t *)(sign_extend_32(var_14) + 0x6010a0) & 0xff) - temp_3 + 0x1;
                                    var_18 = var_18 | *(int8_t *)(sign_extend_32(var_14) + 0x6010a0) & 0xff & 0xff;
                                    *(int8_t *)(sign_extend_32(var_14) + "this_is_not_even_interesting_its_garbage") = (*(int8_t *)(sign_extend_32(var_14) + "this_is_not_even_interesting_its_garbage") & 0xff) - temp_3 + 0x1;
                            }
                    }
            }
            rax = *master;
            rax = strcmp(rax, "this_is_not_even_interesting_its_garbage");
    }
    return rax;
}

但现在对我来说太复杂了。所以我知道密码长度应该是 40 但代码的其他部分是如何工作的?

不清楚的部分是unt8_t演员表。我已经使用 gdb 单步执行了代码,我看到了某些指令的结果,但不明白为什么会出现这些结果?

1个回答

尽管 OP 仅澄清了内部for对他来说是不可读的,但我会写一个答案来彻底解释前几行,然后突然停在内部 for 循环的最关键部分。虽然相信示例是一种很好的学习方式,但我这样做是为了使这尽可能具有教育意义,而不会干扰独立解决练习的学习过程。我也不熟悉特定的练习集,并希望避免在创作者不希望的情况下提供完整的答案。

我希望我能达到足够的教育但不要太多的最佳点。

祝练习顺利,这里是:

var_18 = 0x1;

将内部状态设置为 1。

while (var_18 != 0x0) {

当 state 为 时True,继续执行循环的内容。状态实际上是一个布尔值,表示处理尚未完成。

var_18 = 0x0;

在每次外循环迭代时将内部状态设置为零。这意味着每次迭代都必须设置“尚未完成”标志。

for (var_14 = 0x0; var_14 <= 0x27; var_14 = var_14 + 0x1) {

内部 for 迭代 0x28 次,十进制为 40。由于我们知道这是整个字符串的长度,我们可以假设内部循环以某种方式迭代字符串的所有字符。这可能是巧合,但我们应该密切注意。

if ((*(int8_t *)(sign_extend_32(var_14) + 0x6010a0) & 0xff) != 0x0) {

现在这里变得有点复杂,所以我们将它分成几个部分:

sign_extend_32(var_14)

由于我们知道 var_14 是一个介于 0 和 39(包括)之间的非负整数,因此对其进行符号扩展是没有意义的。符号扩展基本上是将较短的长度值转换为较长的操作,同时考虑到符号位。请参阅有关的符号扩展更多信息。这是编译器或反编译器工件。更高级的反编译器可能已经删除了它。

(int8_t *)(sign_extend_32(var_14) + 0x6010a0)

将我们的添加var_14到相对较长的十六进制偏移量。从它的外观来看,它可能是 40 字节表的偏移量。这是从经验中学到的,查看该地址的值可能会清除它。添加var_14然后转换为 int8 指针进一步表明就是这种情况,var_14用作该表中的索引。

(*(int8_t *)(sign_extend_32(var_14) + 0x6010a0) & 0xff)

如果我们还不相信,那么现在很清楚,因为我们从结果偏移量中读取了一个字节。AND-ing 与 0xff 是另一个反编译器工件,由编译器如何读取 64 位英特尔处理器中的字节引起。

if ((*(int8_t *)(sign_extend_32(var_14) + 0x6010a0) & 0xff) != 0x0) {

回到整行,这是从表中读取字节,并跳过内部循环的当前迭代。

rax = random();

简单的。将一个随机值设置为rax.

rcx = *(int8_t *)(sign_extend_64(var_14) + 0x6010a0) & 0xff & 0xff;

这实际上与之前的条件完全相同,只是现在表中该索引的值存储到rcx.

剩余的代码行

                                temp_3 = rax % rcx;
                                *(int8_t *)(sign_extend_32(var_14) + 0x6010a0) = (*(int8_t *)(sign_extend_32(var_14) + 0x6010a0) & 0xff) - temp_3 + 0x1;
                                var_18 = var_18 | *(int8_t *)(sign_extend_32(var_14) + 0x6010a0) & 0xff & 0xff;
                                *(int8_t *)(sign_extend_32(var_14) + "this_is_not_even_interesting_its_garbage") = (*(int8_t *)(sign_extend_32(var_14) + "this_is_not_even_interesting_its_garbage") & 0xff) - temp_3 + 0x1;
                        }
                }
        }
        rax = *master;
        rax = strcmp(rax, "this_is_not_even_interesting_its_garbage");
}
return rax;