首页
社区
课程
招聘
[翻译] the White Rabbit CrackMe 解答
发表于: 2018-2-6 15:48 3709

[翻译] the White Rabbit CrackMe 解答

2018-2-6 15:48
3709

原文链接: 234K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6E0k6h3c8A6N6h3#2Q4x3X3g2U0L8$3#2Q4x3V1k6Q4y4o6m8S2L8r3g2^5M7$3E0S2L8r3!0*7N6h3u0Q4x3V1k6K6L8$3I4$3K9h3&6Y4i4K6u0V1N6r3S2W2i4K6u0V1N6$3S2A6N6r3g2Q4x3X3c8J5j5h3u0T1K9i4c8Q4x3X3c8U0M7X3q4U0K9$3#2W2i4K6u0V1k6o6k6T1y4U0t1%4j5K6l9J5j5h3b7@1

Crackme文件可以从此处下载: White Rabbit crackme! 也可以从附件里下载(解压密码:crackme).

因为crackme里稍微使用了混淆和一些像恶意程序的把戏, 所以可能会被一些杀毒软件标记为恶意程序, 所以也建议在虚拟机下运行.

这个crackme运行的截图如下:

OK, 首先要做的第一件事就是将其载入到IDA中(我这里使用的是刚刚发布的IDA 7的免费版本). 通过搜索字符串Password#1来看它的交叉引用以及前后都发生了些什么.


就这了! 我们可以看到它被sub_4034D0所引用. 现在我们将跟随到引用处, 来看看接下来发生什么


sub_403D90中有一些初始化操作, 随后在sub_404150的结果与可疑值0x57585384的比较后又一个分支跳转. 子分支中的sub_403990输出了一些提示语以及后续一些有关接受用户输入的内容.

我们首先来看初始化部分(sub_403D90):


函数取了两个参数, 内容看上去也非常清楚: 通过给出的标识符查找资源文件, 加载资源文件, 确定它的文件大小, 然后申请内存空间并将资源文件的数据复制进去. 该函数返回那个新申请的内存空间的指针, 并将资源文件的大小存储在第一个参数中.

现在我们唯一需要注意的就是图中的sub_406A70, 它取了3个参数(target pointer, source pointer 以及 data size)并且看起来非常像是memcpy(或memmove, 是哪个不重要, 因为内存区域没有重叠). 但是函数内的代码却包含有大量的分支, 难以分析. 所以我们不能确定它有没有在复制的过程中以某种方式修改了数据(比如, 解密数据). 最简便的检查方式就是在调试器里动态分析, 比较函数返回时, souretarget内存是否有区别.

我使用[x64Dbg](7f2K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6^5y4U0c8V1j5X3N6Q4x3X3g2U0L8$3#2Q4x3V1k6Q4x3U0V1`.来分析. 在启动调试器后我们打开crackme, 调试器会自动运行程序并暂停在入口点位置.


现在我们需要在我们感兴趣的函数返回处设下一个断点. 指令地址是0x00403DF9(给定.text段的基址是0x00401000). 你可以根据内存布局来了解真正.text段载入的基址(我这里是0x00281000). 因此我的实际断点地址应该是0x00283DF9.

现在我们用bp 0x00283DF9命令设下断点, 继续执行触发断点. 然后我们右键点击右侧面板ebxedi寄存器的值, 选择在数据窗口跟随. 现在我们就可以确认sub_406A70仅仅复制了内存as is, 我们可以放心地将该函数重命名为更易理解的memcpy. 同样我们也把sub_403D90重命名为loadResource

现在我们来分析sub_404150


映入眼帘的是一个常量0x82F63B78. 通过google搜索知道说这是一个用于CRC32计算的多项式值. 代码里看也有从输入缓冲区里对每个字节的值异或累加, 随后再移位/异或8次. 因此它确实是一个crc32c计算函数.

在重命名和初期的分析后, 我们再来看看改动后的代码

注意: 也许你会对lea/cmovnb指令有些许困惑. 不过很好解释: lpPasswordText的值实际上是如下的结构体:

这可能就是栈上std::string的形式. 当字符串仅有static数组那么长时, 不会申请额外的内存空间(并且static缓冲区的地址用lea加载). 相反如果超出了缓冲区, cmovnb会获取dynamic域所分配的内存的指针. 最后, eax会获得指向真正字符串数据的指针, 不论其位置具体在哪.

因此, sub_401000读取键盘输入到std::string, std::string随后传递给crc32c函数. 现在我们知道说我们的password应该含有CRC32的0x57585384, 我们可以根据这个条件判断我们是否获取到了正确的password.

现在我们来假定password跟给出的CRC32值相匹配, 来继续往下分析:


有趣的第一点就是sub_403C90, 因为它同时取了password资源数据作为参数.


很显然这里是一个异或加密的操作. 它首先确定password的长度, 随后用相应的password字符对输入缓冲区的每一个字节进行异或.

随后生成一个临时文件名, 将解密的资源数据内容写入到该文件(在函数sub_403090里). 待一切完成, 却也再没有给出任何关于password的线索了. 我们来看一下sub_403D20, 该函数接收新创建的文件名并执行了一些操作.


OK, 现在事情已经越发清晰. crackme尝试设置新生成的文件作为桌面壁纸, 因此很显然这个文件应该是一个图片.

现在我们要提取crackme里的资源文件, 看看我们能否有所收获. 你可以使用任意的资源编辑软件, 例如: Resource Hacker


我们可以看到它的大小是6,220,854字节, 对于一个图像来说已经很大了, 据此我们猜测, 这是一个无压缩的BMP图像文件.

BMP格式已经是众所周知, 并且有文档说明. 文件起始于一个"BM"签名, 随后是4字节的文件大小(小端序), 接着是两个4字节的保留字(全0), 一个4字节存储着位图数据的起始位置, 再紧接着是40字节的位图信息头(起始的是该信息头所占用的字节数). 再下面就是各种关于BMP信息了, 我们现在也不知道.

由于我们得知了真正的文件大小值, 所以我们可以较准确地推测出文件的前18个字节.

现在我们逐个将实际资源文件里的字节和说期望的字节进行异或, 这样我们就可以恢复出部分key的内容. 如果幸运的话, 我们可以获得一个完整的key

异或得到的结果是"follow_da_rabbitzf". 最后的这一个"f"也许是重复的下一个key的起始字母, 也许就是这个key的一部分. 最简单的检查方法就是将其输入到crackme里看看结果如何.


Yeah. 我们的结果是正确的. 我们再继续.

现在我们有一个超酷的桌面壁纸, 然后还有另外一个password需要破解出来. 我们再次搜索"Password#2"字符串并跟随到交叉引用处:


这看起来跟之前非常相似, 因此我们自己向下来到解密开始的部分:


有趣的部分在sub_403E10, 这里在写入数据到文件之前进行了解密:


这里根据password导出一个AES128的密钥(使用SHA256作为密钥导出算法)并用于解密资源数据.

没有必要去破解AES加密(恐怕就连NSA也无法破解), 我们只知道password的crc32值. 很显然不足以通过暴力破解的手段来获取它(我尝试过!). 但等等, 我们有一个壁纸啊! 或许在壁纸里会有某些隐藏的信息!

用图像编辑器打开并使用"颜色选择"工具:


这应该就是我们一直在寻找的key! 接下来继续:


但是事情还没结束. 现在我们在临时目录下有一个解密过的可执行文件, 但我们还是没有拿到flag. 我们还需要用IDA继续分析.

因为第二个可执行文件按并没有产生任何字符串信息, 也就难以下手. 我们就来看看导入表情况:


这里有一系列的按顺序导入的ws2_32.dll的函数, 这给我们两个线索:

因此我们的第一步就是去到这些函数被调用的地方, 并将这些函数重命名为可读性更高更有意义的名称. 序号与之对应的函数名称可以很容易地通过google搜索找到.

现在我们知道了所有的网络操作都在sub_404480里, 因此接下来仔细看看这个函数. 该函数开始是一个标准流程(WSAStartup/socket/bind/listen), 所以没太多亮点, 有趣的部分在下图:


因此它等待接受一个连接, 从连接中读取4字节, 基于静态缓冲区buf和接收的数据在sub_404640中执行一些操作. 如果操作成功转型(函数返回非零值), 它就会将buf的内容发回给客户端随后关闭连接. 否则它会关闭连接监听新的连接. 所有的操作都是同步的, 所以在sub_404640成功执行前不会退出函数.

来看看sub_404640的内容:


看起来非常像是一个小的状态机, 成功转移到下一状态时返回1, 有如下几个转移:

因此, 我们可能需要按顺序发起3个连接, 连到"server", 更新状态机到下一状态.

但是我们仍有两个问题需要解决:

因此我们需要找到所有的函数被调用的地方, 然后跟踪看它启动了哪一个端口.

如同我们所预料的那样, 函数被调用了3次(因为有3次合法的状态转移), 并且幸运的是, 它都是在同一个步骤里被调用的:


在这里


所以, server一开始开启了端口1337, 随后是1338, 最后是1339. 因此我们首先需要连接到1337端口并发送9, 然后连接到1338端口, 发送3. 最后连接到1339端口, 发送5. 我们可以使用内置的telnet工具来完成这一操作.

完成上述操作后会打开一个简短视频的YouTube页面:


我们成功地拿到了flag. 收工回家!

 
 
 
 

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 1
支持
分享
最新回复 (2)
雪    币: 3
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
图片都看不了
2018-2-6 17:44
0
雪    币: 1966
活跃值: (751)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
3
抱歉,  之前图片链接的是国外medium文章里的图片链接,  国内无法访问.  我已经手动将各个图片都上传好了.
2018-2-6 18:25
0
游客
登录 | 注册 方可回帖
返回