阅读本文需要的一些前置知识:
汇编、C 语言、IDA pro 静态分析、GDB 调试
只有一个文件,没有过多的提示,根据这个文件给出 flag
*不知道文件何意的请自行 Google
没有看到后缀,又是逆向题目,初步分析是Linux 文件的可能性比较大,拖到Linux确认一下
可以看到文件确实是elf 文件
果然密码不是那么好猜到的
动态也不是那么好跑的,GDB 直接卡死
CTRL+C 中断之后的调试信息如下
^C
Program received signal SIGINT, Interrupt.
[----------------------------------registers-----------------------------------]
RAX: 0xffffffffffffffff
RBX: 0x1
RCX: 0x7ffff7b0317e
(<ptrace+78>: cmprax,0xfffffffffffff000)
RDX: 0xffffffffffffff70
RSI: 0x0
RDI: 0x0
RBP: 0x7fffffffdf90 --> 0x2
RSP: 0x7fffffffdf90 --> 0x2
RIP: 0x4007e4 (jmp0x4007e4)
R8 : 0xffffffff
R9 : 0x0
R10: 0x0
R11: 0x282
R12: 0x600e08 --> 0x4006d0 (cmpQWORD PTR
[rip+0x200748],0x0# 0x600e20)
R13: 0x7fffffffe0c8 --> 0x7fffffffe3cb
("XDG_VTNR=1")
R14: 0x7fffffffe0b8 --> 0x7fffffffe3b1
("/root/debug/infinite-loop")
R15: 0x1
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap
INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x4007da: call 0x400600
<ptrace@plt>
0x4007df: test rax,rax
0x4007e2: jns0x4007e6
=> 0x4007e4: jmp0x4007e4
| 0x4007e6: poprbp
| 0x4007e7: ret
| 0x4007e8: push rbp
|
0x4007e9: movrbp,rsp
|->=>
0x4007e4: jmp0x4007e4
0x4007e6: poprbp
0x4007e7: ret
0x4007e8: push rbp
JUMP is taken
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdf90 --> 0x2
0008| 0x7fffffffdf98 --> 0x4008dd (addrbx,0x1)
0016| 0x7fffffffdfa0 --> 0x7fffffffdfd0 -->
0x0
0024| 0x7fffffffdfa8 --> 0x0
0032| 0x7fffffffdfb0 --> 0x400890
(push r15)
0040| 0x7fffffffdfb8 --> 0x400610 (xorebp,ebp)
0048| 0x7fffffffdfc0 --> 0x7fffffffe0b0 -->
0x1
0056| 0x7fffffffdfc8 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGINT
0x00000000004007e4 in ?? ()
Missing separate debuginfos, use:
debuginfo-install glibc-2.17-292.el7.x86_64
根据异常信息,可以看出问题应该处在0x4007e4
因为运行卡住 ,文件存在反调试代码的可能性比较大
本片文章重点不是对抗反调试,直接给出去掉反调试的版本(下期可以讲一下如何破解反调试功能)
反调试功能去除之后可以直接调试
拖入IDA pro 寻找关键点,后面需要在GDB 中下断点(加快分析工作)
只有一个判断,根据输入的内容(也就是密码),进行不同的输出
其实在破解软件的时候我们为了让软件直接输出正确的内容,直接Nop 掉关键点就ok了,但是这里直接使用Nop 不行,为什么?因为我们要知道程序的输入数据是什么,而不是获取程序的输出(功能)
关键点就清楚了 call sub_4006FD
进入关键点看一下具体逻辑
关键点的内容在下面两张图中,真的是太后老佛爷的裹脚布
直接分析这个函数,估计呀吐血三升了
直接F5 大法走起
一切都变得明了了,关键点在for 循环中,由i<=11 可以知道,密码的长度应该在12 位(0~11)
回到默认视图,在cmp(比较指令) 前找一个关键点,此处是对eax 复制,后面会用来操作、比较
关键点通过IDA pro 已经得到了,在 0x400784 下断点(what the f*ck ,竟然没有断下来,什么鬼?别急,要输入密码后才能断下来)
果然,输入密码之后,断点起作用,历经九九八十一难,终于可以动态分析了(程序停在了 `0x400784` 位置,根据前面的静态分析,密码长度为12位,我们这里直接输入12 位的密码以减少分析难度)
看到下面这块code 区域,通过edx、eax 相减之后与0x1 比较,根据比较结果决定是否要进行跳转
此时看一下寄存器的值(目前阶段,可以认为RAX、RDX与eax、edx 等同 )
如果需要edx与eax 相减之后的结果等于1 ,则需要修改RAX 的值(寄存器中现有的‘a’,是我们之前输入的12 位密码中的个a)
修改寄存器的值(因为edx 是0x44 ,我们直接将eax 修改成0x43 就好了,注意 数据值都是16 进制的)
修改之后,第二次循环的寄存器状态如下,edx 变成了‘p’,同理,再次修改eax 的值即可(set $eax=0x69 ???NoNoNo,大错特错,这里是16 进制,不是10 进制)
循环执行上述步骤,得出eax 中的值为 Code_Talkers 也就是我们的密码
输入eax 中的值试一下,成功破解
PS:
本篇没有对如何绕过反调试部分进行讲解,各位有兴趣可以留言,下期可以继续分享;另外此题还有另外一种更简单的破解方法,破解只需3 s
关于文章内容可以留言讨论