假设我有以下功能:
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 但没有结果。
我不应该更改源代码。
我的目标是使用 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)
所以我们的目标是让correctPassword
return 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
,RSI
和RDX
寄存器。他们的价值观是什么?
(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
被改造成C
和b
成@
。从这里我们可以采用以下两种方法之一:我们可以尝试更多字符并猜测输入 -> 混淆密码算法是什么,或者我们可以更多地查看反汇编并简单地“读取”它。
拆机显示:
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)
最后,要构造正确的密码,我们需要取“目标”字符串并对其执行相同的XOR
s序列:
(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。
归档时间: |
|
查看次数: |
4357 次 |
最近记录: |