我的目标是使用gdb来找到我应该为密码选择什么密码才能使personalData功能启动nt你好主人。
您没有说明您使用的平台。我们假设Linux/x86_64。该平台非常重要,因为调用约定取决于平台。这里的答案可以很容易地针对不同的平台进行调整。
让我们考虑优化二进制(通常比较困难)的情况。的personalData
显示拆卸:
0x000000000040074d <+45>: callq 0x400560 <[email protected]>
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>
这就告诉我们,从stdin
读取密码后,我们称之为correctPassword
并根据correctPassword
是否返回0或非零变更控制。接下来的两条指令:
0x0000000000400760 <+64>: mov $0x400851,%edi
0x0000000000400765 <+69>: callq 0x400530 <[email protected]>
正在打印一些输出。如果correctPassword
返回0
并且未采取跳转,正在打印什么?
(gdb) x/s 0x400851
0x400851: "Hello master"
因此,我们的目标是使correctPassword
返回0。让我们看看它的拆解:
(gdb) disas correctPassword
....
0x0000000000400673 <+99>: callq 0x4004c0 <[email protected]>
0x0000000000400678 <+104>: add $0x28,%rsp
0x000000000040067c <+108>: retq
这告诉我们,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
在上面,我进入了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.
我碰巧有glibc安装调试符号,居然可以检查glibc源码和源代码级的参数,但是您可能没有这种奢侈,所以我会用Linux/x86_64
calling convention代替。从它可以看到,strncmp
的3个参数在RDI
,RSI
和RDX
寄存器中传递。他们的价值是什么?
(gdb) p/x $rdi
$1 = 0x7fffffffdd50
(gdb) p/x $rsi
$2 = 0x7fffffffdd20
(gdb) p/x $rdx
$3 = 0x5
好的,所以只有密码的前5个字符被比较,并且之后的任何字符都被忽略。
比较哪些字符串?
(gdb) x/s $rdi
0x7fffffffdd50: "CBEDG"
(gdb) x/s $rsi
0x7fffffffdd20: "\022\023\024\025\026"
嗯,这两个字符串都不像我们的“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"
现在,我们可以立即看到$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)
因为我们知道,“目标”字符串是\022\023...
,这是一个公平的猜测,通过0x400639
从0x4006322
指令只是初始化目标字符串(注意: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>
这与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 '@'
这看起来很有前途! (当然可以通过在适当的指令中设置断点来确认混淆过程前后的各种缓冲区的内容)。
作为我们猜测的最终确认,最后一个字符是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
最后,构建了正确的密码,我们需要采取的“目标”字符串,并做的XOR
S也是一样的顺序就可以了:
(gdb) p/c 022^0x22
$10 = 48 '0'
(gdb) p/c 023^0x23
$11 = 48 '0'
... etc.
因此,正确的密码为00000
。让我们看看是否有效:
(gdb) disable
(gdb) run
Starting program: /tmp/a.out
Please enter your password
00000
Hello master
[Inferior 1 (process 45643) exited normally]
QED。
您的代码*不完整* - 散列来自哪里? –
@EmployedRussian对不起,我忘了复制那段代码。现在完成了。 – ireallytried