前言
现代软件经常将混淆技术作为其反篡改策略的一部分,以防止黑客逆向分析软件的关键组件。他们经常使用多种混淆技术来抵御黑客的攻击,这有点像滚雪球:随着雪层的增多,软件规模也随之变大,使其逆向分析难度随之提高。
在这篇文章中,我们将仔细研究两种常见的混淆技术,以了解它们是如何工作的,并弄清楚如何去混淆。
概述
这里,我们将研究以下混淆技术:
基于IAT导入表的混淆技术
基于控制流的混淆技术
基于IAT导入表的混淆技术
在深入介绍基于IAT导入表的混淆方法之前,先让我解释一下导入表到底是什么。
什么是导入函数/strong>
当进行逆向分析时,需要弄清楚的第一件事,就是它如何调用操作系统的函数。在我们的例子中,我们将重点关注Windows 10系统,因为大多数视频游戏只能在Windows系统上运行。无论如何,对于那些还不知道的人来说,Windows提供了一系列重要的动态链接库(DLL)文件,几乎每个Windows可执行文件都会用到这些库文件。这些DLL文件中保存了许多函数,可以供Windows可执行文件“导入”,使其可以加载和执行给定DLL中的函数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oa7M0AaV-1648539503493)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f4ef5e296cab447885a97e999fe2073e~tplv-k3u1fbpfcp-zoom-1.image)]
它们为何如此重要/strong>
例如,Ntdll.dll库负责几乎所有与内存有关的功能,如打开一个进程的句柄(NtOpenProcess),分配一个内存页(NtVirtualAlloc,NtVirtualAllocEx),查询内存页(NtVirtualQuery,NtVirtualQueryEx),等等。
另一个重要的DLL库是ws2_32.dll,它通过以下函数处理各种 络活动:
Socket
Connect / WSAConnect
Send / WSASend
SendTo / WSASendTo
Recv / WSARecv
RecvFrom / WSARecvFrom
现在读者可能会问,知道这些有什么意义呢吧,如果您把一个二进制文件扔到像IDA这样的反汇编器中(我通常会做的第一件事),就是检查所有导入的函数,以便对二进制文件的功能有一个大致的了解。例如,当ws2_32.dll存在于导入表中时,表明该二进制文件可能会连接到Internet。
现在,我们可能想要进行更深入的研究,并考察使用了哪些ws2_32.dll函数。如果我们使用Socket函数并找出它的调用位置,我们就可以检查它的参数,这样,我们就可以通过搜索引擎查找相应的函数名,从而轻松地找出它所使用的协议和类型。
下面的地址0x7FF7D7F9B000引用了我们的函数0x19AA1040FE1,尽管看起来完全不是这么回事。您可能认为这是垃圾代码,但仔细看看,您会发现并非如此。
请仔细查看前两个指令:前面的指令是mov rax, FFFF8000056C10A1,后面的指令是jmp 19AA1040738,后面的都是垃圾指令。不管怎样,让我们跟随跳转指令,看看它会跳到哪里:
实际上,在经过数学运算之后,我们得到了指向advapi32.regopenkeyexa的指针!
注意:用红色标记的字节是垃圾代码。
现在仔细看看下面的第二张图片,我在这里所做的只是NOP最后一条指令的两个字节,以便让IDA显示隐藏在and [rdx+24448B48h], bh指令后面的指令。
然而,这张图只显示了它在控制流方面造成的混乱,但想象一下,当IDA竭尽全力根据垃圾指令创建这张图时,我的CPU是多么的痛苦。
现在,您可能想知道去混淆后的函数到底是什么样子的,别急,请看下图!
代码很长,是吧时,它比基于IAT导入表的去混淆处理更难理解,因为我们使用了一个实际的反汇编库来获得每个指令的大小和助记符。使用反汇编器几乎是必须的,因为我们还必须弄清楚一条指令是否与其他指令相冲突。
伪代码中提供了大量的注释,可以帮助大家理解其中的工作原理。
关于DeFlow算法的深入解释
主函数在递归调用DeflowChunk进行线性反汇编时,会跟踪已经发现的块。对新发现的块的跟踪是通过列表和循环完成的:由于在一个块中可能执行大量的分支指令,所以可能触发StackOverflow。
DeflowChunk将首先检查是否遇到给定的分支指令,如果是的话,则执行以下操作之一:
Ret——如果没有设置lastTarget,则停止
Invalid——如果没有设置lastTarget,则停止
ConditionalJump——如果在我们的缓冲区范围内,则计算目标地址并跟踪
UnconditionalJump——如果在我们的缓冲区范围内,则计算目标地址并保存以供进一步分析
Call——如果在我们的缓冲区范围内,计算目标地址并保存以供进一步分析
如果我们没有设置lastTarget,将检查当前指令是否是在缓冲区范围内跳转的ConditionalJump(isJmp标志),如果是的话将lastTarget设置为ConditionalJump的目标。
一旦我们获得了符合条件的lastTarget,就用当前的指令指针减去lastTarget,从而计算出还需要反汇编多少字节(stepsLeft)。
在计算出stepsLeft之后,需要检查该值是否等于零。如果该值大于零,我们将继续线性反汇编。
当stepsLeft小于零时,表示汇编代码与下一条指令发生了冲突。这很可能意味着负责设置lastTarget的最后一个ConditionalJump是一个不透明的条件,这意味着我们当前的块很可能永远不会被执行,而是用来掩盖后面的几条合法的汇编指令。
我们可以通过将ConditionalJump的第一个字节修改为0xEB,使其成为UnconditionalJump,从而修复该问题。为了进一步扫清障碍,我们还修改了最后一个ConditionalJump和lastTarget之间的所有字节。
然后,对于在线性反汇编过程中发现的每个调用或条件跳转操作,都进行相应的处理。
小结
不仅是恶意软件,像视频游戏这样的合法软件也倾向于使用这类混淆技术来尽可能多地隐藏有价值的信息,希望能防止软件被逆向工程。然而,正如您所看到的,我们已经成功地解开了这两种技术的神秘面纱,并能够揭示所有隐藏的信息。
无论如何,我们仍然可以得出结论:这些混淆技术极大地提高了逆向分析的难度,这是一个很好的方法,在一定程度上可以阻止软件被逆向工程。最重要的是,Deflow算法本身需要几分钟/小时(取决于文件大小),就能消除混淆技术对二进制文件的完整控制流造成的影响。
文章知识点与官方知识档案匹配,可进一步学习相关知识 络技能树WAN技术PPP22111 人正在系统学习中
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!