在360Netlab的旧文章《“双枪”木马的基础设施更新及相应传播方式的分析》中,在双枪木马传播的过程中提到了恶意驱动程序kemon.sys,它具有自定义加密的Ascii字符串和Unicode字符串100+:
这只是双枪特洛伊木马通信链中的一个小技术点,因此文中没有说明采用何种加密算法以及如何解密它,为分析师做更方便的样本分析工作。但这个技术点有点有趣,特别是对于反向进入阶段的朋友,你可以参考解决方案。最近我遇到了这个驱动程序的最新变种,我咨询了该团队的老司机。只写一篇短文。
感谢老司机的疑虑。我们也欢迎所有的主人给我们建议,并提出一些更快,更准确的解决方案。
2.样本概述
MD5:b001c32571dd72dc28fd4dba20027a88
2.1字符串加密
驱动程序中使用的100多个字符串是自定义加密的。在设置每个IRP调度功能和卸载例程之后,第一步是依次解密字符串。样本在IDA中打开,部分解密过程如下:
整个解密过程的功能是sub_100038C4,它将调用两个特定的解密函数:sub10003871和sub_10003898。前者解密Ascii字符串,后者解密Unicode字符串。它有两个参数:arg1->
要解密的字符串地址; arg2->
字符串的长度。这两个函数分别命名为DecryptAsciiStr和DecryptUnicodeStr。这两个函数在IDA中看到的外部参照状态如下:
2.2加密算法
如前所述,算法并不复杂。以DecryptAsciiStr函数为例:
反编译看:
DecryptUnicodeStr算法实际上是相同的,只是因为字节组成不同,所以两个解密函数分开编写:综上所述,解密过程实际上是:将当前字节后的特定偏移量的字节与0xC进行异或,然后替换当前字节,将解密后的字节写入当前位置,即解密完成。 。我不熟悉密码学。我不知道这是一个有着名的加密算法。它看起来像凯撒密码的修改版本。知道这一点的朋友欢迎提供建议。
解密
在了解了上述情况之后,是时候解密一百多个字符串了。由于IDA用于分析此样本,理想的情况应该是批量输出这些字符串并直接在IDA中显示,然后执行后续分析。由于它是自动化批量解密,因此编写IDAPython应该是最简单的方法。最终结果如下:
3.1姿势1——自实现解密算法
首先想到的是:两种解密算法,并不复杂,可以直接编写一个IDAPython脚本来实现这两种解密算法。解密后,纯文本字符串直接写入IDB文件并在IDA中呈现。两种解密算法的Python版本如下(使用IDB的补丁操作):
以下是制作unicode str时的操作的一点解释:
Old_type=idc.GetLongPrm(INF_STRTYPE)
idc.SetLongPrm(idc.INF_STRTYPE,idc.ASCSTR_UNICODE)
idc.MakeStr(argv [0],argv [0] +(argv [1] * 2))
idc.SetLongPrm(idc.INF_STRTYPE,old_type)
在IDA UI界面中,您可以选择生成的字符串的类型(如下所示),快捷键只有一个A,相应的idc函数是idc.MakeStr(0。但是,ida.MakeStr()函数默认情况下生成Ascii字符串。要生成Unicode字符串,需要调用idc.SetLongPrm()函数来设置字符串的类型。
IDA支持的字符串类型如上所示,因此,idc库中的定义如下:
ASCSTR_C=idaapi.ASCSTR_TERMCHR#C风格的ASCII字符串
ASCSTR_PASCAL=idaapi.ASCSTR_PASCAL#Pascal样式的ASCII字符串(长度字节)ASCSTR_LEN2=idaapi.ASCSTR_LEN2#Pascal-style,长度为2个字节
ASCSTR_UNICODE=idaapi.ASCSTR_UNICODE #Unicode字符串
ASCSTR_LEN4=idaapi.ASCSTR_LEN4#Pascal-style,长度为4个字节
ASCSTR_ULEN2=idaapi.ASCSTR_ULEN2 #Pascal式Unicode,长度为2个字节
ASCSTR_ULEN4=idaapi.ASCSTR_ULEN4#Pascal式Unicode,长度为4个字节
ASCSTR_LAST=idaapi.ASCSTR_LAST#最后一个字符串类型
因此,要生成Unicode格式的字符串,首先需要使用idc.SetLongPrm()函数设置字符串类型。其中idc.INF_STRTYPE表示字符串类型常量,在idc库中定义如下:
在Python中实现解密函数后,如何模拟这个波解密过程依次解密100多个字符串?这可以通过组合IDA中的外部参照和idc.PrevHead()函数来完成:
首先找到xrefs调用两个解密函数的位置;
然后使用idc.PrevHead()来定位两个解密函数的参数地址并解析出参数的值;
执行解密功能,将解密后的纯文本字符串写回IDB和MakeStr。
3.2姿势2——指令模拟
此示例中的字符串解密算法并不复杂,因此可以轻松编写Python版本并使用IDAPython脚本直接在IDA中解密。那么如果字符串解密算法更复杂,那么很难在Python中实现一个版本?
此时,请考虑命令模拟器。
近年来,Unicorn作为新一代命令模拟器一直是业界的一大火。基于Unicorn的IDA教学模拟插件也已经开发出来,例如简单的IdaEmu和FireEye开发的功能强大的Flare-Emu。指令模拟器可以模拟执行汇编指令,IDA中的指令模拟插件可以模拟IDA中指定指令片段的执行(需要手动指定起始指令地址和结束指令地址,并设置初始值)相关登记册的状态)。通过这种方式,我们可以使用指令模拟插件来模拟IDA中上述批量解密指令的执行。模拟解密字符串的汇编指令,并自然解密字符串。这种情况下的指令模拟姿势基于Flare-Emu。
但是,这种姿势需要注意两个问题:
指令模拟器无法模拟系统API。如果在解密函数中存在调用系统API的操作,则该指令模拟该位置并且需要花费很多精力。
所谓的模拟指令执行实际上只是一个模拟,它不会修改IDA中的任何数据。在这种情况下,您需要在执行指令模拟器后将纯文本字符串修补到IDB文件,以便您可以在IDA中看到纯文本字符串。
3.2.1 hook api
第一点是IdaEmu需要实现相关API本身的功能,并在指令片段中隐藏相应的API以便顺利模拟。例如,在下面的示例中,在指令片段中调用_printf函数,然后我们需要手动实现_printf的函数并在指令片段中挂钩_printf:
Flare-Emu更方便,它们直接在框架中实现一些基本的系统API,而不是手动实现和执行Hook操作:
原因是因为kemon.sys示例中字符串的批量解密涉及对memcpy函数的调用:
这样,使用Flare-Emu直接模拟执行应该是一个更方便的选择。
3.2.2补丁IDB
点2,将模拟结果写回IDB文件并在IDA中显示。
第一个问题是如何获得成功模拟执行——明文字符串的结果。如在字符串解密算法的上述描述中所述,解密的字节(字节)将直接替换密文中的特定字节,并解密密文的先前dataLen字节,即纯文本字符串。该字节替换操作实际上对应于Unicorn指令模拟器中定义的MEM_WRITE操作,即写入存储器,并且只有字符串替换操作在字符串解密过程期间写入存储器。 Flare-Emu提供了一个可以挂钩多个内存操作的memAccessHook()接口(如下所示):
memAccessHook可以是您定义的函数,只要访问内存进行读取或写入,就可以调用该函数。它具有以下原型: memAccessHook(unicornObject,accessType,memAccessAddress,memAccessSize,memValue,userData)。idc.PatchQword():补丁8字节;
指令模拟器中补丁的操作不仅限于PatchByte。根据我打印出来的打印操作说明,我可以看到有3种补丁操作(如下图所示):1个字节,2个字节和4个字节,所有这些都在上面的mem_hook()中有3个功能。 memAccessSize的种类。
通过识别和解决“System API Hook”和“Capture Command Simulation Results and Patch IDB”问题,您可以编写准确的IDAPython脚本。
3.2.3 Radare2 ESIL仿真
在r2上还有一个名为ESIL(可评估字符串中间语言)的强大指令模拟模块:
在r2上使用这个东西来模拟解密一批字符串的指令,你不必像在IDA中那样自己编写IDAPython脚本。