首先我们通过静态分析发现该程序的导入表中有很多文件操作类函数。
又在StringView中发现了Lab07-03.dll字符串,我们推断程序有可能调用了该动态库,但却没有发现任何loadlibrary或者getprocaddress函数。所以我们的推测可能是错的,他有可能并没有加载这个动态库。
其中也出现了Kernel32.dll的完整路径。但还出现了一个Kerne132.dll的完整路径,很明显,这个程序可能恶意的冒充Kernel32.dll,从而达到一些目的。
接下来,我们用IDA分析该动态库。
相同的我们先查看它的导出表,导入表以及StringView。
很奇怪的是,这个动态库并没有导出任何函数,他不能被一个程序所导入。
我们在导入表中发现了一些网络通信的函数。而且看到了CreateProcessA函数,这意味着它很可能创建了另外一个进程。
在StringView中,我们看到了一个IP地址127.26.152.13,这个恶意代码可能会连接它。
分析DLL:
首先由于代码冗杂,我们开启FunctionsCallView,通过查看函数调用来阅读代码。
首先,是调用通过库函数__alloca_probe来在空间中分配栈。接下来调用了OpenMutexA和CreateMutexA函数是为了保证程序单开。(只有一个实例在运行)
接下来的函数通过一个socket来建立连接(127.26.152.13),并且传输和接收数据。这个函数以对Sleep和CreateProcessA的调用结束。我们并不知道发送的内容是什么。但是我们可以猜测这个DLL在做什么。对于一个发送和接收数据并创建进程的函数,对于它最好的解释是被设计来从一个远程机器接受命令。
我们发现了参数127.26.152.13(远程主机IP),80端口(常被web流量使用)。
通过对send函数传参的分析,它向远程机发送了一个“Hello”,这似乎是受害机发送的一个问候,来使服务器知道他已经准备好执行下一个命令。
我们发现,这个程序期望在回复中接受到一些数据,并将其会保存在esp+120ch+buf(栈上的缓冲区)
接下来我们判断这个程序会在回复中做什么。我们发现这个缓冲区值(esp+120ch+buf)在后面几行被检查。
虽然到Esp的偏移发生了变化,但是同一个缓冲区(站的大小发生了变化),IDA同时标记了buf来告诉我们是同一个值。
并且在代码中,调用strncmp函数,判断它前五个字符是不是字符串“Sleep”,如果是,它将调用sleep函数来睡眠60秒。这告诉我们如果远程服务器发送的是sleep命令,这个程序将会调用sleep函数。
同样的,代码检查这个缓冲区是不是以“exec”开始,如果是,strncmp函数将会返回0,并顺序执行调用CreateProcessA函数。我们发现这个函数有很多参数,最有价值的是它的CommandLine参数,他告诉我们要被创建的进程。而且代码显示CommandLine中的字符串被保存在栈的某处。但是我们却没有发现该指针在别处被调用。
我们去查看CommandLine的定义:
我们发现recv函数的接受缓冲区是从1000h开始的,并且这个值使用lea指令来设置的,这告诉我们这个数据本身是保存在栈上的,并且不仅仅是一个指向数据的指针。同样我们发现了CommandLine的定义是0FFBh,这说明它会接收缓冲区中保存的任意5字节的东西。
所以从远程服务器中接受到的字符串是exec FullPathOfProgramToRun。它会用FullPathOfProgramToRun来调用CreateProcessA。
现在我们知道了该Dll实现了后门功能,这允许攻击者通过发送回复给80端口上的一个数据包,来启动一个系统上的可执行文件。
分析EXE:
首先我们查看这个可执行文件的main函数,首先我们看见了一个对命令行参数的检查。
首先,它比较检查参数个数是否为2.如果参数不是2,代码跳转是程序提前退出。(这是我们试图执行动态分析,程序快速终止的原因)
接着程序将argv[1]移动到EAX,以及将WARNING_THIS_WILL_DESTROY_YOUR_MACHINE字符串移动到ESI寄存器。然后循环比较,如果他们不一样,这个程序将会跳转到一个位置,并从这个函数返回,不执行任何其他事情。
接下来,我们看到了对CreateFile,CreateFileMapping以及MapViewOfFile的调用,这里它打开了Kernel32.dll和Lab07-03.dll。
接下来,我们使用FunctionsCallView。
我们发现两个其他函数的调用:Sub_401040和Sub_401070。这些函数相对较短且没有相互调用。这些函数在比较内存,计算偏移,或者写入内存。我们暂且跳过。接着向下观察。
我们发现它调用了两个closehandle函数将两个动态库文件进行了关闭清理操作,所以我们知道了这个恶意代码完成了对那些文件的编辑。
接下来我们看到调用了CopyFileA函数,这个函数将Lab07-03.dll中的内容复制并保存到C:\\windows\\system32\\kerne132.dll(注意文件名,冒充的Kernel32.dll)但我们不知道如何被调用(冒充的动态库)。
接下调用了exit函数,说明该段程序功能结束。
接着向下,我们发现了一个很有趣的参数和一个函数Sub_4011E0。
进入函数,我们发现首先它在“C:\\*”上调用FindFirstFile,来搜索C:驱动器。
跟随FindFirstFile函数,我们发现了许多算术运算和比较。所以我们先行跳过。
使用FunctionsCallView。
我们发现代码又一次调用了sub_4011E0,说明这是一个本身递归函数。
跳过malloc函数,我们发现调用了Stricmp函数。
然后我们寻找压入的参数寻找是什么比较。(通过push指令寻找)发现它的参数是一个字符串和“.exe”检查,然后调用了sub_4010A0函数,来查看他们是否匹配。
我们发现程序又调用了FindNextFileA函数,同时又发现了jmp跳转指令,所以我们推测这个功能在一个循环中被执行。并且在函数末尾FindClose被调用,以及一些异常终止代码。
所以我们断定这个函数正在搜索C:驱动器来查找.exe文件,如果这个文件有.exe扩展,对它进行一些操作。这个递归告诉我们它可能搜索整个文件系统。
然后我们进入sub_4010A0进行分析。
首先调用了CreateFileA,CreateFileMappingA和MapViewOfFile来映射整个文件到内存,并且这个程序可以读写这个文件,而没有附加任何的函数调用。
下面我们发现调用了IsBadReadPtr,这主要验证了指针是否有效。由于上面出现过sub_401040,先行跳过,接下来发现一个stricmp函数。
它检查了一个字符串值是否为kernel32.dll。一些指令后面,我们看到了这个程序调用后repne scasb,或者rep movsd,这在功能上和strlen以及memcpy函数是等价的,为了看到哪个内存地址正在被通过memcpy调用写入,我们需要判断在EDI中保存什么,这个寄存器被rep movsd指令使用。然后我们发现EDI被用在EBX的值加载,所以我们寻找EBX在哪里被设置。
可以看到EBX被用在传给stricmp的值来加载。这意味着如果找到字符串Kernel32.dll,这段代码用某些东西来替换它。要判断他用什么来代替字符串,我们转到rep movsd指令,并查看到源头在dword_403010。
我们发现这是一个DWORD型的数据,用来覆盖字符串不合逻辑,所以我们尝试使用a键,将该数据转换为string。发现程序会用kerne132.dll代替kernel32.dll。
现在,我们知道这个可执行文件遍历整个文件系统来查找.exe结尾的文件,在.exe文件中找到字符串Kernel32.dll的位置,并使用kerne132.dll来代替。而这个kerne132.dll中的内容Lab07-03.dll。这意味着kerne132.dll会代替Kernel32.dll被修改过的可执行文件所加载。
做到这里,我们清楚了系统上的每一个可执行文件都试图加载我们的恶意DLL。
我们去查看两个动态库是否被代码修改。
通过计算Kernel32.dll的前后MD5发现并没有什么变化,说明恶意代码并没有修改Kernel32.dll。而不同的是kerne132.dll发生了变化,我们使用了PE工具发现它多了一个导出节,它导出了所有Kernel32.dll的导出函数,并且是重定向的导出函数,所以实际功能还是在Kernel32.dll中。
总之,该程序遍历整个文件系统来查找.exe结尾的文件,在.exe文件中找到字符串Kernel32.dll的位置,并使用kerne132.dll来代替。而它所要执行的恶意代码保存在kerne132.dll的dllmain函数中。
原文作者:风沙QAQ
原文链接:https://bbs.pediy.com/thread-249025.htm
转载请注明:转自看雪学院
更多阅读:
[分享]恶意代码分析第七章Lab-07-03实战分析笔记
[原创]尝试用vmp指令模拟ARM条件跳转
fuzzing技术总结
[原创]cve-2018-8453分析及利用EXP编写