恶意软件API汇总(更新ing)

该地会汇总笔者在学习恶意样本分析过程中遇到的知名API,其中包括书上介绍的、实践分析过程中遇到的,会以功能为标题进行整理。

恶意代码分析实战

核心功能:窃密

击键记录

位置:恶意代码分析实战-11章-击键记录

功能:实现击键记录获取-登录凭证获取

函数名SetWindowsHookExGetAsyncKeyStateGetForegroundWindow

  1. SetWindowsHookEx介绍:

    函数功能:

    SetWindowsHookEx 函数用于安装一个钩子过程,以监视和拦截特定的事件或消息。它允许你拦截和处理全局的鼠标、键盘、消息和其他系统事件。

    函数格式

    参数介绍

    • idHook(int):指定要安装的钩子类型。它可以取以下值之一:

      • WH_KEYBOARD:键盘钩子,用于监视键盘输入。
      • WH_MOUSE:鼠标钩子,用于监视鼠标输入。
      • WH_KEYBOARD_LL:低级键盘钩子,用于监视全局键盘输入。
      • WH_MOUSE_LL:低级鼠标钩子,用于监视全局鼠标输入。
      • 其他钩子类型,可以根据需要选择。
    • lpfn(HOOKPROC):指向钩子过程的指针。钩子过程是一个回调函数,当特定事件发生时,系统会调用此函数。钩子过程的具体实现根据钩子类型而异。

    • hMod(HINSTANCE):指定包含钩子过程的 DLL 的句柄。如果钩子过程位于当前进程的可执行文件中,可以传递 NULL。

    • dwThreadId(DWORD):标识要安装钩子的线程的线程标识符。如果要安装一个全局钩子,可以将此参数设置为 0,并且钩子过程将在所有线程上调用。

    返回值

    如果函数成功,返回值是一个钩子的句柄(HHOOK)。如果函数失败,返回值为 NULL。

    歌曰:安装钩子需要具有足够的权限,因此通常需要以管理员身份运行应用程序。


  2. GetAsyncKeyState函数

    函数功能:

    GetAsyncKeyState 函数用于检索指定虚拟键的状态(按下或释放)。它可以用于获取当前键盘上某个键的状态,无论该键是否与当前活动窗口相关。

    函数格式:

    1
    2
    3
    SHORT GetAsyncKeyState(
    int vKey
    );

    参数

    • vKey(int):指定要检索状态的虚拟键码。可以使用预定义的虚拟键码(如 VK_RETURNVK_ESCAPE 等)或自定义虚拟键码。

    返回值:

    如果指定的虚拟键当前被按下,则返回值的最高位(MSB)被设置为1;如果指定的虚拟键当前未被按下,则返回值为0。对于返回值的其余位(除了最高位),它们是按下时返回的计数值,以指示键被按下的次数。

  3. GetForegroundWindow函数

    函数功能:

    GetForegroundWindow 函数用于获取当前具有焦点的窗口的句柄。它返回当前活动窗口的句柄,即用户当前正在与之交互的窗口。

    函数格式:

    1
    HWND GetForegroundWindow();

    参数

    返回值:

    返回值是当前具有焦点的窗口的句柄(HWND)。如果没有窗口具有焦点,返回值为 NULL

资源节使用

位置:恶意代码分析实战-示例

功能:资源节隐藏载荷,进行读取与载入

函数FindResource、LoadResource、LockResource、SizeofResource、VirtualAlloc、FreeResource

以下是关于这些函数的介绍:

  1. FindResource 函数:

    • 用途:在可执行文件(或 DLL)的资源表中查找指定的资源
    • 参数
      • hModule:指定包含资源的模块的句柄,通常是可执行文件或 DLL 的句柄。如果为 NULL,表示使用当前可执行文件或 DLL。
      • lpName:指定资源的名称或标识符
      • lpType:指定资源的类型
    • 返回值:如果函数调用成功,返回指向资源的句柄;否则返回 NULL
  2. LoadResource 函数:

    • 用途:加载指定资源的内容,返回资源的句柄。
    • 参数
      • hModule:指定包含资源的模块的句柄,通常是可执行文件或 DLL 的句柄。如果为 NULL,表示使用当前可执行文件或 DLL。
      • hResInfo:指定资源的句柄,可以使用 FindResource 函数获取。
    • 返回值:如果函数调用成功,返回指向资源的句柄;否则返回 NULL
  3. LockResource 函数:

    • 用途:锁定 LoadResource 返回的资源句柄,返回资源的指针。
    • 参数
      • hResData:指向资源的句柄,通常是 LoadResource 函数的返回值。
    • 返回值:如果函数调用成功,返回指向资源数据的指针;否则返回 NULL
  4. SizeofResource 函数:

    • 用途:获取指定资源的大小。
    • 参数
      • hModule:指定包含资源的模块的句柄,通常是可执行文件或 DLL 的句柄。如果为 NULL,表示使用当前可执行文件或 DLL。
      • hResInfo:指定资源的句柄,可以使用 FindResource 函数获取。
    • 返回值:如果函数调用成功,返回资源的大小;否则返回 0。
  5. VirtualAlloc 函数:

    • 用途:在进程的虚拟地址空间中分配内存。
    • 参数:
      • lpAddress:指定要分配的内存的首选地址。如果为 NULL,系统将自动选择一个地址。
      • dwSize:指定要分配的内存的大小(以字节为单位)。
      • flAllocationType:指定内存分配的类型和属性,例如 MEM_COMMITMEM_RESERVEMEM_RESET 等。
      • flProtect:指定内存保护属性,例如 PAGE_READWRITEPAGE_EXECUTE_READ 等。
    • 返回值:如果函数调用成功,返回指向分配内存的首地址的指针;否则返回 NULL
  6. FreeResource 函数:

    • 用途:释放由 FindResourceLoadResourceLockResource 函数加载的资源。
    • 参数
      • hResData:指向资源的句柄,通常是 LoadResource 函数的返回值。
    • 返回值:无。

这些函数通常在处理和操作可执行文件或 DLL 中的资源时使用。它们允许你查找、加载、访问和释放资源的内容。

实例

image-20230913171304978

文件操作

进程源文件的移动

主要用于源文件的替换操作,先把源文件放到temp目录下。涉及到的API有: GetWindowsDirectoryA、GetTempPathA、 snprintf、MoveFileA

  • GetWindowsDirectoryA:该函数用于获取 Windows 操作系统的安装目录。
  • GetTempPathA:该函数用于获取临时文件夹的路径。
  • snprintfsnprintf 是一个 C 标准库函数,用于将格式化的数据写入字符串缓冲区。
  • MoveFileA:该函数用于重命名或移动文件或目录。

网络下载到文件

用于从网络下载数据到指定文件,进行软件更新。

URLDownloadToFile 函数:

函数功能:它可以从指定的 URL 下载文件,并将其保存到本地文件系统中的指定位置。

函数格式:

1
2
3
4
5
6
7
HRESULT URLDownloadToFile(
LPUNKNOWN pCaller,
LPCWSTR szURL,
LPCWSTR szFileName,
DWORD dwReserved,
LPBINDSTATUSCALLBACK lpfnCB
);

函数参数

  • pCaller: 指向 IUnknown 接口的指针,可以为 NULL
  • szURL: 要下载的文件的 URL 地址。
  • szFileName: 下载的文件保存的本地路径和文件名。
  • dwReserved: 保留参数,必须为 0。
  • lpfnCB: 指向 IBindStatusCallback 接口的指针,用于接收下载进度回调,可以为 NULL

返回值:函数返回值为 HRESULT 类型的值,用于表示操作的结果。如果函数调用成功,返回值为 S_OK,否则可能返回其他错误代码。

歌曰URLDownloadToFile 函数已被标记为过时(deprecated),不推荐在新的应用程序中使用。在现代的 Windows 系统中,推荐使用更强大和灵活的网络库或框架,如 WinINet、WinHTTP、CURL 等来进行文件下载操作。

注册表操作

网络连接操作

进程操作

创建(挂起)- 进程替换、打开进程-获取句柄、遍历操作-搜索指定进程、创建线程-进程注入。

遍历操作-搜索指定进程

一般是psapi.dll 提供的一组函数。可以查询系统中的进程和模块信息,这些函数可以用于编写诸如进程监视、进程注入、进程枚举等类型的系统工具和应用程序。

  • EnumProcesses:枚举系统中正在运行的进程。
  • EnumProcessModules:枚举指定进程中加载的模块。
  • GetModuleBaseName:获取指定进程中模块的基本名称。
  • GetModuleFileNameEx:获取指定进程中模块的完整路径。
  • GetModuleInformation:获取指定进程中模块的详细信息。
  • GetProcessImageFileName:获取指定进程的可执行文件路径。
  • GetProcessId:获取指定进程的进程 ID。
  • GetProcessTimes:获取指定进程的运行时间和 CPU 时间等。

下面主要细致介绍下前三个函数:

  1. EnumProcesses函数介绍:

    函数功能:用于枚举系统中正在运行的进程。

    函数格式:

    1
    2
    3
    4
    5
    BOOL EnumProcesses(
    DWORD *lpidProcess,
    DWORD cb,
    DWORD *lpcbNeeded
    );

    函数参数

    • lpidProcess:指向一个接收进程 ID 列表的数组的指针。调用该函数时,数组将被填充为系统中正在运行的进程的 ID。每个进程 ID 都是一个 DWORD 类型的值。
    • cb:指定 lpidProcess 数组的大小,以字节为单位。
    • lpcbNeeded:指向一个变量的指针,用于接收函数执行所需的实际字节数。如果传入的 cb 小于所需的大小,则此变量将包含所需的大小值

    返回值:返回值为 BOOL 类型,表示函数执行的成功或失败。如果函数成功执行,则返回值为非零;如果函数失败,则返回值为零。

    实际例子

    image-20230912165300260

  2. EnumProcessModules函数:

    函数功能:枚举指定进程中加载的模块(DLL),存储句柄。

    函数格式:

    1
    2
    3
    4
    5
    6
    BOOL EnumProcessModules(
    HANDLE hProcess,
    HMODULE *lphModule,
    DWORD cb,
    LPDWORD lpcbNeeded
    );

    函数参数:

    • hProcess:指定要枚举模块的进程的句柄。通常使用 OpenProcess 函数打开进程并获取句柄。
    • lphModule:指向存储模块句柄的数组的指针。调用该函数时,数组将被填充为进程中加载的模块的句柄。每个模块句柄是一个 HMODULE 类型的值。
    • cb:指定 lphModule 数组的大小,以字节为单位。
    • lpcbNeeded:指向一个变量的指针,用于接收函数执行所需的实际字节数。如果传入的 cb 小于所需的大小,则此变量将包含所需的大小值。

    返回值:返回值为 BOOL 类型,表示函数执行的成功或失败。如果函数成功执行,则返回值为非零;如果函数失败,则返回值为零。

  3. GetModuleBaseNameA函数:

    函数功能:用于获取指定进程中模块的基本名称(即模块的文件名)

    函数格式

    1
    2
    3
    4
    5
    6
    DWORD GetModuleBaseNameA(
    HANDLE hProcess,
    HMODULE hModule,
    LPSTR lpBaseName,
    DWORD nSize
    );

    函数参数

    • hProcess:指定要获取模块名称的进程的句柄。通常使用 OpenProcess 函数打开进程并获取句柄。
    • hModule:指定要获取基本名称的模块的句柄。如果指定 NULL,则返回主模块(即进程的可执行文件)的基本名称。
    • lpBaseName:指向接收基本名称的字符缓冲区的指针。
    • nSize:指定接收基本名称的字符缓冲区的大小,以字节为单位。

    返回值:返回值为 DWORD 类型,表示实际复制到缓冲区中的字符数,不包括终止 null 字符。如果函数失败,则返回值为 0。

    实践一下

    image-20230912171212640

    歌曰:

    GetModuleBaseNameA 是 ANSI 版本的函数,适用于使用 ANSI 字符集的应用程序。如果需要使用 Unicode 字符集,可以使用 GetModuleBaseNameW 函数。

进程创建与进程替换

这部分主要是进程替换过程中涉及的函数,包括:CreateProcessVirtualAllocWriteProcessMemorySetThreadContextRssumeThread

  1. CreateProcess函数:

    函数功能:用于创建新的进程。它可以创建一个新的进程并且启动执行指定的可执行文件。

    函数格式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    BOOL CreateProcess(
    LPCWSTR lpApplicationName,
    LPWSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCWSTR lpCurrentDirectory,
    LPSTARTUPINFOW lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
    );

    函数参数

    • lpApplicationName:可执行文件的路径或文件名。
    • lpCommandLine:命令行参数。
    • lpProcessAttributes:进程对象的安全属性,可以为NULL
    • lpThreadAttributes:线程对象的安全属性,可以为NULL
    • bInheritHandles:指定新进程是否继承父进程的句柄。
    • dwCreationFlags:指定创建进程的标志和选项。dwCreationFlags 参数设置 CREATE_SUSPENDED 标志表示挂起。
    • lpEnvironment:新进程的环境变量,可以为NULL
    • lpCurrentDirectory:新进程的当前工作目录,可以为NULL
    • lpStartupInfo:指向STARTUPINFOW结构的指针,用于指定新进程的启动信息。
    • lpProcessInformation:指向PROCESS_INFORMATION结构的指针,用于接收新进程的信息。

    返回值:函数返回值为BOOL类型,表示函数调用是否成功。如果函数成功,返回值为TRUE,否则返回值为FALSE

  2. VirtualAllocEx函数

    函数功能:

    VirtualAllocEx函数用于在指定的外部进程中分配内存空间。它允许一个进程分配内存,而该内存可以在不同的进程之间共享。这个函数的常见用途之一是在远程进程中注入代码或数据

    函数格式:

    1
    2
    3
    4
    5
    6
    7
    LPVOID VirtualAllocEx(
    HANDLE hProcess,
    LPVOID lpAddress,
    SIZE_T dwSize,
    DWORD flAllocationType,
    DWORD flProtect
    );

    函数参数:

    • hProcess:要分配内存的目标进程的句柄
    • lpAddress:指定分配内存的起始地址,如果为NULL,则由系统自动选择。
    • dwSize:要分配的内存大小,以字节为单位。
    • flAllocationType:内存分配类型的标志,指定内存的使用方式,例如MEM_COMMIT表示立即提交内存,MEM_RESERVE表示保留内存。
    • flProtect:内存保护标志,指定内存区域的访问权限,例如PAGE_READWRITE表示可读写。

    返回值:

    VirtualAllocEx函数的返回值是分配的内存的起始地址。如果分配内存失败,返回值为NULL。可以使用GetLastError函数获取更多关于失败原因的信息。

  3. WriteProcessMemory函数

    函数功能:

    WriteProcessMemory函数用于在指定的外部进程中写入数据。它允许一个进程将数据写入其他进程的分配的内存空间。这个函数的常见用途之一是在远程进程中修改数据或注入代码。

    函数格式:

    1
    2
    3
    4
    5
    6
    7
    BOOL WriteProcessMemory(
    HANDLE hProcess,
    LPVOID lpBaseAddress,
    LPCVOID lpBuffer,
    SIZE_T nSize,
    SIZE_T *lpNumberOfBytesWritten
    );

    函数参数:

    • hProcess:要写入数据的目标进程的句柄
    • lpBaseAddress:要写入数据的内存地址
    • lpBuffer:指向要写入的数据缓冲区的指针。
    • nSize:要写入的数据大小,以字节为单位。
    • lpNumberOfBytesWritten:指向保存实际写入字节数的变量的指针。

    返回值:

    WriteProcessMemory函数的返回值是一个BOOL类型的值,表示写入操作是否成功。如果写入成功,返回值为非零;如果写入失败,返回值为零。可以使用GetLastError函数获取更多关于失败原因的信息。

  4. GetThreadContext 函数:

    函数功能:用于获取指定线程的上下文信息。

    函数格式

    1
    2
    3
    4
    BOOL GetThreadContext(
    HANDLE hThread,
    LPCONTEXT lpContext
    );
    • hThread:要获取上下文信息的目标线程的句柄。
    • lpContext:指向 CONTEXT 结构体的指针,用于接收线程的上下文信息。

    流程:调用 GetThreadContext 函数时,传递目标线程的句柄和指向 CONTEXT 结构体的指针。函数将填充指定的 CONTEXT 结构体,以提供有关线程的寄存器值、标志寄存器状态、堆栈指针等信息。

  5. SetThreadContext函数:

    函数功能:用于设置指定线程的上下文信息,包括寄存器值、标志位等。(在这里一般用于设置入口点)

    函数格式:

    1
    2
    3
    4
    BOOL SetThreadContext(
    HANDLE hThread,
    const CONTEXT *lpContext
    );

    函数参数:

    • hThread:要设置上下文的线程句柄。
    • lpContext:指向CONTEXT结构的指针,包含要设置的上下文信息。

    返回值:函数返回值为BOOL类型,表示函数调用是否成功。如果函数成功,返回值为TRUE,否则返回值为FALSE

  6. RssumeThread函数:

    函数功能:用于恢复被挂起的线程的执行。

    函数格式:

    1
    2
    3
    DWORD ResumeThread(
    HANDLE hThread
    );

    函数参数:

    • hThread:要恢复执行的线程句柄。

    返回值:函数返回值为线程的先前挂起计数。如果函数调用失败,返回值为DWORD类型的错误代码。

总结:一般是连环使用,先是创建一个挂起的进程,之后清空数据再替换数据,然后修改入口点最后启动进程。


Createprocess创建与上下文的扩展:

  1. 挂起态创建

    在使用该函数创建一个新的进程的时候,可以指定dwCreationFlags参数为4,表示挂起该进程,直到使用RssumeThread函数才进行执行;

  2. IpStartupinfo参数介绍:

    该参数是一个结构体,指定新进程的启动信息。下面是该结构体的定义:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    typedef struct _STARTUPINFOA {
    DWORD cb;
    LPSTR lpReserved;
    LPSTR lpDesktop;
    LPSTR lpTitle;
    DWORD dwX;
    DWORD dwY;
    DWORD dwXSize;
    DWORD dwYSize;
    DWORD dwXCountChars;
    DWORD dwYCountChars;
    DWORD dwFillAttribute;
    DWORD dwFlags;
    WORD wShowWindow;
    WORD cbReserved2;
    LPBYTE lpReserved2;
    HANDLE hStdInput;
    HANDLE hStdOutput;
    HANDLE hStdError;
    } STARTUPINFOA, *LPSTARTUPINFOA;
    • cb:结构体的大小,以字节为单位,用于指定结构体的版本。
    • lpReserved:保留字段,未使用,应设置为 NULL
    • lpDesktop:新进程要使用的桌面名称,通常为 NULL 表示使用默认桌面。
    • lpTitle:新进程的窗口标题,可以为 NULL
    • dwXdwY:新进程的初始窗口左上角的屏幕坐标。
    • dwXSizedwYSize:新进程的初始窗口的宽度和高度。
    • dwXCountCharsdwYCountChars:新进程的控制台窗口的字符单元数。
    • dwFillAttribute:控制台窗口的填充属性。
    • dwFlags:指定启动标志,例如 STARTF_USESHOWWINDOWSTARTF_USESTDHANDLES 等。
    • wShowWindow:指定新进程的窗口如何显示,如 SW_SHOWSW_HIDE 等。
    • cbReserved2lpReserved2:保留字段,未使用,应设置为 0 或 NULL
    • hStdInputhStdOutputhStdError:指定新进程的标准输入、输出和错误句柄。

    所以我们可以先以挂起态创建某进程,再修改该结构体内容。

  3. IpProcessInformation参数介绍:

    用于接收新进程的信息,该结构体定义如下:

    1
    2
    3
    4
    5
    6
    typedef struct _PROCESS_INFORMATION {
    HANDLE hProcess;
    HANDLE hThread;
    DWORD dwProcessId;
    DWORD dwThreadId;
    } PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
    • hProcess:新进程的句柄,用于操作和管理该进程。
    • hThread:新进程的主线程句柄,用于操作和管理该主线程。
    • dwProcessId:新进程的进程标识符(Process ID)。
    • dwThreadId:新进程的主线程标识符(Thread ID)。
  4. IpContext介绍:

    CONTEXT 结构体用于保存和描述线程的上下文信息,下面是结构体定义:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    typedef struct _CONTEXT {
    DWORD ContextFlags;
    DWORD Dr0;
    DWORD Dr1;
    DWORD Dr2;
    DWORD Dr3;
    DWORD Dr6;
    DWORD Dr7;
    FLOATING_SAVE_AREA FloatSave;
    DWORD SegGs;
    DWORD SegFs;
    DWORD SegEs;
    DWORD SegDs;
    DWORD Edi;
    DWORD Esi;
    DWORD Ebx;
    DWORD Edx;
    DWORD Ecx;
    DWORD Eax;
    DWORD Ebp;
    DWORD Eip;
    DWORD SegCs;
    DWORD EFlags;
    DWORD Esp;
    DWORD SegSs;
    BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
    } CONTEXT, *PCONTEXT;
    • ContextFlags:用于指定上下文中包含的特定标志。
    • Dr0Dr7:调试寄存器,用于保存调试相关的信息。
    • FloatSave:浮点寄存器的保存区域。
    • SegGsSegSs:段寄存器,用于保存段选择子。
    • EdiEax:通用寄存器,用于保存通用数据。
    • Ebp:基址指针寄存器,指向当前栈帧的基址。
    • Eip:指令指针寄存器,存储下一条要执行的指令的地址。
    • SegCs:代码段寄存器,保存当前代码段的选择子。
    • EFlags:标志寄存器,包含了一些特定的控制和状态标志。
    • Esp:堆栈指针寄存器,指向当前栈顶的地址。
    • SegSs:堆栈段寄存器,保存当前堆栈段的选择子。
    • ExtendedRegisters:用于保存扩展寄存器的数据。

    通过传递一个指向 CONTEXT 结构体的指针作为参数,可以获取或设置线程的上下文信息。这些信息包括寄存器的值、标志寄存器的状态、堆栈指针、代码指针等。

熊猫烧香样本

魔窟样本

Mydoom样本

大黄蜂样本

游蛇样本


写在最后:这是不断更新的一章,后续分析各种家族样本与书籍示例时都会学习到新的API,会以样本为分类进行记录。