首页
社区
课程
招聘
[翻译]自解密中的乐趣
2018-6-25 14:55 3129

[翻译]自解密中的乐趣

2018-6-25 14:55
3129

2018-02-25,作者ViRb3

校对id:玉林小学生

译者id:frust

[本文作者为ViRb3,如果想在本博客发表文章,请先参考链接]

一个月前我正在参加每年一度的Hack Cambridged大会。且不说以往的编程和社交,在这次CTF比赛中,遭遇了强大对手——Avast团队。实际上,这也在很大程度上鼓舞了我,于是把任务带回家并在此完工。所有难题里面有一个非常有意思——一段两次自解密代码泄露了一点个秘密!我们今天就借助x64dbg来解析这个秘密。关于比赛更多信息参考文末。

经过之前的分析后,我们得到如下代码

===========================================================

| [ ADDR3S5  ] - [ H3X DUMP    ] ------ [ C0MM4ND                        ] |

===========================================================

| [ 00401000 ] > [ 31 C0       ] ------ [ XOR EAX,EAX                    ] |

| [ 00401002 ] . [ B0 40        ] ------ [ MOV AL,40                      ] |

| [ 00401004 ] . [ C1 E0 10   ] ------ [ SHL EAX,10                     ] |

| [ 00401007 ] . [ 80 F4 20    ] ------ [ XOR AH,20                      ] |

| [ 0040100A ] . [ FF D0        ] ------ [ CALL EAX                       ] |

| [ 0040100C ] . [ 50               ] ------ [ PUSH EAX                       ] |

| [ 0040100D ] . [ 8A 50 03    ] ------ [ MOV DL,BYTE PTR DS:[EAX+3]     ] |

| [ 00401010 ] . [ 30 54 01 04 ] ------ [ XOR BYTE PTR DS:[EAX+ECX+4],DL ] |

| [ 00401014 ] . [ FE C1         ] ------ [ INC CL                         ] |

| [ 00401016 ] . [ 80 F9 64     ] ------ [ CMP CL,64                      ] |

| [ 00401019 ] . [ 75 F5          ] ------ [ JNE SHORT 00401010             ] |

| [ 0040101B ] . [ 80 E9 5F    ] ------ [ SUB CL,5F                      ] |

| [ 0040101E ] . [ 00 C8         ] ------ [ ADD AL,CL                      ] |

| [ 00401020 ] . [ FF D0         ] ------ [ CALL EAX                       ] |

| [ 00401022 ] . [ F4               ] ------ [ HLT                            ] |

============================================================================

| [ ADDR3S5  ] - [ M3M0RY H3X DUMP                                       ] |

===========================================================

| [ 00402000 ] : [ 31 C9 C3 C0 | 1E 40 2C D0 | 3E 80 CA 41 | B0 CB C0 C0 ] |

| [ 00402010 ] : [ C9 DA 40 A8 | D3 DA 40 A8 | D8 F3 F0 00 | 90 03 2F D7 ] |

| [ 00402020 ] : [ 94 4A 1F 00 | 9E EC 3A 97 | DD 9E D9 EE | AD 3C 96 0D ] |

| [ 00402030 ] : [ E0 DF 9E E7 | 32 6B F7 D8 | 1D EA E1 CD | 6A 7E 6F 04 ] |

| [ 00402040 ] : [ 0A 0A 0A 0B | 0A 0A 0A 0B | 02 0B 7C 0A | 0A 0A 0B 0A ] |

| [ 00402050 ] : [ 0A 79 0C 0B | 0D 0C 09 03 | 03 79 0A 7F | 02 7C 7F 03 ] |

| [ 00402060 ] : [ 7F 0B 7C 08 | 03 79 0A 7F | 00 00 00 00 | 00 00 00 00 ] |

===========================================================

针对上述情况主要有两种方法:用x86模拟器(比如Unicorn),或者用调试器劫持执行流并用上面dump文件里的指令替换原来执行流,我觉得后者更有趣并且更便捷。于是这种情况下我们用第二个方法继续深入下去。

首先,我们需要将dump下来的数据以字节的形式查看。这一步我手动完成的,并以如下形式分隔hex转储和内存转储。

Hex dump (starts at 0x00401000):

Hex dump (从0x00401000开始):

31 C0 B0 40 C1 E0 10 80 F4 20 FF D0 50 8A 50 03 30 54 01 04 FE C1 80 F9 64 75 F5 80 E9 5F 00 C8 FF D0 F4

Memory dump (starts at 0x00402000):

Memory dump (从0x00402000开始):

31 C9 C3 C0 1E 40 2C D0 3E 80 CA 41 B0 CB C0 C0 C9 DA 40 A8 D3 DA 40 A8 D8 F3 F0 00 90 03 2F D7

94 4A 1F 00 9E EC 3A 97 DD 9E D9 EE AD 3C 96 0D E0 DF 9E E7 32 6B F7 D8 1D EA E1 CD 6A 7E 6F 04

0A 0A 0A 0B 0A 0A 0A 0B 02 0B 7C 0A 0A 0A 0B 0A 0A 79 0C 0B 0D 0C 09 03 03 79 0A 7F 02 7C 7F 03

7F 0B 7C 08 03 79 0A 7F 00 00 00 00 00 00 00 00

然后我们需要找出一段可执行空间。启动x32dbg(不是x64dbg,因为当前我们处理的是32位代码),打开任意一个32位可执行程序。这里我们直接打开x32.dbg自身得了。

程序初始化,然后停在了系统断点处:


现在我们需要把第一段dump数据插入到程序的开始处(当前执行的位置),使用Ctrl+Shift+V或者右键选择Binary(二进制编辑)->Paste(Ignor Size黏贴时忽略大小)。最后我们得到如下指令块。


现在最重要的是将Hex Dump和Memory Dump分开并使其间距为0xFDE个字节 (0x00402000到0x00401022之间的距离),这样可以保持原始结构不变。最简单的方法是选择第段代码块的最后一个指令(HALT),按下Ctrl+G或者转到表达式,弹出窗口后在编辑框已有内容后面直接添加“+FDE”。我这里是: 7714DB02+FDE


这样我们可以直接跳转到黏贴第二段dump数据的确切位置。黏贴数据后如下所示:(最后部分代码的截图)


我们记下这段代码块的起始地址(7714EAE0),然后返回到代码开始的地方。(Numpad *)

现在,我们对第一段指令执行单步跳过调试,直到运行到call eax。


查看下EAX寄存器:00402000。有没有感觉到似曾相识?该地址正好是第二段代码块的首地址(查看下原始dump数据)。但是在我们这个实例中该地址是无效的,我们得把它替换成刚才我们记下的那个真正的地址(译者注:7714EAE0)。双击寄存器改成7714EAE0。步进call然后继续单步步过,直到再次返回到第一段代码块。

接下来需要一点小技巧。注意下面的指令:


上图中指令替换了给定地址处1字节数据。这倒不是问题所在,不过在我们的实例中这个操作会引起一个异常。因为当前代码位于.text节,该节可执行但不可写。要解决该问题,我们需要选定与该节对应的内存页然后标记为FULL ACCESS,或者至少我们应该把可写属性赋予该段内存。在x64dbg中通过右键单击高亮的内存页->设置内存保护权限->全选->FULL ACCESS->设置权限然后关闭窗口。


现在回到CPU标签页

继续上述操作之前,我们分析如下指令:


虽然不需要,但我们还是可以推断出这是一段XOR循环解密指令。这段代码起止地址为7714DAF0 - 7714DAF9,共循环0x64次,十进制是100次。当然我们不会手动步过,选择jump (sub cl,5F)后面的指令按F2(或者右键->断点->切换)设置断点。然后运行程序(F9)。程序断下来后,我们步进后面的指令,直到跳转到第二段代码块,此时,该段代码块已经解密。

我们看下面的指令:


是不是很容易理解?观察EAX+A发现指向第一段代码块!注意下这个循环过程的最后:PUSH+RET指令组合与JMP等效。RET指令使程序返回栈顶的地址执行,此处栈顶恰好为PUSH EAX后得到的EAX值。

另一个小技巧是Push ret之前的xor指令。与原始dump比较,XOR指令会把最后两个字节(原文可能有误?)设置为零,于是代码转向16进制dump块的开始位置(00401000)。不过在分析这个实例中,这个XOR会把事情给搞乱。因为我们的地址不是以00结尾。要修正此处的话,我们得步进直到执行到PUSH指令,然后将EAX改为第一个代码块的首地址(本实例中为7714DAE0)。

跳过PUSH和RET指令,到达第一个代码块。查看下如下代码。


我们已经知道这些指令是干嘛的,步过到call eax指令CALL EAX,将EAX修改第二段代码块地址(7714EAE0),然后步进一步,到达第二段代码块,接着继续步过指令,直到再次回到第一段代码块。

现在检查下代码:


只是参与XOR运算的值不同,其他与上述解密相同。在CALL EAX处设置断点,运行(F9)然后单步步进,到达第二段代码块。

现在来分析下该二进制挑战最后一环:


仔细阅读指令,我们发现有点与以往不同:每次循环会覆盖EBX指向的内存一个字节,循环结束后覆盖ebx指向的一字节为f4被覆盖值为F4(F4对应汇编指令为HALT),该指令会结束程序。我敢打赌EBX(或者DL)是个关键数据。

为了把这些记录到日志,我们在关键点(mov byte ptr ds:[ebx],dl)处下断点。转到断点标签页,找到刚才下的断点,单击右键->编辑,然后填写日志记录条件,每次X64dbg执行到该指令时会写入到日志。本实例下,我们希望x64dbg可以记录DL的值,于是在Log Text编辑框填写:{DL}。花括号起到字符串格式化作用。我们还需要设置Break Condition(暂停条件)为零,这样只进行日志记录,不会暂停程序。


更多关于字符串格式化的资料,查看文档即可。

返回到CPU标签页并在JNE后面(mov byte ptr ds:[ebx],F4)添加个额外的断点。


运行程序,程序暂停在第二个断点处。

转到日志标签项并记录下这些字节。


我们得到如下数据:

50 44 55 3E 30 30 30 31 30 30 30 31 38 31 46 30 30 30 31 30 30 43 36 31 37 36 33 39 39 43 30 45

38 46 45 39 45 31 46 32 39 43 30 45

(译者注PDU>0001000181F000100C6176399C0E8FE9E1F29C0E

根据网站https://www.diafaan.com/sms-tutorials/gsm-modem-tutorial/online-sms-pdu-decoder/解析数据为:aleaiactaest,希腊语,意思是木已成舟,具体意义未明白,读者有理解望告知哈!)

万岁,终于打败了这个二进制恶魔!如果你在纳闷,我是如何根据这个字节型数组的SMS消息得知密码的,参考我的攻略然后去查看下其他惊人的挑战就明白了。

谢谢Avast神秘团队创办这次CTF挑战!



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

上传的附件:
收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回