恶意代码分析———分析恶意Windows-API

多数恶意代码以Windows平台为目标,并且与操作系统进行紧密交互。对基本Windoows编程概念的深刻理解会帮助你识别出恶意代码在主机上的感染迹象,跟踪恶意代码的执行(因为它们可能使用操作系统来执行代码而不是用一个跳转或调用指令),并最终分析出恶意代码的目的。

Windows API

Windows API是一个广泛的功能集合,管理着恶意代码与微软程序库之间的交互方式。Windows API使用特定术语,名字,已经约定。

类型和匈牙利表达法

多数Windows API使用它自己的 名字,来表示C语言类型。Windows总体上使用匈牙利表达法,作为API函数标识符,这个表达式 使用一个前缀命名模式
windows API中的常见类型

1
2
3
4
5
6
类型和前缀 描述
WORD(w) 一个16位的无符号数值
DWORD(dw) 一个双字节,32位的无符号数值
Handles(H) 一个对象索引
Long Pointer(L) 一个指向另一类型的指针
Callback 表示一个将会被Windows API调用的函数

句柄(HANDLE)

句柄是在操作系统中被打开或被创建的项。你对句柄做的唯一的事情,就是保存它,并在后续函数调用中使用它来引用同一对象。
例:CreateWindowEx函数是一个句柄的简单例子。他返回一个HWND,这是一个窗口句柄。任何时候当你想要对那个窗口做些什么,比如调用DestroyWindows函数是,你就需要使用这个窗口。

文件系统函数

恶意代码与系统交互的一个最常用的方式就是创建或修改文件,而且独特文件名或修改为既有的文件名是明显的基于主机的感染现象。

CreateFile

这个函数被用来创建和打开文件。它可以打开已经存在的文件,管道,流,以及I/O设备。还能创建新的文件。
函数结构体

1
2
3
4
5
6
7
8
9
HANDLE CreateFile(
LPCTSTR,lpFileName, //指向文件名的指针
DWORD dwDesiredAccess, //访问模式(读/写)
DWORD dwShareMode, //共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes,//指向安全属性的指针
DWORD dwCreationDisposition, //如何让创建
DWORD dwFlagAndAttributes, //文件属性
HANDLE hTemplateFile //用于复制文件句柄
);

ReadFile和WriteFile

这两个函数用来对文件进行读写
ReadFile函数结构体

1
2
3
4
5
6
7
8
9
BOOL ReadFile(
HANDLE hFile, //文件的句柄
LPVOID lpBuffer, //用于保存读入数据的一个缓冲区
DWORD nNumberOfBytesToRead, //要读入的字节数
LPDWORD lpNumberOfBytesRead, //指向实际读取字节数的指针
LPOVERLAPPED lpOverlapped
//如文件打开时指定了FILE_FLAG_OVERLAPPED,那么必须,用这个参数引用一个特殊的结构。
//该结构定义了一次异步读取操作。否则,应将这个参数设为NULL
);

WriteFile函数结构体

1
2
3
4
5
6
7
BOOL WriteFile(
HANDLE hFile,//文件句柄
LPCVOID lpBuffer,//数据缓存区指针
DWORD nNumberOfBytesToWrite,//你要写的字节数
LPDWORD lpNumberOfBytesWritten,//用于保存实际写入字节数的存储区域的指针
LPOVERLAPPED lpOverlapped//OVERLAPPED结构体指针
);

CreateFileMapping和MapViewofFile

文件映射经常被恶意代码作者使用,因为它们允许讲一个文件加载到内存中,以便更加容易地进行操作。CreateFileMapping函数负责从磁盘上加载一个文件到内存。MapViewofFile函数则返回一个指向映射的基地址指针,它可以被用来访问内存中的文件。
CreateFileMapping函数结构体

1
2
3
4
5
6
7
8
HANDLE WINAPI CreateFileMapping(
_In_HANDLE hFile,
_In_opt_LPSECURITY_ATTRIBUTES lpAttributes,
_In_DWORD flProtect,
_In_DWORD dwMaximumSizeHigh,
_In_DWORD dwMaximumSizeLow,
_In_opt_LPCTSTR lpName
);

MapViewofFile函数结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
LPVOID WINAPI MapViewOfFile(
  __in HANDLE hFileMappingObject,
  __in DWORD dwDesiredAccess,
  __in DWORD dwFileOffsetHigh,
  __in DWORD dwFileOffsetLow,
  __in SIZE_T dwNumberOfBytesToMap
  );
LPVOID WINAPI MapViewOfFileEx(
  __in HANDLE hFileMappingObject,
  __in DWORD dwDesiredAccess,
  __in DWORD dwFileOffsetHigh,
  __in DWORD dwFileOffsetLow,
  __in SIZE_T dwNumberOfBytesToMap,
  __in LPVOID lpBaseAddress
  );

特殊文件

Windows系统中有一些特殊文件类型,它们的访问方式与普通文件不太一样。恶意程序经常使用特殊文件。比如:一下特殊文件比普通文件更隐蔽,因为它们在列出目录时不会显示出来,某些特殊文件可以提取对系统硬件和内部数据更强的访问能力。

Windows注册表

Windows注册表被用来保存操作系统与程序配置信息,比如设置和选项,和文件系统一样,它是基于主机的感染迹象的很好来源,并且能够揭示出关于恶意代码功能的有用信息。Windows的早期版本使用.ini文件来保存配置信息。注册表被引入后,作为一个层次逐步提升。现在几乎所有的Windows配置信息都保存在注册表中,包括网络驱动,启动项,用户账户,以及其他信息。
恶意代码经常使用注册表来完成持久驻留或者存储配置数据。恶意代码添加项到注册表中,这使它在计算机引导时能够自动运行。

你需要知道几个重要的注册表术语

根键: 注册表被划分为称为根键的5个顶层节。有时,术语HKEY和储巢也被使用。每一个跟键有一个特定的目的。
子键: 一个子键就像一个文件夹中的子文件夹
键: 一个键是一个注册表中的文件夹,它可以包含额外的文件夹或键值。跟健和子键都是键。
值项: 一个值项是一个配对的名字和值。
值或数据: 值或数据是存储在注册表项中的数据。

注册表根键

注册表被划分成下面5个键:

1
2
3
4
5
HEKY_LOCAL_MACHINE(HKLM) 保存对本地机器全局设置
HEKY_CURRENT_USER(HKCU) 保存当前用户特定的设置
HEKY_CLASSES_ROOT 保存定义的类型信息
HEKY_CURRENT_CONFIG 保存关于当前硬件配置的设置
HEKY_USERS 定义默认用户,新用户和当前用户的配置

最常用的根键是HKLM和HKCU。

注册表编辑器(Regedit)

注册表编辑器(Regedit)是windows内建的用来查看和编辑注册表的工具。

自启动程序

向run子健中写入项,是一个普定设置程序自启动的方法,尽管不是一个隐蔽的技术,它任然经常被恶意代码用来自启动自身程序。(Autoruns工具可以使用)

常用注册表函数

1
2
3
RegOpenKeyEx 打开一个注册表进行编辑和查询(有些函数例外,大多数还是会使用的)。
RegSetValueEx 添加一个新值到注册表,并设置它的数值。
RegGetValue 返回注册表中一个值项的数值

网络API

伯克利兼容套接字

在Windows的网络选项中,恶意代码最普遍使用的是伯克利兼容套接字,在UNIX也是一样的。伯克利兼容套接字的网络功能在Windows系统中是由Winsock库实现的,主要在ws32_32.dll中,在所有库函数中,socket,connect,bind,listen,accept,send和recv函数最常用的

1
2
3
4
5
6
7
socket 创建一个套接字
bind 将一个套接字绑定到特定端口,应该在accept前调用
listen 准备着一个套接字将进入监听,等待入站连接
accept 向一个远程套接字打开一个连接,并接受连接
connect 向一个远程套接字打开一个连接,远程套接字必须在等待连接
recv 从远程套接字接收数据
send 发送数据到远程套接字

跟踪恶意代码的运行

动态链接库(DLL)

DLL(Dynamic Link Library)文件为动态链接库文件,又称“应用程序拓展”,是软件文件类型。在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中。当我们执行某一个程序时,相应的DLL文件就会被调用。一个应用程序可使用多个DLL文件,一个DLL文件也可能被不同的应用程序使用,这样的DLL文件被称为共享DLL文件
恶意代码作者使用DLL的三种方式:

保存恶意代码

有时候将恶意代码保存到一个DLL文件比exe文件更加有利。它可以附加到其他进程

通过使用Windows DLL

可以通过windows调用dll的程序

通过使用第三方的DLL

因为是动态链接,所以可以使用第三方的DLL

DLL的结构

dll的文件结构几乎和exe文件一模一样,只有一个单一的标志指示这是dll。DLL经常有更多的导出函数,并且导入函数较少。

进程

恶意代码可以通过创建一个新的进程或修改一个已存在的进程,来执行恶意代码。
创建一个新的进程需要用到这个函数CreateProcess,下面是代码范例:

1
2
3
4
5
6
7
8
9
10
11
12
BOOL CreateProcess(
 LPCTSTR lpApplicationName, // 应用程序名称
 LPTSTR lpCommandLine, // 命令行字符串
 LPSECURITY_ATTRIBUTES lpProcessAttributes, // 进程的安全属性
 LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程的安全属性
 BOOL bInheritHandles, // 是否继承父进程的属性
 DWORD dwCreationFlags, // 创建标志
 LPVOID lpEnvironment, // 指向新的环境块的指针
 LPCTSTR lpCurrentDirectory, // 指向当前目录名的指针
 LPSTARTUPINFO lpStartupInfo, // 传递给新进程的信息
 LPPROCESS_INFORMATION lpProcessInformation // 新进程返回的信息
);

线程

进程是执行代码的容器,线程才是Windows操作系统真正要执行的内容。
创建一个新的线程CreateThread

使用互斥量(mutex)的进程间协作

互斥量是全局对象,用于协调多个进程和线程。主要用于控制共享资源的访问,并且经常被恶意代码使用(因为它们经常使用硬编码)。因为如果一个互斥量被两个不使用其它方式通讯的进程使用时,它的名字必须是互斥一致的。
一个互斥量可以通过CreateMutex函数进行创建,而进程可以通过OpenMutex调用来获取另一个进程中互斥量的句柄。线程通过一个对WaitForSingleObject的调用,获取对互斥量的访问,当一个线程完成对互斥量的使用后,需要使用ReleaseMutex

内核与用户模式

Windows使用两种处理器特权级别:内核模式与用户模式。
依据处理器上执行的代码的类型,处理器在两个模式之间切换。应用程序在用户模式下执行。核心操作系统组件在内核模式下执行。多个驱动程序在内核模式下执行,但某些驱动程序在用户模式下执行。
用户模式:不能直接访问硬件,应用程序的虚拟地址空间除了为专用空间以外。还会受到限制。在用户模式下执行的处理器无法訪问为该操作系统保留的虚拟地址。限制用户模式应用程序的虚拟地址空间可防止应用程序更改而且可能损坏关键的操作系统数据。
内核模式: 内核模式下执行的全部代码都共享单个虚拟地址空间。这表示内核模式驱动程序未从其它驱动程序和操作系统自身独立开来。

原生态API

原生态API是用来和Windows进行交互的底层API,他们很少被非恶意软件使用,但是在恶意代码作者之间却很受欢迎。调用原生API函数可以绕过普通Windows API。
当调用Windows API中的一个函数时,这个函数通常不会直接执行请求的动作,因为大多数重要的数据结构都会被保存在内核中。这时用户程序被给予对用户API(如:DLL)的访问,这些DLL会调用ntdll.dll,这是一个特殊的DLL程序,它管理用户空间与内核的 交互。然后处理器却换到内核模式,并执行内核中的一个函数。这里ntdll函数像内核中的函数一样,使用API和结构体,这些函数组成了原生API。应用程序不建议调用原生API,但是操作系统中没有任何东西来阻止它们这样做。所有直接调用原生API对恶意代码作者来说很有吸引力,因为原生态API允许它们做一些采用别的方式不可能做到的事情,同时有时候很隐蔽(比如:一些反病毒软件是监控一个进程使用的系统调用,这里就可以躲避啦)。

lab

这个程序如何完成持久化驻留,来确保在计算机被重启后它能继续运行

发现两个函数:

其中一个是StartServiceCtrlDispatcherA——>在2000/XP等基于NT 的操作系统中,有一个服务管理器,它管理的后台进程被称为 service。在任务管理器中,并且随系统启动而最先运行,随系统关闭而最后停止。

为什么程序会使用一个互斥量

用于控制共享资源的访问

可以用来检测这个程序的基于主机特征是什么?

使用Malservice与HGL345的互斥量,发现恶意域名:http://www.malwareanalysisbook.com

检测这个恶意代码基于网络特征是什么?

发现恶意域名:http://www.malwareanalysisbook.com。并且调用InternetOpenUrlA网络函数

这个程序的目的是什么

开机自启动,并且访问域名http://www.malwareanalysisbook,同时有WriteFile函数,可以写文件

这个程序什么时候完成执行

关机即结束

API实战

MessageBox

MessageBox指的是显示一个模态对话框
函数定义

1
2
3
4
5
6
7
8
9
10
int WINAPI MessageBox(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType);
hWnd:
此参数代表消息框拥有的窗口。如果为NULL,则消息框没有拥有窗口。
lpText:
消息框的内容。如果使用了Unicode库,则把文本变成:
lpCaption:
消息框的标题。如果使用了Unicode库,则把文本变成
uType:
指定一个决定对话框的内容和行为的位标志集。此参数可以为下列标志组中标志的组合。指定下列标志中的一个来显示消息框中的按钮以及图标。

使用

1
MessageBox(NULL,L"显示的字",L"标题",MB_RETRYCANCEL);//L宏来传递字符串 MB_RETRYCANCEL按钮数量

GetWindowText

Windows API宏,在WinUser.h中根据是否已定义Unicode被分别定义为GetWindowTextW和GetWindowTextA。该函数将指定窗口的标题条文本(如果存在)拷贝到一个缓存区内。如果指定的窗口是一个控件,则拷贝控件的文本。
函数定义

1
2
3
4
5
6
7
8
Int GetWindowText(HWND hWnd,LPTSTR lpString,Int nMaxCount);
GetWindowText(
hWnd: HWND; {窗口句柄}
lpString: PChar; {接收文本的缓冲区的指针}
nMaxCount: Integer {指定缓冲区大小, 其中包含NULL字符; 如果文本超出,会被被截断}
): Integer; {返回字符个数, 不包括中断的空字符; 如果标题为空或句柄无效, 则返回零}

Donate
-------------本文结束感谢您的阅读-------------