恶意代码分析实战 Lab 11-1 习题笔记

问题

1.这个恶意代码向磁盘释放了什么?

解答: 我们刚刚完成了Windows内核病毒的分析,包括了一些过时的RootKit技术的分析,现在我们回归二进制分析

这里要分析的文件是Lab11-1这个文件,我们先做一些基本的静态分析

我们先看KERNEL32.DLL这个导出DLL

恶意代码分析实战 Lab 11-1 习题笔记_第1张图片

我们可以看到这里有个我们需要注意的CreateFileA导出函数,还有下面那个ExitProcess函数,我们找找看有没有CreateProcess这个函数,找了一圈没有

然后我们再看ADVAPI32.DLL

恶意代码分析实战 Lab 11-1 习题笔记_第2张图片

这里有个RegCreateKeyExA这个东西,还有下面的RegSetValueExA这个函数,都会改变注册表

然后我们看下一个DLL,是ADVAPI32.DLL下面的KERNEL32.DLL里面的导出函数

恶意代码分析实战 Lab 11-1 习题笔记_第3张图片

这里我们注意到这么几个函数比较有意思

第一是CreateMutexW,这个函数会创建了一个互斥量,然后就是CreateThread,这个会创建一个线程,然后就是DeleteFileW,这个删除一个文件,然后就是我们内核分析时候见过的DeviceIoControl这个,用于给内核中的驱动发送一个信号的函数

下面的导出函数也证明了这个函数可能会操纵MutexThread

恶意代码分析实战 Lab 11-1 习题笔记_第4张图片

这里我们发现了MoveFileWOpenMutexW这个函数

恶意代码分析实战 Lab 11-1 习题笔记_第5张图片

这里还有一个SizeofResource这个函数,说明这个函数对资源节会有一些操作,然后我们还看见了ls*的一堆对字符串进行操作的函数

我们还可以在ADVAPI32.DLL下面发现这么两个DLL

图片

其中一个是WINTRUST.DLL,另一个是SECUR32.DLL,其中第一个DLL的作用是

wintrust.dll是 DLL文件信息,用于验证第三方应用程序的文件,目录,内存使用,数据签名等

全名是Microsoft Trust Verification APIs

而下面的SECUR32.DLL全名是Security Support Provider Interface,这是一个提供了各种安全支持提供程序的接口调用库

然后我们可以看看这个程序的字符串有什么

恶意代码分析实战 Lab 11-1 习题笔记_第6张图片

我们可以发现这个GinaDLLmsgina32.dll字符串中的显示,然后我们开始看资源节的内容

恶意代码分析实战 Lab 11-1 习题笔记_第7张图片

我们可以看到这个.rsrc节中,有个叫BINARY TGAD 0000的二进制文件,我们点开就可以发现那个This program cannot be run in DOS mode这个字符串,这表明这个二进制是个可执行程序

进行完这些基本的静态分析,我们开始使用动态分析,还是先设置DNSfakeWebfake,之后使用procmon设置过滤器来看程序的操作

这里书上有个错误,中文版的书上写的是

设置Lab11-02.exe

中文版我没有PDF,所以这里截个英文版的

恶意代码分析实战 Lab 11-1 习题笔记_第8张图片

这里对照了一下英文版的,是Lab11-01.exe,这里应该是译者的翻译错误

这里在DNS中病未看见程序查找了什么域名,但是注意这不代表这个程序不会进行网络连接,如果有奇葩的病毒直接用ip来访问的话,是不会进行任何的DNS查询的

然后我们再设置一个过滤条件为CreateFile这个东西,之后我们就可以看见

恶意代码分析实战 Lab 11-1 习题笔记_第9张图片

这里我们看见在本地的目录下创建了一个msgina32.dll这个动态链接库

恶意代码分析实战 Lab 11-1 习题笔记_第10张图片

这里我们看见创建了一个msgina32.dll文件

然后我们回到时间顺序,然后看

图片

这里我们可以看到这里调用了一个RegCreateKey然后创建了一个HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon

然后通过函数RegSetValue来设置了一个值为HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GinaDLL

恶意代码分析实战 Lab 11-1 习题笔记_第11张图片

而对这个msgina32.dll的操作只有如下的几个操作

恶意代码分析实战 Lab 11-1 习题笔记_第12张图片

只有一个CreateFile之后就是WriteFile,然后就CloseFile

我们可以还可以发现这么两个调用CreateFile的参数的比较有意思,其中一个是conime.exe,还有一个是apphelp.dll,其中conime.exe是控制输入法的程序,apphelp.dll是维护程序运行时候的一些错误的修正补丁

现在我们将资源节里面的二进制导出来看看和这个msgina32.dll是不是一样的

恶意代码分析实战 Lab 11-1 习题笔记_第13张图片

这个Out.dll是我们从资源节导出来的文件

恶意代码分析实战 Lab 11-1 习题笔记_第14张图片

我们可以看到这两个dll大小是一样的,当然,这样比较是比较肤浅的,比较严谨的比较方式是计算其MD5SHA1的值,我们先在也计算一下

这里我们介绍的是在Linux下面计算的方法,Windows的有专门的软件,或者用在线的工具

我这里有台CentOS 7的机器用做ftp,直接上传然后计算就好了,因为在Linux里面自带了计算MD5的工具

恶意代码分析实战 Lab 11-1 习题笔记_第15张图片

然后就计算MD5

恶意代码分析实战 Lab 11-1 习题笔记_第16张图片

可以看到我们的两个文件的MD5值是一样的

然后我们计算SHA1的值

恶意代码分析实战 Lab 11-1 习题笔记_第17张图片

都是一样的

所以我们就可以确定这个在资源节的二进制文件就是msgina32.dll这个文件

接下来还是一样的,我们打开IDA来分析这个Lab11-01.exe,一样的,我们每次遇到call就做一段分析

恶意代码分析实战 Lab 11-1 习题笔记_第18张图片

然后我们遇到的第一个callGetModuleHandleA,这个函数在MSDN中定义是

检索指定模块的模块句柄。 该模块必须已由调用进程加载

然后我们可以看到这个函数的入参是0也就是NULL

如果此参数为NULL,则GetModuleHandle将返回用于创建调用进程(.exe文件)的文件的句柄

这句话的意思就是,如果这个入参是0,那么这个函数返回的是这个函数的本身的句柄

所以我们这里的返回值是指向这个exe文件的句柄,然后我们开始看下一个call

恶意代码分析实战 Lab 11-1 习题笔记_第19张图片

我们可以看到这里压入了一个参数eax,而这个eax虽然经过上面那么多的变换,还是GetModuleHandleA的返回值

进来这个函数之后,我们可以看到如下的初始化操作

恶意代码分析实战 Lab 11-1 习题笔记_第20张图片

注意这里的cmp指令,这里的指令让[ebp+hModule]0进行了比较,我们注意这里的hModule的偏移值是dword ptr 8,也就是最后这个的值是[ebp+8],根据我们以前的栈分析,ebp+8的值代表了调用函数的最后一个入参,如果只有一个入参的话,就代表了那个入参的值

当然你这里也可以不用像我们这样分析栈的组成,因为IDA已经给你标出来了这个值是hModule

OK,言归正传之后,我们开始分析这段代码

汇编代码到push edi之前的代码都是在初始化栈

之后的代码

mov [ebp+hResInfo], 0
mov [ebp+hResData], 0
mov [ebp+var_8], 0
mov [ebp+dwSize], 0
mov [ebp+var_C], 0
cmp [ebp+hModule], 0
jnz shrt loc_4010B8

前面的mov指令都是在为参数赋值,这里将hResInfo等等的参数都赋值为了0

然后下面就是比较hModule的值是否为0,如果函数调用成功的话,返回的是一个指针肯定不等于0,如果函数调用失败,就返回NULL也就是0

如果这个返回的指针为0,则cmp指令之后,ZF=1jnz不跳转,执行红色的线

如果返回指针不为0cmp之后,ZF=0jnz跳转,走绿色的线

绿色的线就是

恶意代码分析实战 Lab 11-1 习题笔记_第21张图片

下面就会执行这个FindResourceA函数,而这个函数在MSDN中的定义是

确定指定模块中具有指定类型和名称的资源的位置

这里我们注意这个函数的入参,通过查看就可以得出如下的入参表

恶意代码分析实战 Lab 11-1 习题笔记_第22张图片

HRSRC WINAPI FindResource(
  _In_opt_ HMODULE hModule = [ebp+hModule],
  _In_     LPCTSTR lpName = TGAD,
  _In_     LPCTSTR lpType = BINARY
);

这个函数的意思就是将在资源节中名字叫TGAD,类型是BINARY的节找出来,函数执行成功之后,将返回一个指定资源的信息块的句柄

恶意代码分析实战 Lab 11-1 习题笔记_第23张图片

下一个函数是LoadResource这个函数,在MSDN中的定义是

检索可用于获取指向内存中指定资源的第一个字节的指针的句柄

这个函数调用成功之后,会返回

如果函数成功,则返回值是与资源关联的数据的句柄

这个返回值最后会存在hResData里面

之后下一个调用是LockResource

恶意代码分析实战 Lab 11-1 习题笔记_第24张图片

这个函数在MSDN里面的意思就是

检索指向内存中指定资源的指针

这个函数的返回值如下

如果加载的资源可用,则返回值是指向资源第一个字节的指针;否则,它是NULL

这就会把指针开始指向了资源的第一个字节,从这个时候开始才开始操作资源节的内容

然后下一个代码块

恶意代码分析实战 Lab 11-1 习题笔记_第25张图片

这个函数会返回资源节的大小,还是一样的做了一个结果比较跳转

恶意代码分析实战 Lab 11-1 习题笔记_第26张图片

之后调用VirtualAlloc分配一个空间给要导出的DLL

其中,这个函数在MSDN中的定义如下

LPVOID WINAPI VirtualAlloc(
  _In_opt_ LPVOID lpAddress = 0,
  _In_     SIZE_T dwSize = dwSize,
  _In_     DWORD  flAllocationType = 1000h,
  _In_     DWORD  flProtect = 4
);

其中需要我们注意的是flAlloctaionType这个参数,这个参数的值为1000hMSDN里面代表了MEM_COMMIT

恶意代码分析实战 Lab 11-1 习题笔记_第27张图片

意思是

为指定的保留内存页分配内存费用(从内存的总大小和磁盘上的分页文件)。 该函数还保证当调用者最初访问内存时,内容将为零。 除非实际访问虚拟地址,否则不会分配实际的物理页面。

要一步保留并提交页面,请使用MEM_COMMIT |调用VirtualAllocMEM_RESERVE。

尝试通过指定MEM_COMMIT而不使用MEM_RESERVE来提交特定地址范围,除非整个范围已被保留,否则非空lpAddress将失败。 由此产生的错误代码是ERROR_INVALID_ADDRESS。

尝试提交已提交的页面不会导致函数失败。 这意味着您可以在不首先确定每个页面的当前承诺状态的情况下提交页面。

如果lpAddress指定飞地内的地址,则flAllocationType必须是MEM_COMMIT

这一步就是分配了一个空间给将来要释放出来的dll

恶意代码分析实战 Lab 11-1 习题笔记_第28张图片

之后的代码,我们可以看到这里调用了_fopen函数,并且这个函数的modewb,也就是二进制写入,名字是msgina32.dll

之后用_fwrite函数将指向资源节的数据写入了这个_fopen打开的文件里面,之后就调用了_fclose关闭句柄,之后还有一个函数叫sub_401299,起始这个是printf函数,以前我们分析过为什么是printf函数

这里就是调用printf函数输出了一个DR\n

恶意代码分析实战 Lab 11-1 习题笔记_第29张图片

之后用FreeResource来释放这个操作Resource的指针

之后我们看这个sub_401000函数的操作

恶意代码分析实战 Lab 11-1 习题笔记_第30张图片

这是一个在整个函数底部的函数

恶意代码分析实战 Lab 11-1 习题笔记_第31张图片

进入这个函数之后我们可以看见,第一个函数调用是RegCreateKeyExA这个函数,我们可以得到这个函数在MSDN中的定义

LONG WINAPI RegCreateKeyEx(
  _In_       HKEY                  hKey = 80000002h,
  _In_       LPCTSTR               lpSubKey = SubKey,
  _Reserved_ DWORD                 Reserved = 0,
  _In_opt_   LPTSTR                lpClass = 0,
  _In_       DWORD                 dwOptions = 0,
  _In_       REGSAM                samDesired = 0F003Fh,
  _In_opt_   LPSECURITY_ATTRIBUTES lpSecurityAttributes = 0,
  _Out_      PHKEY                 phkResult phkResult,
  _Out_opt_  LPDWORD               lpdwDisposition = 0
);

其中hKey的意思是A handle to an open registry key,也就是一个一个开放注册表项的句柄

lpSubKey的意思此函数打开或创建的子项的名称

这里我们大概知道这个函数会创建一个叫

SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon

的键,这个键按照字面意思winlogon理解就是登录,这个在MSDN中的解释是

Windows操作系统的一部分,提供交互式登录支持。
Winlogon是围绕交互式登录模型设计的,该模型由三部分组成:Winlogon可执行文件,图形标识和认证动态链接库(DLL)(称为GINA)以及任意数量的网络提供程序。

恶意代码分析实战 Lab 11-1 习题笔记_第32张图片

下一个调用的函数是RegSetValueExA这个函数,这个函数我们注意这个ValueNamelpData的值,一个是键的名字,一个是键的值

我们已经运行过了这个代码,所以我们现在可以打开注册表来查看这个位置的值

恶意代码分析实战 Lab 11-1 习题笔记_第33张图片

可以看见,我们这里的的GinaDLL的值被设置成了msgina32.dll的绝对路径,不管你在吧这个Lab11-01.exe放哪里,都可以将对应的msgina32.dll的绝对路径写入键值中

恶意代码分析实战 Lab 11-1 习题笔记_第34张图片

之后,函数就关闭句柄,然后就退出了

下面我们进行的msgina32.dll的分析

恶意代码分析实战 Lab 11-1 习题笔记_第35张图片

我们打开这个msgina32.dll文件,可以看到这样的结构

我们查看字符串

恶意代码分析实战 Lab 11-1 习题笔记_第36张图片

这里我们可以看到许多的函数名,还有一些DLL的名称

在这里我们并没有看见书中说的那些字符串的位置,然后我们进入高级静态分析的过程

先找到DLL的开始的地方DllMain这个地方,可以看到如下的样子

恶意代码分析实战 Lab 11-1 习题笔记_第37张图片

这里从外部传入了一个fdwReason参数,这个fdwReason参数的意义是

reason code说明了为什么调用DLL入口函数

而这个是正DllMain的结构函数

BOOL WINAPI DllMain(
  _In_ HINSTANCE hinstDLL,
  _In_ DWORD     fdwReason,
  _In_ LPVOID    lpvReserved
);

这里将fdwReason1进行了比较,之后跳转,这里我们要明确一下这个1代表什么意义在DllMain

恶意代码分析实战 Lab 11-1 习题笔记_第38张图片

这里我们可以看到这个1代表的意思是DLL_PROCESS_ATTACH,而它的解释翻译过来是这样的

由于进程启动或由于调用LoadLibrary,DLL正被加载到当前进程的虚拟地址空间中。 DLL可以使用此机会初始化任何实例数据或使用TlsAlloc函数分配线程本地存储(TLS)索引。

看不懂,大概意思是这个DLL会被调用程序加载进调用程序的虚拟地址空间之中,这也就是ATTACH的意思,想起ODATTACH功能了不?

然后就是一个jnz跳转,我们考虑一下这里的jnz会在什么情况之下跳转

cmp eax, 1

之后,如果eax也就是fdwReason的值是1的话,cmp之后,ZF=1,之后jnz不会跳转,而如果eax的值不是1的话,ZF=0,之后jnz跳转,跳转之后执行的是函数清理和退出

所以这里的代码期望的值是1,这样就可以继续执行如下的代码

恶意代码分析实战 Lab 11-1 习题笔记_第39张图片

这里执行的第一个函数是DisableThreadLibraryCalls,这个函数的解释是

为指定的动态链接库(DLL)禁用DLL_THREAD_ATTACH和DLL_THREAD_DETACH通知,这可以减少某些应用程序的工作集大小

之后下一个调用是GetSystemDirectoryW,这个函数的解释是

检索系统目录的路径,系统目录包含系统文件,如动态链接库和驱动程序

此功能主要是为了兼容性而提供的。应用程序应将代码存储在Program Files文件夹中,并将持久数据存储在用户配置文件的Application Data文件夹中

下一个调用函数lstrcatW,这个函数熟悉的C语言的同学应该非常熟悉了

追加一个字符串到另一个

这个函数会将两个字符串拼接在一起

LPTSTR WINAPI lstrcat(
  _Inout_ LPTSTR lpString1 = ecx,
  _In_    LPTSTR lpString2 = "\\MSGina"
);

这里我们可以看到,第一个字符串的值是ecx,而要拼接在ecx之后的字符串是\\MSGina,而我们也可以知道,这个ecx的值是GetSystemDirectoryW的返回值

MSDN中,这样解释lpBuffer

指向接收路径的缓冲区的指针,除非系统目录是根目录,否则此路径不会以反斜杠结尾
例如,如果系统目录在驱动器C上名为Windows\System32,则此函数检索到的系统目录的路径为C:\Windows\System32

所以我们可以预测这个ecx的值是C:\Windows\System32,之后,和字符串\\MSGina拼接之后,就成了

C:\Windows\System32\MSGina

下一个要调用的函数是LoadLibraryW,这个函数在MSDN中的解释是

将指定的模块加载到调用进程的地址空间中,指定的模块可能会导致其他模块被加载

恶意代码分析实战 Lab 11-1 习题笔记_第40张图片

这里我们可以分析得出,这个[esp+20Ch+Buffer]其实就是

C:\Windows\System32\MSGina

所以这里的的LoadLibraryW其实要加载的DLL就是上面那个路径上的MSGina,这里需要我们注意一下,这个路径用LoadLibraryW调用之后,会加载的DLL其实是C:\Windows\System32\msgina.dll

恶意代码分析实战 Lab 11-1 习题笔记_第41张图片

这里稍微解释一下这个原因

MSDN中对于LoadLibraryWlpFileName的原文解释就是

The name of the module. This can be either a library module (a .dll file) or an executable module (an .exe file). The name specified is the file name of the module and is not related to the name stored in the library module itself, as specified by the LIBRARY keyword in the module-definition (.def) file.


If the string specifies a full path, the function searches only that path for the module.


If the string specifies a relative path or a module name without a path, the function uses a standard search strategy to find the module; for more information, see the Remarks.


If the function cannot find the module, the function fails. When specifying a path, be sure to use backslashes (), not forward slashes (/). For more information about paths, see Naming a File or Directory.


If the string specifies a module name without a path and the file name extension is omitted, the function appends the default library extension .dll to the module name. To prevent the function from appending .dll to the module name, include a trailing point character (.) in the module name string.

这里对我们理解问题有用的是最后一段

If the string specifies a module name without a path and the file name extension is omitted, the function appends the default library extension .dll to the module name. To prevent the function from appending .dll to the module name, include a trailing point character (.) in the module name string.’

这段翻译过来就是

如果字符串指定一个没有路径的模块名称,并且省略了文件扩展名,则该函数会将缺省库扩展名.dll附加到模块名称中。为防止函数将.dll附加到模块名称中,请在模块名称字符串中包含尾部字符(.)

在这里LoadLibraryW函数会认为字符串C:\Windows\System32\MSGina是个省略了文件后缀名的字符串,程序会自动补全,也就是会去加载

C:\Windows\System32\MSGina.dll

上的DLL,这个地址上的也就是C:\Windows\System32\msgina.dll

所以这里为什么没有后缀名也会去加载msgina.dll,原因就是这样

之后函数就是做了清理工作就退出了

书上说

msgina.dll是实现GINA的Windows DLL,然而msgina32.dll是拦截GINA的恶意DLL程序。设计成msgina.dll名字的目的显然是为了欺骗分析人员

之后这个代码会将打开的msgina.dll 的句柄保存在hLibModule中,这个变量是个全局变量,使用这个变量msgina32.dll会在合适的时候调用msgina.dll中的函数,但是这个函数明显是被msgina32.dll劫持了,这样可以保持系统的正常运行

到这里为止,我们已经完成了DllMain的分析,之后我们分析这个dll的导出函数

这些导出函数我们可以在这里找到

恶意代码分析实战 Lab 11-1 习题笔记_第42张图片

这里我们可以随便点一个函数,比如第一个函数

恶意代码分析实战 Lab 11-1 习题笔记_第43张图片

之后我们可以看到这个hLibModule,佐证了这个变量是全局变量

恶意代码分析实战 Lab 11-1 习题笔记_第44张图片

我们还是按照书上的步骤来走,先来分析一下这个WlxLoggedOnSAS函数的代码

恶意代码分析实战 Lab 11-1 习题笔记_第45张图片

恶意代码分析实战 Lab 11-1 习题笔记_第46张图片

这个函数打开之后就可以看到这些代码

这个函数压栈了一个参数WlxLoggedOnSAS,然后调用了sub_10001000,很巧的是,这个函数就是我们刚刚随便点的那个函数

恶意代码分析实战 Lab 11-1 习题笔记_第47张图片

这里函数会用全局变量hLibModule这个打开的msgina.dll的句柄来赋值给eax

然后下一个函数调用是GetProcAddress,这个函数的定义就是

从指定的动态链接库(DLL)中检索导出的函数或变量的地址

而这个要检索的DLL就是msgina.dll,这个是由eax来决定的,而eax刚刚才被赋值为hLibModule

而这个lpProcName其实就是刚刚上面压栈的那个字符串WlxLoggedOnSAS,下面我们来分析一下这个

执行完如下代码

push offset aWlxLoggedons_0

之后,栈空间如下

 ----------------
| WlxLoggedOnSAS | <--- esp
 ----------------
|   ...          |
 ----------------
 .
 .
 .
 ----------------
|   ...          | <--- ebp
 ----------------

进入调用函数之后,会第一时间push一个返回地址入栈,这个操作在代码上是不会显示的,但是却是实实在在操作了的

 ----------------
| retuen address | <--- esp
 ----------------
| WlxLoggedOnSAS |
 ----------------
|   ...          |
 ----------------
 .
 .
 .
 ----------------
|   ...          | <--- ebp
 ----------------
sub esp, 10h
 ----------------
|   ...          |-- <--- esp
 ----------------   |
|   ...          |  |
 ----------------   |---> 0x10h = 16d
|   ...          |  |
 ----------------   |
|   ...          |--
 ----------------
| retuen address |
 ----------------
| WlxLoggedOnSAS |
 ----------------
|   ...          |
 ----------------
 .
 .
 .
 ----------------
|   ...          | <--- ebp
 ----------------

之后就是下面这句操作

push esi
 ----------------
|   esi          | <--- esp
 ----------------
|   ...          |--
 ----------------   |
|   ...          |  |
 ----------------   |---> 0x10h = 16d
|   ...          |  |
 ----------------   |
|   ...          |--
 ----------------
| retuen address |
 ----------------
| WlxLoggedOnSAS |
 ----------------
|   ...          |
 ----------------
 .
 .
 .
 ----------------
|   ...          | <--- ebp
 ----------------

之后的指令操作,注意这个指令比较关键

mov esi, [esp+14h+lpProcName]

lpProcName在函数的开头就标注了值是4,但是注意一下这里标注的数据类型是dword ptr类型

这里有两个类型,一个是byte ptr -10h,就是上面那个var_10,一个是dword ptr的类型,这两个类型是完全不同的两个数据类型

其中byte是一个字节的数据,而dword是两个字节的数据

图片

我们推导一下esp+18h是哪里

从上图简略的栈图中,我们可以看出,存储esi的空间占了四个地址,加上刚刚申请的0x10h个字节,就是0x14h个字节的空间

于是我们得到esp+18h的值

 ----------------
|   esi          | <--- esp
 ----------------
|   ...          | <--- esp+4h
 ----------------
|   ...          | <--- esp+8h
 ----------------
|   ...          | <--- esp+ch
 ----------------
|   ...          | <--- esp+10h
 ----------------
| retuen address | <--- esp+14h
 ----------------
| WlxLoggedOnSAS | <--- esp+18h
 ----------------
|   ...          |
 ----------------
 .
 .
 .
 ----------------
|   ...          | <--- ebp
 ----------------
mov esi, [esp+18h]

于是我们可以看出,这里的esi最后会指向WlxLoggedOnSAS字符串的上一个数据

(PS:这里我认为是作者写恶意代码的时候的一个错误或者什么的,反正我现在只能分析得出这些结论,这些问题我已经反馈给他们的出版社的老大了,不知道会不会回我 2018-4-16)

(2018-4-23)查明是因为函数调用后第一时间会push一个返回地址进去,但是这个操作不会体现在汇编代码中

这样,而不是想移动,再赋值,所以esp指向的地方永远是空值

所以这里的函数会在用GetProcAddress来在真正的msgina.dll中找到函数WlxLoggedOnSAS的地址,之后

之后如果函数调用成功,会做一个判断跳转,这里依旧是假设我们的函数调用成功

就会执行下面这些函数

恶意代码分析实战 Lab 11-1 习题笔记_第48张图片

这里的第一个指令是

mov ecx, esi

之后做的操作是

shr ecx, 10h

这句指令的意思是将ecx指向的那个字符串的指针的值置为0esi的值并未改变

因为shr是逻辑右,左边用0补齐,所以移动0x10h之后,整个指针的16bit都为了0

因为最后的结果为0,所以ZF=1,则JNZ不会跳转,最后会按照红线的路线执行

恶意代码分析实战 Lab 11-1 习题笔记_第49张图片

这里会打印一个字符串,这里的var_10=-10,最后其实寻找的地址是

[esp+8h]

这里我们就不去推理这个地址上是什么了

然后我们注意这里的eax,自从调用了GetProcAddress之后,就一直未改变过

恶意代码分析实战 Lab 11-1 习题笔记_第50张图片

这里可以看到,最后一次出现eax

test eax, eax

这句代码,之后就再也没改变过eax的值,这里的eax的值代表了WlxLoggedOnSASmsgina.dll中的地址,真实的那个WlxLoggedOnSAS的地址,之后

恶意代码分析实战 Lab 11-1 习题笔记_第51张图片

函数调用完sub_10001000之后就会跳转到eax的位置,这个位置就是WlxLoggedOnSAS的真实地址,也就是这里只是劫持了WlxLoggedOnSAS函数,然后执行完恶意程序作者的自己的代码sub_10001000之后就又会跳转到真实的WlxLoggedOnSAS函数去执行

这样保证了系统的正常运行

下面书上的说法是

如果我们继续分析其他导出函数,将看到大部分于WlxLoggedOnSAS(它们是中转函数)中的类似操作,然而WlxLoggedOutSAS例外,它包含了一些额外的代码(当系统注销时调用WlxLoggedOutSAS)

下面我就缩短分析的步骤,直接去分析这个WlxLoggedOutSAS函数

恶意代码分析实战 Lab 11-1 习题笔记_第52张图片

上图是这个函数第一部分

我们可以看到这个函数的第一个调用是sub_10001000,但是这次传入的参数是WlxLoggedOutSAS这个字符串

我们可以知道,这个函数传入一个字符串之后,就会调用GetProcAddress来查找这个函数在真正的msgina.dll中的地址

图片

这里有个IDA中无法注释的函数

我们打开看看会发现

恶意代码分析实战 Lab 11-1 习题笔记_第53张图片

这个调用的函数是在MSVCPRT中的函数,用IDA可能还不好看,可以用OD打开看看

恶意代码分析实战 Lab 11-1 习题笔记_第54张图片

可以看到,这个有点像乱码的函数和fclosefwprintf都是出自MSVCRT

下一个调用是

恶意代码分析实战 Lab 11-1 习题笔记_第55张图片

这里的edi我们可以看看是从哪里来的参数

恶意代码分析实战 Lab 11-1 习题笔记_第56张图片

这里的edi我们可以从代码就看出来,就是eax,而eax是调用sub_10001000之后的的返回值,也就是函数WlxLoggedOutSAS的真实地址

所以这个代码会在这里去调用真实的WlxLoggedOutSAS函数,但是这个函数的入参我们需要注意一下,分析图中的各种movpush操作我们可以得到下面的参数表

int WlxLoggedOutSAS(
  _In_    PVOID                pWlxContext = [esp+1Ch+arg_0] = [esp+20h],
  _In_    DWORD                dwSasType = [esp+18h+arg_4] = [esp+20h],
  _Out_   PLUID                pAuthenticationId = [esp+14h+arg_8] = [esp+20h],
  _Inout_ PSID                 pLogonSid = [esp+0Ch+arg_C] = [esp+1ch],
  _Out_   PDWORD               pdwOptions = [esp+0Ch+arg_10] = [esp+20h],
  _Out_   PHANDLE              phToken = [esp+0Ch+arg_14] = [esp+24h],
  _Out_   PWLX_MPR_NOTIFY_INFO pNprNotifyInfo = [esp+0Ch+arg_18] = [esp+28h],
  _Out_   PVOID                *pProfile = [esp+0Ch+arg_1C] = [esp+2ch]
);

这里就直接会调用在真正msgina.dll中的函数

调用之后就会执行下面的函数

恶意代码分析实战 Lab 11-1 习题笔记_第57张图片

这里调用了一个函数sub_10001570,这个函数我们可以看看是什么

这里入栈的参数有一个eaxaUnSDmSpwSOlds这个东西

恶意代码分析实战 Lab 11-1 习题笔记_第58张图片

函数的开始,第一个call是调用了_vsnwprintf这个函数,这个函数使用是这样的

int _vsnprintf(char *buffer, size_t max_count, const char *format, va_list vArgList);

这里的各个参数之间是作用是这样的

1. char *buffer [out],把生成的格式化的字符串存放在这里.
2. size_t max_count [in], buffer可接受的最大字节数,防止产生数组越界.
3. const char *format [in], 格式化字符串
4. va_list vArgList [in], va_list变量. va:variable-argument:可变参数

对照我们的汇编代码,我们可以得出下面结论

buffer = edx = [esp+54h]
max_count = 800h
format = "UN %s DM %s PW %s OLD %s"
vArgList = eax [esp+860h]

这里需要注意的是[esp+54h][esp+860h]均是指向sub_10001570函数调用之前栈空间,这里我们就没法溯源这些数据到底是什么了

之后函数会调用_wfopen,这个函数会打开一个指向文件的文件句柄

FILE *_wfopen(   
   const wchar_t *filename,  
   const wchar_t *mode   
);  

这里的两个入参是这样的

filename = msutil32.sys
mode = 61h

我们查一下这个sys是什么,查不到,查出来的都是这个书的解题答案的网站

书上说或

_vsnwprintf 调用填充了传入WlxLoggedOutSAS导出函数的格式化字符串

这个字符串的格式是这样的

UN %s DM %s PW %s OLD %s

这里我们可以猜测这个UN可能是UserName的简写

PW可能是PassWord的简写

根据这个对应关系,我们回到调用这个函数之前的地方,可以得出一下结论

恶意代码分析实战 Lab 11-1 习题笔记_第59张图片

这里的eax对应的应该就是用户的UserName,而[esi+4]则对应了DM[esi+8]对应了PassWord,最后的[esi+0ch]对应了我们的OLD

我们溯源回去可以发现,eax其实就是调用真正的WlxLoggedOutSAS的一个参数,在[esp+0Ch+arg_18]

我们画出函数调用sub_10001570时候的栈图

 --------------
|      0       | <--- esp
 --------------
|aUnSDmSPwSOlds|
 --------------
|      UN      |
 --------------
|      DM      |
 --------------
|      PW      |
 --------------
|      OLD     |
 --------------
|      ---     |
 --------------

之后我们进入了函数之后,第一时间会压栈返回地址

 --------------
|return address| <--- esp
 --------------
|      0       |
 --------------
|aUnSDmSPwSOlds|
 --------------
|      UN      |
 --------------
|      DM      |
 --------------
|      PW      |
 --------------
|      OLD     |
 --------------
|      ---     |
 --------------

所以这里我们可以看出,赋值给ecx[esp+8]其实就是那个格式化字符串的地址aUnSDmSPwSOLDs

然后,会有一个sub操作来扩大栈

恶意代码分析实战 Lab 11-1 习题笔记_第60张图片

 --------------
|              | --- <--- esp
 --------------     |
       .            |
       .            | ---> 0x854h
       .            |
 --------------     |
|return address| ---
 --------------
|      0       |
 --------------
|aUnSDmSPwSOlds|
 --------------
|      UN      |
 --------------
|      DM      |
 --------------
|      PW      |
 --------------
|      OLD     |
 --------------
|      ---     |
 --------------

之后的eax其实就是UN的地址

但是我们这里注意这个格式化字符串,需要4个入参,于是函数会自动往后依次去四个数据,也就是把那四个缩写都取了进去

由于我们这里不知道具体变量UN代表了什么值,所以这里我们用小写来代替它的值

于是我们可以这样写这个调用函数

_vsnwprintf(esp+54, 800h, "UN %s DM %s PW %s OLD %s", un, dm, pw, old)

我们可以看出,这里是将变量都格式化到了字符中,然后保存在esp+54的地址上

我们理清这个栈关系之后,我们就可以继续下面的分析

恶意代码分析实战 Lab 11-1 习题笔记_第61张图片

这个函数_wstrtime的作用是

将时间复制到缓冲区

这里的Dest其实就是我们刚刚格式化字符串的那个缓冲区的地址,但是这里没用到

_wstrtime的入参只需要一个,这里我们记住Buffer这里存储的是时间字符串

图片

下一个调用函数是_wstrdate其实就是获得日期的函数

之后

图片

之后函数就会调用fwprintf函数来格式化一个字符串

这里是定义

int fwprintf(
   FILE *stream,
   const wchar_t *format [,
   argument ]...
);

这里的esi我们回头去看其实就是打开msutil32.sys的那个句柄

这里的eax则是上面的上面那个格式化字符串操作之后保存结果的地址

我们这里再截一个图来看整体的代码

恶意代码分析实战 Lab 11-1 习题笔记_第62张图片

这里分析的时候要注意的是这里的三个参数不是一次性push入栈的,是分了三次

最后一个pusheax代表了_wstrdate的返回值

而倒数第二个的eax代表了_wstrtime的返回值

倒数第三个的eax代表了我们前面格式化字符串后的那个字符串

我们可以写出伪代码的样子就是这样

fwprintf(esi, "%s %s - %s ", date, time, "UN un DM dm PW pw OLD old")

于是这个程序会把这些信息写入了msutil32.sys

如果我们重启机器,然后就可以在msutil32.sys中发现我们的用户和密码

我们现在验证一下~

我们在

C:\WINDOWS\system32\

中可以找到这个文件

恶意代码分析实战 Lab 11-1 习题笔记_第63张图片

然后我们打开看看

恶意代码分析实战 Lab 11-1 习题笔记_第64张图片

我们可以发现,我们的信息已经被记录下来了,我这里用的是空密码,所以PW后面是个空值

病毒分析到这里基本我们就清除他的运作方式了

于是我们可以来回答这个问题也顺便回顾一下这个病毒

解答:这个恶意代码会从名叫TGAD的资源节提取一个文件并命名为msgina32.dll


2. 这个恶意代码如何进行驻留?

解答:这个恶意代码会在注册表中添加一个键值来安装这个DLL

恶意代码分析实战 Lab 11-1 习题笔记_第65张图片

系统重启之后,依旧会加载这个DLL


3. 这个恶意代码如何窃取用户登录凭证

解答:恶意代码用GINA机制来拦截用户的登录凭证,msgina32.dll会拦截所有提交到系统认证的用户登录凭证


4.这个恶意代码对窃取的凭证做了什么处理?

解答:这个恶意代码会将凭证保存在C:\WINDOWS\system32\msutil32.dll

恶意代码分析实战 Lab 11-1 习题笔记_第66张图片


5.如何在你的测试环境让这个恶意代码获得用户登录凭证?

解答:重启系统


本文完

你可能感兴趣的:(安全)