深入解析Flare-On 2015逆向挑战:Android ARM破解、.NET反混淆与RC5加密

本文详细解析了Flare-On 2015逆向挑战中的四个关键技术案例,涵盖Android ARM动态调试、.NET反混淆、Intel Pin指令计数攻击和RC5加密算法破解,为安全研究人员提供实用的逆向工程方法和工具指南。

Flare-On逆向挑战2015

今年夏天,FireEye的FLARE团队举办了第二届年度Flare-On挑战赛,面向逆向工程师、恶意软件分析人员和安全专业人士。总共包含11个挑战,每个挑战采用不同的反逆向技术且格式各异,从简单的密码破解到内核驱动,再到图像隐写术。

本文将重点介绍11个挑战中最有趣的四个(具体为第6、7、9和11关),以及一些对未来类似挑战有用的工具和材料。

挑战六

摘要:挑战六是一个混淆的Android应用破解程序,接收并验证用户输入。

使用技术:远程Android调试、IDAPython

这一关的新颖之处在于它不是Windows二进制文件(大多数挑战针对Windows平台,显然在寻找Windows逆向工程师;]),并且需要ARM逆向知识。

该关卡的核心是包含密钥检查算法的ARM共享对象库。在备用Android恶意软件指定手机或模拟器上启动应用后,我们看到以下屏幕:

尝试输入“password”进行猜测,但没有成功。

在IDA中打开(如果您先运行了它而没有先分析……您做得对),我们看到库的重要部分是比较函数。

逆向追踪此比较,我们找到生成预期输入值的函数。我们只需要静态逆向此函数。该解密函数的主要部分是对二进制文件中存储的加密密码进行因式分解。

将此函数的逻辑与加密字符串一起移植到Python中。使用IDAPython从二进制文件中提取必要数据使此过程更加容易。对于从未使用过IDAPython的人,脚本如下所示。

主要IDAPython脚本

上述逻辑通过静态逆向从混淆的二进制文件中提取。IDAPython帮助从应用中提取正确的数据段。

IDAPython脚本转储素数索引映射

IDAPython脚本转储“轮次”

运行最终的Python脚本解密字符串,打印出预期密码。

Should_have_g0ne_to_tashi_$tation@flare-on.com

旁注

除了静态逆向,还可以使用gdbserver.py进行远程调试,附加到手机上运行的应用或模拟的Android服务器。

然后可以在比较处设置断点,并从调试器中读取解密后的标志。为此,提取Android apk,设置Android调试环境,并在调用共享的混淆对象时中断。

网上有一些很好的资源展示如何在Android上设置远程gdb环境。具体来说,一些有用的资源可以在本文底部找到。

挑战七:YUSoMeta

摘要:挑战7是一个混淆的.NET应用程序,验证用户提供的密码。

使用技术:.NET反混淆、Windbg特殊断点

挑战7,YUSoMeta,是一个.NET便携可执行格式应用程序。像所有优秀的逆向工程师一样,我们将.NET应用程序加载到IDA Pro中。

浏览函数窗口揭示了许多命名奇特的方法。许多类和类字段名称不仅包含ASCII字符(如“正常”.NET应用程序所示)。这表明存在混淆。

在十六进制编辑器(我们选择HxD)中打开应用程序,我们找到一个有趣的字符串:“Powered by SmartAssembly 6.9.0.114”。

SmartAssembly是.NET应用程序的混淆器(很像Trail of Bits的MAST)。幸运的是,de4dot是一个反混淆SmartAssembly保护应用程序的工具。反混淆后,诸如.NET Reflector之类的工具可以将公共中间语言(CIL)程序集反编译回C#。使用此方法,我们找到一个密码验证函数。

该挑战捕获用户输入获得的密码,并将其与通过一系列 somewhat复杂操作生成的预期密码进行比较。获取预期密码的最简单方法是使用Windbg。

首先,我们通过加载SOS调试扩展来设置Windbg,以检查托管程序(即.NET应用程序)。

在Windbg中

其次,我们需要设置符号路径以获取调试符号。

在Windbg中

之后,我们在字符串相等比较函数System.String.op_Equality in mscorlib.dll上设置断点。注意:我们运行!name2ee两次,因为!name2ee在第一次发出时总是失败。

在Windbg中

中断后,我们使用!dumpstackobjects检查堆栈对象。用于提取密钥的密码应在.NET堆栈上。

在Windbg中

挑战九:you_are_very_good_at_this

摘要:挑战9是一个混淆的命令行密码检查应用程序

使用技术:Intel PIN、Windbg、Python、IDA Pro

挑战9,you_are_very_good_at_this,是一个x86便携可执行命令行应用程序,接受一个参数并根据预期密码进行验证——一个基本的破解程序。

像所有优秀的逆向工程师一样,我们立即在IDA Pro中打开应用程序,这揭示了一堵巨大的混淆代码墙——显然动态分析是要走的路。

对我们来说,有两种明确的解决此挑战的方法。第一种使用pintool,第二种使用Windbg。

第一种解决方案:Pin

我们知道破解程序通过大量操作以某种方式逐个字符检查命令行输入。幸运的是,我们不需要知道更多。

使用简单的指令计数Pintool(来自Pin教程的inscount0.cpp完美工作),我们可以计算执行以检查输入的指令数,并确定它是在第一个字符失败还是在第n个字符失败。这使我们能够逐字节暴力破解密码。

显然,在第二种情况下执行了更多指令,其中第n个字符不正确,并且exit()直到程序执行后期才被调用。我们可以利用这些知识确定前n-1个字符是正确的输入。

使用Python,我们编写pintool脚本,为密码的第一个字符使用每个可能的可打印字符,给出二进制执行的指令计数。

Python伪代码

这样做,所有输入都给我们相同的指令计数结果,除了包含正确第一个字符的输入。这是因为正确字符是唯一通过应用程序验证器的字符。当这种情况发生时,二进制文件执行了否则不会运行的附加指令。

现在我们知道一个字符,我们在脚本中添加一个for循环来检查异常值,并对密码的每个字符做同样的事情……成功泄露密码!

去年,Flare-on挑战6也可以用完全相同的方式解决,感谢@gaasedelen对此的详细记录。

第二种解决方案:Windbg/Python/IDA Pro

以传统方式解决此挑战,我们启动WinDbg并在kernel32!ReadFile上设置断点。我们追踪kernel32!ReadFile调用者,并通过在IDA Pro中交叉分析手动反混淆密码检查循环。

密码检查循环使用CMPXCHG指令比较用户提供的密码字符和预期密码。

我们确定感兴趣的寄存器是AL和BL寄存器。追踪感兴趣寄存器的数据流揭示AL寄存器遇到一些转换,作为CL和AH的函数,但最终源自用户提供的缓冲区。这意味着BL寄存器包含预期密码的转换字符。

幸运的是,我们能够在密码验证循环中的指令处精确断点,并提取必要的寄存器值(即BL、CL和AH寄存器)以解码实际密码。

在Windbg中

为了解码预期密码,我们为每个“字符轮次”获取打印的BL、CL和AH寄存器值,并实现一个Python函数来逆转对AL完成的XOR/ROL转换。

Python伪代码

我们通过连接每个“字符轮次”的ror_xor输出来挖掘密钥。

挑战十一:CryptoGraph

摘要:挑战十一是一个使用rc5算法和混淆密钥的加密jpeg图像。结果是一个坚实的“逆向”挑战。

使用技术:RC5加密算法、Windbg、资源段提取

最终挑战。挑战十一,CryptoGraph.exe,是一个命令行二进制文件,当没有传递参数时,在系统上创建一个垃圾jpeg文件。仔细看,我们看到二进制文件确实接受一个命令行参数。然而,当传递任何数字时,二进制文件“永远”循环。

在IDA Pro中打开二进制文件,我们假设标志将以某种方式出现在正确创建的图像中。这意味着我们通过从“WriteFile”向上追踪调用来开始逆向。

几个函数后,我们意识到资源#124正在被加载、解密并保存为此图像文件。

解密算法通过Google容易识别为RC5。第一个结果是卡巴斯基关于Equation Group及其使用RC5/6的报告。

现在我们需要的是RC5解密密钥。不幸的是,密钥长度为16字节,不能轻易暴力破解。然而,进一步逆向,我们意识到密钥是两个不同RC5解密阶段的结果。

第一次解密使用介于0x0和0xf之间的随机索引字节进行索引。这创建一个8字节密钥。

然后此密钥用于另一个RC5解密,该解密实际上在偏移处获取加密源(Resource_122),该偏移是相同随机索引字节的函数。第二阶段仅解密16字节,即加密jpeg Resource_124所需的16字节RC5密钥。

显示不同解密阶段的图表

在Windbg中中断,我们意识到Resource_121的解密是导致程序似乎永远循环的原因。事实上,从0x0运行到0x20的循环每次迭代执行时间呈指数级增长。

给定RC5密钥长度和用于索引到解密的Resource_121的算法,该算法给我们这个RC5密钥,我们确定只有资源的一个部分是必要的。

索引算法

仅解密Resource_121的相关位显著减少执行时间。

索引算法不完全确定,最多可以索引到解密资源的前784字节。

因为每个循环解密48字节(硬编码为传递给解密函数的参数),我们需要让主解密循环运行超过0x10次迭代,然后跳出函数。

用于计算所需循环的数学

使用Windbg,我们在循环处中断以在0x10次迭代后停止。这意味着只有部分密钥Resource_121将被解密,但幸运的是,这是8字节密钥唯一需要的部分。

最后需要暴力破解的是介于0x0和0xf之间的单个字节值,它影响索引算法。此字节影响先前讨论的8字节密钥的生成以及Resource_122的索引,从该索引解密16字节密钥。

Python-Windbg伪代码

在Windbg中编写此脚本(完整脚本在此处找到),我们让二进制文件运行0xf次;每次在0x10次迭代后停止循环。

在第0x9次迭代(魔法索引字节)时,正确解密的图像保存到文件,标志可以读出:]。

完结

感谢FireEye的FLARE团队提出这些挑战,并成功迫使我打开Windows VM并学习一些新的逆向工具。希望明年的挑战同样有趣、晦涩,也许更难一点。

参考资料、指南和工具

我们发现对挑战有用的东西。

Windbg:设置符号路径 Intel PIN:教程 Android远程调试:如何设置gdb服务器 RC5加密算法:卡巴斯基关于Equation组的报告

如果您喜欢这篇文章,请分享: Twitter LinkedIn GitHub Mastodon Hacker News

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计