使用angr的密码暴力破解

逆向工程 数据库 愤怒
2021-07-01 21:21:30

我有一个二进制文件,它从用户那里获取输入并使用check_value函数检查我们字符的每一位,该函数执行一些操作。感兴趣的函数的反汇编是:

.text:000000000005620B loc_5620B:                              ; CODE     XREF: main+A1j
.text:000000000005620B                 movzx   edi, byte ptr [rbx+rdx]
.text:000000000005620F                 mov     rsi, [r8+rdx*8]
.text:0000000000056213                 call    check_value
.text:0000000000056218                 inc     rdx
.text:000000000005621B                 and     ecx, eax
.text:000000000005621D                 cmp     rdx, 3Bh   <----**This condition**
.text:0000000000056221                 jnz     short loc_5620B
.text:0000000000056223                 test    cl, cl
.text:0000000000056225                 lea     rdi, aYouDidnTGetItM ; "You didn't get it, much sadness :("
.text:000000000005622C                 jz      short loc_5623E
.text:000000000005622E                 lea     rdi, aYouGotItCorrec ; "You got it! correct! awesome!"
.text:0000000000056235                 call    _puts
.text:000000000005623A                 xor     eax, eax
.text:000000000005623C                 jmp     short loc_56248

我所理解的是cmp指令是执行被转移的地方,我想做一个直接的蛮力。我如何使用gdb脚本或angr框架?

2个回答

根据上下文,我认为这是 CSAW ctf 决赛的兔子洞问题。正确的解决方案不是蛮力,尽管你会很想,因为蛮力是微不足道的。check_value为输入中的每个字符调用函数,该字符应该是 0x3b 个字符长。

for(i=0; i<59; ++i){
    flag &= check_value(input[i], roots[i])
}

其中,roots 是一个数组,用于在 中遍历的二叉树的根check_value我在现场解决了它,如果你想看看我是如何做到的,请在此处查看

除非您对输入有所了解,否则会大大限制需要检查的内容的空间,否则蛮力通常不是可行的方法。我认为您最好在这里了解机器代码中发生的事情,特别是考虑到反汇编看起来多么简单。

让我们分解一下。

movzx   edi, byte ptr [rbx+rdx]
mov     rsi, [r8+rdx*8]
call    check_value

在这里我们可以看到程序正在将两个值移动到rsiedi寄存器中。它们都被索引,rdx所以 rdx 可能是一个索引计数器。edi加载了一个字节,因此它可能是正在检查的密码/值的一部分。因此,rbx是指向密码/值本身的指针。

inc     rdx
and     ecx, eax
cmp     rdx, 3Bh
jnz     (5 instructions above)

递增索引计数器,查看eaxcheck_value的返回值 ( ),ecx如果返回值为零则清除寄存器最后,它将计数器与 0x3b(十进制 59)进行比较,并重复上面不等于它的所有内容。由此,我们可以得出我们的秘密密码/值是 59 个字节,如果值正确,则 check_value 可能必须为每个字节返回 1 (true)。

test    cl, cl
lea     rdi, aYouDidnTGetItM
jz      short loc_5623E
lea     rdi, aYouGotItCorrec
call    _puts

在这里我们可以看到,根据 的值,我们正在获取两条消息之一cl如果ecx从上面的循环cl中为零则将为零并且jz跳转将给我们留下消息“你没有得到它,非常悲伤:(”但如果ecx是非零,我们得到“你明白了!正确的!真棒!”然后显示在具有该puts功能的控制台上

所以,通过稍微分解一下,我们可以确切地看到这个函数在做什么。

从上面的列表中缺少反汇编推断一下(ecx列表开始之前设置的内容以及列表结束后打印出没有收到消息的内容),用于此的 C 代码可能看起来像

bool passwordOkay = True;
for (index = 0; index != 59; ++index) {
    byte = password[index]
    secondVal = table[index]
    passwordOkay &= check_value(byte, secondVal);
}
if (passwordOkay)
    printf("You got it! correct! awesome!");
else
    printf("You didn't get it, much sadness :(");

您应该在check_value函数中应用相同的技术来完全反转以击败此检查器。