gdb - 找到密码

ire*_*ied 2 gdb

假设我有以下功能:

void personalData()
{
    char password[30];
    puts("Please enter your password");
    fgets(password, 6, stdin);

    if(correctPassword(password) != 0) {
        puts("Try again later !");
    }
    else {
        puts("Hello master");
    }
}

int correctPassword(char password[5])
{
    int i = 0;
    char desiredPassword[5] = {0x12, 0x13, 0x14, 0x15, 0x16};
    char hash[5] = {0x22, 0x23, 0x24, 0x25, 0x26};

    for(i = 0; i < 5; ++i) {
        password[i] ^= hash[i];
    }
    return strncmp(password, desiredPassword, 5);
}

int main() {
    personalData();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我的目标是使用gdb查找我应该选择什么密码password,以便函数 personalData 打印Hello master。我尝试使用断点,每个部分都有许多断点,但我不知道在哪里寻找实际结果。我也尝试查看 strncmp 但没有结果。 我不应该更改源代码。

Emp*_*ian 5

我的目标是使用 gdb 来查找我应该为密码选择什么密码,以便函数 personalData 打印 Hello master。

您没有说明您正在使用的平台。让我们假设 Linux/x86_64。平台很重要,因为调用约定取决于平台。可以针对不同的平台轻松调整此处的答案。

让我们考虑优化二进制的(通常更难)的情况。personalData节目拆解:

   0x000000000040074d <+45>:    callq  0x400560 <fgets@plt>
   0x0000000000400752 <+50>:    xor    %eax,%eax
   0x0000000000400754 <+52>:    mov    %rsp,%rdi
   0x0000000000400757 <+55>:    callq  0x400680 <correctPassword>
   0x000000000040075c <+60>:    test   %eax,%eax
   0x000000000040075e <+62>:    jne    0x400780 <personalData+96>
Run Code Online (Sandbox Code Playgroud)

这告诉我们,从 读取密码后stdin,我们会correctPassword根据correctPassword返回 0 还是非零来调用和更改控制。接下来的两条指令:

   0x0000000000400760 <+64>:    mov    $0x400851,%edi
   0x0000000000400765 <+69>:    callq  0x400530 <puts@plt>
Run Code Online (Sandbox Code Playgroud)

正在打印一些输出。如果correctPassword返回0并且没有进行跳转,将打印什么?

(gdb) x/s 0x400851
0x400851:   "Hello master"
Run Code Online (Sandbox Code Playgroud)

所以我们的目标是让correctPasswordreturn 0,我们看一下它的反汇编:

(gdb) disas correctPassword
....
  0x0000000000400673 <+99>: callq  0x4004c0 <strncmp@plt>
  0x0000000000400678 <+104>:    add    $0x28,%rsp
  0x000000000040067c <+108>:    retq
Run Code Online (Sandbox Code Playgroud)

这告诉我们correctPassword返回任何strncmp返回的值,即返回我们想要的 0 IFF我们的密码匹配N它正在strncmp使用的任何字符的第一个字符。设置断点的时间strncmp

(gdb) break strncmp
Breakpoint 1 at 0x4004c0
(gdb) run
Starting program: /tmp/a.out
Please enter your password
aaaaaaaaa
Run Code Online (Sandbox Code Playgroud)

上面我输入了 9 个字符的密码,作为初步猜测。

Breakpoint 1, __strncmp_ssse3 () at ../sysdeps/x86_64/multiarch/../strcmp.S:174
174 ../sysdeps/x86_64/multiarch/../strcmp.S: No such file or directory.
Run Code Online (Sandbox Code Playgroud)

我碰巧安装了 GLIBC 调试符号,并且可以实际检查 GLIBC 源代码和源代码级参数,但您可能没有那么奢侈,所以我将改用Linux/x86_64 调用约定。从中可以看出,to 的 3 个参数strncmp传入RDI,RSIRDX寄存器。他们的价值观是什么?

(gdb) p/x $rdi
$1 = 0x7fffffffdd50
(gdb) p/x $rsi
$2 = 0x7fffffffdd20
(gdb) p/x $rdx
$3 = 0x5
Run Code Online (Sandbox Code Playgroud)

好的,所以只比较密码的前 5 个字符,之后的任何字符都将被忽略。

比较的字符串是什么?

(gdb) x/s $rdi
0x7fffffffdd50: "CBEDG"
(gdb) x/s $rsi
0x7fffffffdd20: "\022\023\024\025\026"
Run Code Online (Sandbox Code Playgroud)

嗯,这两个字符串都不像我们的“aaa...”密码。让我们尝试不同的密码:

(gdb) run
Starting program: /tmp/a.out
Please enter your password
bbbbb

Breakpoint 1, __strncmp_ssse3 () at ../sysdeps/x86_64/multiarch/../strcmp.S:174
174 ../sysdeps/x86_64/multiarch/../strcmp.S: No such file or directory.
(gdb) x/s $rdi
0x7fffffffdd50: "@AFGD"
(gdb) x/s $rsi
0x7fffffffdd20: "\022\023\024\025\026"
Run Code Online (Sandbox Code Playgroud)

我们现在可以立即看到$rsi序列没有改变,可以假设这"\022\023\024\025\026"是预期的密码。

我们也看到,第一个a被改造成Cb@。从这里我们可以采用以下两种方法之一:我们可以尝试更多字符并猜测输入 -> 混淆密码算法是什么,或者我们可以更多地查看反汇编并简单地“读取”它。

拆机显示:

   0x0000000000400622 <+18>:    movb   $0x12,(%rsp)
...
   0x000000000040062a <+26>:    movb   $0x13,0x1(%rsp)
   0x000000000040062f <+31>:    movb   $0x14,0x2(%rsp)
   0x0000000000400634 <+36>:    movb   $0x15,0x3(%rsp)
   0x0000000000400639 <+41>:    movb   $0x16,0x4(%rsp)
   0x000000000040063e <+46>:    movb   $0x22,0x10(%rsp)
   0x0000000000400643 <+51>:    movb   $0x23,0x11(%rsp)
   0x0000000000400648 <+56>:    movb   $0x24,0x12(%rsp)
   0x000000000040064d <+61>:    movb   $0x25,0x13(%rsp)
   0x0000000000400652 <+66>:    movb   $0x26,0x14(%rsp)
Run Code Online (Sandbox Code Playgroud)

由于我们知道“目标”字符串是\022\023...,因此可以合理地猜测,从0x4006322到的指令0x400639只是初始化目标字符串(注意0x12== \022)。也许从开始的指令0x40063e与混淆有关?进一步查看反汇编,我们看到:

   0x0000000000400626 <+22>:    cmp    $0x5,%rax
...
   0x0000000000400657 <+71>:    je     0x40066b <correctPassword+91>
   0x0000000000400659 <+73>:    movzbl 0x10(%rsp,%rax,1),%edx
   0x000000000040065e <+78>:    xor    %dl,(%rdi,%rax,1)
   0x0000000000400661 <+81>:    add    $0x1,%rax
   0x0000000000400665 <+85>:    cmp    $0x5,%rax
   0x0000000000400669 <+89>:    jne    0x400659 <correctPassword+73>
Run Code Online (Sandbox Code Playgroud)

这是一个固定行程计数为 5 的循环,在循环中,我们从一个缓冲区加载单个字符,并XOR从另一个缓冲区加载该值。密码的第一个字符被XOR编辑的可能性有多大0x22

(gdb) p/c 'a' ^ 0x22
$5 = 67 'C'
(gdb) p/o 0x12
$6 = 022
(gdb) p/c 'b' ^ 0x22
$7 = 64 '@'
Run Code Online (Sandbox Code Playgroud)

这看起来很有希望!(当然,您可以通过在适当的指令上设置断点来确认混淆过程前后各种缓冲区的内容)。

由于我们的猜测最终确认,最后一个字符被XOR编带0x26

(gdb) p/c 'a' ^ 0x26
$8 = 71 'G'           # matches last char of 'aaa...' guess
(gdb) p/c 'b' ^ 0x26
$9 = 68 'D'           # matches last char of 'bbb...' guess
Run Code Online (Sandbox Code Playgroud)

最后,要构造正确的密码,我们需要取“目标”字符串并对其执行相同的XORs序列:

(gdb) p/c 022 ^ 0x22
$10 = 48 '0'
(gdb) p/c 023 ^ 0x23
$11 = 48 '0'
... etc.
Run Code Online (Sandbox Code Playgroud)

因此,正确的密码是00000。让我们看看这是否有效:

(gdb) disable
(gdb) run
Starting program: /tmp/a.out
Please enter your password
00000
Hello master
[Inferior 1 (process 45643) exited normally]
Run Code Online (Sandbox Code Playgroud)

QED。