问题
题目提示是:
假设一个名为Lab11-02.ini的可疑文件与这个恶意代码一同被发现
我们这次把分析过程放在上面
我们还是一样的先做一些静态的分析

这里有一个我们以前没见过的函数叫CreateToolhelp32Snapshot
这个函数在MSDN里面的定义是这样的
获取指定进程的快照, 以及这些进程使用的堆、模块和线程
书中对这个导入函数的解释是
搜索一个进程或者线程列表的导入函数
当然,我们还可以看见一些包括就像CopyFile和CreateFile函数,说明这个代码会操纵一些文件

还有三个操作注册表的函数
之后我们查看一下字符串有哪些

这里我们看到有趣的字符串有
installer
THEBAT.EXE
OUTLOOK.EXE
MSIMN.EXE
wsock32.dll
SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows
spoolvxx32.dll
AppInit_DLLs
\\spoolvxx32.dll
\\Lab11-02.ini
这些,尤其是AppInit_DLLs需要我们注意
对于这个键值的解释有
AppInit_Dlls
键值位于注册表
HKLM\Microsoft\Windows NT\CurrentVersion\Windows
下面,相对于其他的注册表启动项来说
这个键值的特殊之处在于任何使用到User32.dll 的EXE、DLL、OCX等类型的PE文件都会读取这个地方
并且根据约定的规范将这个键值下指向的DLL文件进行加载,加载的方式是调用
LoadLibrary
众所周知,Windows服务程序的启动时机是可以非常早的,往往在用户登录之前就完成启动了
而这个时候最常见的Run键值还不一定被处理完,而且Windows服务程序拥有相当高的权限
(默认是Local System,可以对系统里面所有的资源进行操作)
因此如果一个恶意软件被加载到Windows服务里面,那么是会非常危险的
前文提到,任何进程使用了User32.DLL,都会对AppInit_Dlls键值指向的DLL进行加载,如果是一个Windows服务程序,也不例外
上面这些话的意思就是,这个Appinit_DLLs驻留方式是在系统启动之前就会把恶意DLL加载在Windows服务中
而\Lab11-02.ini表明了这个程序有可能使用我们看到的那个Lab11-02.ini
其中还有一些像OUTLOOK.EXE和THEBAT.EXE还有MSIMN.EXE的字符串,书上说
这些都是邮件客户端,说明这个程序有可能操作这些客户端来发送邮件什么的
然后我们打开Lab11-02.ini来看看是什么

这个没有意义的乱码说明这个程序很可能有加解密的功能
CHMMXaL@MV@SD@O@MXRHRCNNJBNL
字符串中还有wsock32.dll,这个说明这个程序可能会使用网络的功能
还有那么几个RCPT的字符串,这个查了一下
RCPT是RECIPIENT 的缩写,为SMTP协议中的一个命令
说明这个代码很有可能用了邮件客户端来发送了一个邮件之类的
然后我们开始准备运行这个DLL
这里我们运行的时候要加一个DLL导出函数名字
这个名字就DLL的导出函数

我们可以在这里来找
现在我们运行,运行的同时记得做好系统的监控
rundll32.exe Lab11-02.dll, installer

我们看到了一个弹窗错误(因为我没加路径)
后来再次执行之后会发现,什么也没出现,就结束了,然后我们看看我们的监控的结果

这里删除了两个,分别是

新增加的键

这里是在浏览器的位置增加的键
以改变的值里面有这么一个需要注意的

这里在这个位置上修改AppInit_DLLs的值,修改为spoolvxx32.dll,说明很有可能这里有个spoolvxx32.dll被释放出来了
然后我们看看Procmon和Procexp的监控情况,对于不会常驻内存的进程来说,Procexp没多大作用
我们看Procmon的记录
我们设置过滤为Process name为rundll32.exe
因为注册表的值我们刚刚已经比对过了,现在我们再设置一个过滤是操作CreateFile
这里要注意的就是虽然DLL调用了CreateFile,但是不一定都是创建文件,有可能是打开文件的操作
我们最后找会发现在
C:\WINDOWS\system32\
下面,创建了一个文件叫
spoolvxx32.dll

其实这个文件和我们的Lab11-02.dll是一样的,最后恶意代码会尝试在C:\WINDOWS\system32\中打开Lab11-02.ini
为了让这个恶意代码能访问这个文件,我们把这个Lab11-02.ini放在C:\WINDOWS\system32\下
这个恶意代码会在最后将自己加载到user32.dll中,然后所有加载了user32.dll的进程也会加载了它
然后我们开始用IDA来分析它看看~~

然后我们可以看到这个导出函数的大概调用图,虽然没什么用

导出函数installer的内部第一个函数调用是RegOpenKeyExA
这是打开键的函数,我们主要看他的键的位置
SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows
如果RegOpenKeyExA这个函数执行成功,函数返回ERROR_SUCCESS也就是0
在test之后,因为eax为0,那么ZF=1,jnz不会跳转

之后函数会来到这里执行
这里的第一个函数调用是strlen,计算字符串长度的
这个函数的返回值就是eax,成为了下一个函数调用RegSetValueExA的最后一个参数cbData
这里调用RegSetValueExA来将键AppInit_DLLs的值改为了spoolvxx32.dll
之后代码关闭了设置键的句柄

之后函数就会来到这里,这里调用的第一个函数是sub_1000105B,我们看看这个函数是干什么的

这个函数是来查询系统的目录的,函数的返回值是目录的长度,目录的路径是保存到了Buffer这里,调用完函数之后,代码将路径赋值给了eax来返回
然后下面的函数调用是

这里的eax其实就是上面那个sub_1000105B函数调用之后查出来的系统路径,一般就是C:\WINDOWS\system32\
写成伪代码的形势就是
strncat("C:\WINDOWS\system32\", "\\spoolvxx32.dll", 104h)
这个就是最后会得到一个这样的字符串
C:\WINDOWS\system32\spoolvxx32.dll
之后

开始调用函数CopyFileA
这个从字面意思就是赋值文件的意思
这里的入参lpNewFileName其实就是上面组装成的字符串C:\WINDOWS\system32\spoolvxx32.dll,因为strncat的第一个参数是指向[ebp+Dest]的
而lpExistingFileName的意思是我们要复制的文件名字
而这个lpExistingFileName是个260大小的字符数组

因为这个函数里面并没有调用ExistingFileName这个参数,所以我们不知道这个变量的值是从哪里来的,不过我猜想就是我们现在Lab11-02.dll这个文件的绝对路径
之后就是一个CopyFileA之后导出函数就退出了
下面我们看看DLL的主函数

其中这里的hinstDLL是
DLL模块的句柄,该值是DLL的基地址。 DLL的HINSTANCE与DLL的HMODULE相同
而fdwReason是
原因码说明了为什么调用DLL入口函数
这里将DLL文件的基地址赋值到了dword_100035a4中去
还比较了fdwReason和1的大小
其中1代表的意思就是
由于进程启动或由于调用LoadLibrary,DLL正被加载到当前进程的虚拟地址空间中, DLL可以使用此机会初始化任何实例数据或使用TlsAlloc函数分配线程本地存储(TLS)索引
如果这个值等于1的话,cmp之后ZF=1
则jz会跳转,如果不跳转,就会退出了DLLMain
之后

我们上面说的hinstDLL指向的是这个DLL的基地址
所以这里的GetModuleFileNameA其实返回的是当前这个DLL的绝对路径
之后调用memset分配了几个空间
所以我们这里知道了,ExistingFileName的值其实就是这个DLL的绝对路径
之后会调用sub_1000105B这个函数我们上面分析过,会返回系统目录的路径
之后调用了strncat,不过这次最后拼接出来的字符串是这样的
C:\WINDOWS\system32\Lab11-02.ini
这就印证我们在Procmon中看到的调用C:\WINDOWS\system32\Lab11-02.ini的操作

之后代码会调用CreateFileA,这里的lpFileName其实就是我们上面strncat返回的那个最终的字符串
也就是这里会创建一个文件在
C:\WINDOWS\system32\Lab11-02.ini

然后这里开始调用ReadFile来读这个文件
这里的[ebp+hFile]其实在上面被eax赋值了
之后的函数调用

这里把我们上面读进内存的数据传了进去sub_100010B3中

这个函数我们看着像是一个解密的函数,这里我们可以硬分析这个编码,但是我们也可以通过OD运行之后直接看结果就行了~

这里我们找到函数的地址
100016CA
这里有个神奇的地方,只是跳到这个位置的时候,OD中已经自动标注出了这个字符串的解密
我甚至没运行DLL呢
billy@malwareanalysisbook.com

然后下面还有一个函数调用

在这里的地方,叫sub_100014B6,这个函数就是书上说的hook_install函数,这个函数会在这里安装恶意代码的hook
这个函数入参了一个1

之后这里会比较入参的值是否和0相当呢个,之后如果这个值是0的话,cmp之后ZF=1,则jz跳转,跳转之后就直接退出了
如果不等于0的话,像我们现在的值是1,就不会跳转继续往下执行

之后函数会来到这里
这里出现一个[ebp+Buf1]的地址
这里的Buf1等于-4
我们画一下栈图就知道这个值是多少了
-------
| ecx | <--- ebp-4
-------
| oebp | <--- ebp
-------
| 1 | <--- ebp+4
-------
.
.
.
所以[ebp-4]其实就是ecx的地址
然后这步第一个调用的函数是sub_10001075,这个函数会返回系统的系统路径

之后函数又会调用sub_10001104这个函数

之后函数进入这个函数之后的第一调用是strrchr
这个函数的意思就是找到最后在字符串中最后出现某个字符的位置
这里我们可以推算得出,这个eax或者说strrchr的第一个入参其实就是我们的系统路径的返回值,因为外面的一个函数将ebp+Buf1的地址赋值给了eax,于是sub_10001104的返回值就会直接写到ebp+Buf1上
所以这里的Str就是C:\WINDOWS\system32
然后要查找的值是5Ch换算成ASCII的话就是
\
这个会查找最后一个字符为\出现的位置,这里注意我们的系统路径最后是没有\
也就是我们的系统路径只是
C:\WINDOWS\system32
而不是
C:\WINDOWS\system32\
因为我们前面拼接字符串的时候,后面拼接的字符串是这样的
\\Lab11-02.ini
这样的
所以前面的字符串是不会提供\的
于是我们这里的strrchr返回的就是
\system32

这里先将返回值eax赋值给了[ebp+var_4],然后将又将[ebp+var_4]赋值给了ecx,然后ecx又加1,
这里的ecx代表了字符串的地址,于是+1就是往后偏移了一个位置
最后的字符串就变成了
system32
然后计算字符串的长度strlen

返回值是在eax中,这里计算eax的值是否为0
如果是0的话,test之后ZF=1
之后jz就会跳转,跳转之后就会看到将eax异或置0了
如果不跳转,也就是eax不为0
之后就会将[ebp+var_4]的值赋值给了eax也就是system32这个字符串
之后函数就退出了

从函数中出来之后,将返回值的eax赋值给了[ebp+Buf1]之后,和0比较大小,如果为0就jnz跳转之后就结束了
然后假设我们这里没有跳转结束

之后函数就会来到这里执行
这里的第一个函数调用是sub_1000102D这个函数

这个函数长这样的
我们可以用OD来进行动态分析
我们让OD在这里停住

我们可以看见,这时候我们入栈的参数是LOADDLL.EXE这个字符串
通过动态运行我们可以看出,这个函数sub_1000102D作用其实就是将字符串中的字母由小写变成大写的字母
然后一个栈缩小之后,将esp指向我们变成大写的字符串

之后计算字符串THEBAT.EXT的长度

之后又一个add esp, 4
将esp指回我们的变成大写的字符串
然后这里通过memcmp函数来比较我们刚刚变成大写的那个字符串和现在这个字符串THEBAT.EXE的相等与否
比较的长度为strlen("THEBAT.EXT")
如果不想等,就会继续比较其他字符串

包括OUTLOOK.EXE和MSIMN.EXE
如果其中一个相等,马上跳转到这里执行这些代码,如果不相等,马上跳转结束

这里首先调用的是sub_100013BD这个函数

这个函数首先会调用GetCurrentProcessId,这个函数是返回这个进程的PID的
之后准备调用sub_100012FE
在函数sub_100012FE中我们可以看到一些有意思的调用操作

第一个函数调用是sub_10001000,这个函数的目的主要是返回第一个push的DLL的基地址

函数在调用LoadLibraryA的时候,push入栈的参数是eax,也就是[ebp+8],这个地址其实是调用这个函数的第一个入参,也就是我们上面的kernel32.dll
这里恶意代码用LoadLibraryA将这个kernel32.dll加载到内存中
之后把这个句柄保存到hModule中,之后函数会调用GetProcAddress来检索kernel32.dll上函数,我们来看这个函数名字的地址是lpProcName,也就是ebp+0Ch,这个地址其实指向的是我们上面的那个入参OpenThread
然后函数找到了这个函数的地址,就返回了

我们注意这里的之后函数将返回值,也就是OpenThread函数的地址,赋值给了[ebp+var_4]也就是[ebp-4]

之后代码会调用GetCurrentThreadId函数,这个函数的作用是获取当前线程的标识符
之后会调用CreateToolHelp32Snapshot来获取当前进程的
获取指定进程的快照,以及这些进程使用的堆,模块和线程
这里的dwFlags的值是0x4也就是代表了TH32CS_SNAPTHREAD,意思就是
快照包括系统中的所有线程,同时会枚举线程
之后代码把返回值保存到了hSnapshot中

之后代码会来到这里
代码会调用Thread32First函数,来
检索有关系统快照中遇到的任何进程的第一个线程的信息
这里的[ebp+te.dwSize]的te是个THREADENTRY32结构
描述当拍摄快照时从系统中执行的线程列表中的条目
调用函数Thread32First成功之后,返回的结构就会保存到[ebp+te]中,这个结构是这样的
typedef struct tagTHREADENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ThreadID;
DWORD th32OwnerProcessID;
LONG tpBasePri;
LONG tpDeltaPri;
DWORD dwFlags;
} THREADENTRY32, *PTHREADENTRY32;
之后函数会把结构中th32ThreadID和[ebp+var_28]比较,这个var_28是函数当前线程的标识符

如果两个值相等的话,执行cmp之后ZF=1,那么jz就会跳转到最下面的loc_10001399这个地方

这里将我们的那个结构和hSnapshot压入了栈,然后就调用了Thread32Next这个函数
这个函数在MSDN中的解释如下
检索有关系统内存快照中遇到的任何进程的下一个线程的信息
然后这个函数的返回值是这样的
如果线程列表的下一个条目已被复制到缓冲区,则返回
TRUE,否则返回FALSE
假设返回的是TRUE,在计算机中TRUE的值一般是非零值,而FALSE的值一般是0
所以这里test返回值eax的时候,如果在内存中找不到已经复制的缓冲区,返回了FALSE,也就是0,那么ZF=1,之后就会沿着红线执行
就会结束这个小代码,如果找到了缓冲区,那么就会跳回去迭代循环比较
好了,我们退出这个函数的分析,回到主函数

我们上面是在sub_100013BD里面分析这个函数的作用,他是一个在进程中搜索某个线程的函数
之后下面的函数会将这个一个int类型的值和sub_1000113d压入栈中
其中我们快速看一下sub_1000113d这个函数的作用

这个函数的第一个call是strstr函数
而两个入参一个是字符串RCPT TO:,另一个是函数sub_1000113d的第二个入参,也就是上面的第二个char *Str的这个

我们可以看一下剩余的函数,基本可断定,这是一个构造邮件的函数
且如果在邮件中发现了RCPT TO的字样,这个代码会加上我们在ini文件中发现的邮箱,加上这个邮箱,这台电脑发送的邮件都会发到这个邮箱一份,这应该是一个盗窃邮件中关键信息的病毒
我们会到外面的函数

然后还会压栈几个参数,一个是字符串send,一个是字符串wsock32.dll,这个wsock32.dll函数是用来发送东西用的
之后就会调用函数sub_100012A3这个函数,我们看看这个函数

这个函数会去查找那个wsock32.dll函数
其中MSDN对GetModuleHandleA的定义是这样的
检索指定模块的模块句柄。该模块必须已由调用进程加载
这个函数其实就是找wsock32.dll的地址的作用
之后会调用sub_10001499这个函数
这个函数我们也是粗略的看一下

这个函数的第一个调用的是GetCurrentProcessId,这个函数在MSDN中的解释是
检索调用进程的进程标识符
其后就会退出这个函数了
上面分析了这么多偏的函数,我们现在总结归纳一下(因为时间过去久远)
函数进入了这个hook_install之后

第一个调用会是sub_10001075这个函数,这个函数的里面就是

有一个GetModuleFileNameA,因为这个函数的hModule参数的值被设置为了0
所以这个函数的会返回加载了这个DLL的进程的绝对路径,之后就会把这个路径返回了
以AppInit_DLLs作为驻留机制的恶意代码常常使用GetModuleFileNameA。这个恶意DLL几乎被加载到系统中的所有启动的进程,而恶意代码作者可能只是针对某些进程,所以他们必须确定运行恶意代码进程的名称
确定了这个返回值之后,之后会剔除多余的路径名,并且进行大写变换
之后就会于进程名THEBAT.EXE和OUTLOOK.EXE还有MSIMN.EXE比较,确定这个加载了恶意DLL的进程是这三个中的一个
如果不属于他们中的一个,就会退出
然后代码确定了自己加载到了怎么三个进程中的其中一个,就会执行下面的代码

在第一个调用sub_100013BD中,我们可以看到以下内容

这个函数里面第一个调用是GetCurrentProcessId这个,然后就会调用sub_100012FE这个函数,这个函数会返回当前运行进程内的所有线程标识符(TID,进程标识符是PID),之后这个sub_100012FE就会调用CreateToolHelp32Snapshot来遍历所有的线程

如果这个线程不是当前的线程,就会挂起这个线程,所以这个函数会挂起所有不是当前线程的所有线程
然后下一个函数sub_100012A3这里,这个函数就是执行安装挂钩的地方

这里第一个调用是GetModuleHandleA来获取wsock32.dll的句柄,之后用LoadLibrary加载这个DLL到空间中
将指定的模块加载到调用进程的地址空间中。指定的模块可能会导致其他模块被加载。
之后函数在wsock32.dll中查找send函数的地址

并且把这个地址赋值到了lpAddress中存起来了
之后就会调用sub_10001203

这里的arg_C和arg_8就是我们外面调用这个函数的时候那两个int类型的入参
这里的sub_10001203函数的入参是这样的

第一个参数是我们的send函数的地址,第二个参数就是arg_8,第三个是arg_C
这里会计算一个地址差

arg_4是我们第二个参数,这个int类型的参数其实是sub_1000113D的地址,这个函数就是构建邮件文件的那个函数
之后我们代码开始计算两个地址之间的地址差值
之后将这个差值减去5之后,存储在一个变量var_4中

这里为什么要减去5我们继续往下看就知道了
之后代码会调用函数VirtualProtect
这个函数的作用是
> 更改调用进程的虚拟地址空间中已提交页的区域的保护
然后地这个函数的lpAddress参数的值是
一个地址, 描述要更改其访问保护属性的页区域的起始页
这个地址就是我们构造邮件发送的那个函数的地址,这里用VirtualProtect改变了这个地址的保护属性
之后,这个函数会调用malloc

分配了一个0FFh大小的空间

下一个调用的函数就是memecpy这个函数
这个函数的作用是将我们上面刚刚分配好的空间,复制了send函数的前五个字节,为了方便跳转回来之后保证send函数代码的完整性
之后

在段函数我们应该分成两段来看
第一段是一个暂时不知道什么操作的代码,从memcpy之后到lea操作

第二段就是我们调用VirtualProtect这个函数的的函数入栈操作,这个段函数会再次调用VirtualProtect函数来对我们的地址进行保护更改
我们先看第一段
通过代码
mov edx, [ebp+var_8]
来把memcpy函数分配的空间的地址赋值给了edx
之后从空间偏移量为0Ah的地方开始,将值赋值成为0E9h,这个十六进制书上已经提醒我们是jmp的操作码,因为这里在我们从正常的send跳到恶意函数之后,要从恶意函数调回来正常的send函数,也会计算send到我们分配的内存空间的地址差,之后就会利用这个地址差来跳转到我们的复制了5字节send函数代码的空间,之后用这个代码来接上send函数之下的代码
这样可以保证代码的运行
之后会再次计算构造邮件那个函数的地址和memcpy分配空间地址之间的差值
之后将这个差值减去0Ah
下面

代码将0E9h机器码复制到send函数的开头,这里的edx其实就是send_address
之后代码将我们的var_4的值复制给ecx,var_4中存储了send函数和我们的构造邮件函数地址send_address之间的地址差值,这个差值还要减去5
这里有个问题,书上说,var_4存储的是我们想要跳转的地址,但是在这段代码中调用了var_4一共就两次,现在我们遇到的是第二次也是最后一次
然后第一次是计算函数send和sub_1000113d之间的差值,并把这个值减去5,之后保存在var_4中,为什么这个var_4现在成了要跳转的函数地址?
这个var_4不是应该是函数之间地址的差值么?
这里的有一个比较难以理解的就是这个地址的差值为什么要减去5
因为这个5,是存储0E9h和我们要跳转地址的所需空间,也就是比如我们的send函数和sub_1000113d之间差值是10d,且send函数在sub_1000113d之前,且send函数的开始地址是0,那么推理可知sub_1000113d的地址就应该是10d,这里用十进制说明了,方便理解
但是我们的0E9h和地址查占了5个字节的空间,之后就如果我们要跳转地址10d的地方,我们就需要要写jmp 5d,这个5d的意思是在jmp开始的地方开始数,往后5个地址
这里有个概念就是,函数的跳转地址,不是直接写0x1000113d这种,而是往后跳转多少字节这种
比如我们这里,有一个代码是这样的,这里用的是je跳转

0x74h是je的机器码,然后后面的0x23h就是我们要往后跳转多少字节
我们地址的下一个地址开始是0x1000153eh,那么加上0x23h之后,就是0x10001561h,所以就会跳转到这里的call spoolvxx.100013BD这个地方
这是汇编的跳转原理,理解这里,上面这个跳转就好理解了
函数先计算了地址差值,之后减去5,是为我们将来要增加的机器码和跳转地址做准备,之后

我们将我们要跳转的字节写入send_address中,这样在我们调用send函数的时候,代码找到send的地址,一进去第一个代码就是jmp,这样就将我们的send函数替换成为了gen_mail函数,而这个函数会发送恶意代码定义的东西
这个所谓的hook_function的工作原理就是这个
之后函数在最后一个调用sub_10001499这里

会执行和sub_100013BD相反的操作

之后这个函数会恢复所有的进程
1. 这个恶意DLL导出了什么?
解答: 这个恶意DLL中包含了一个教installer的导出函数
2. 使用run32dll.exe来安装这个恶意代码之后,发生了什么?
解答: 恶意代码会将spoolvxx32.dll复制到系统目录中C:\\WINDOWS\system32\
之后会从这个目录中打开Lab11-02.ini
3. 为了使这个恶意代码正确安装,Lab11-02.ini必须放置在何处?
解答: 放在C:\\WINDOWS\system32\下
4. 这个安装的恶意代码如何驻留?
解答: 恶意代码会将自己安装到AppInit_DLLS中,然后这个恶意代码就可以load到了所有导入了User32.dll的进程中
5. 这个恶意代码采用的用户态Rootkit技术是什么?
解答: 这个恶意代码针对wsock32.dll中的send函数安装了一个inline挂钩
6. 挂钩代码做了什么?
解答: 这个挂钩会检查对外的所有邮件,之后,会增加一个RCPT TO的邮箱
7. 哪个或者哪些进程执行了这个恶意攻击,为什么?
解答: 这个恶意代码仅针对的是MSIMN.exe或者THEBAT.exe或者OUTLOOK.exe这些程序,除非这些程序运行在进程空间中,不然这个代码不会安装
8. .ini文件的意义是什么?
解答: 这个ini文件提供了一个恶意的邮箱地址
9. 你怎样用WireShark动态抓获这个恶意代码的行为?
解答: 可以看到这个恶意代码发送的邮件信息
本文完
本文分析了一个恶意DLL的功能,包括导出函数、安装过程、驻留机制、Rootkit技术及挂钩行为。恶意DLL通过替换send函数来监视邮件并添加特定收件人。
1055

被折叠的 条评论
为什么被折叠?



