远线程注入原理是利用Windows 系统中CreateRemoteThread()这个API,其中第4个参数是准备运行的线程,我们可以将LoadLibrary()填入其中,这样就可以执行远程进程中的LoadLibrary()函数,进而将我们自己准备的DLL加载到远程进程空间中执行。
函数原型:
HANDLE
WINAPI
CreateRemoteThread(
_In_ HANDLE hProcess, //远程线程的句柄
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, //安全属性
_In_ SIZE_T dwStackSize, //栈大小
_In_ LPTHREAD_START_ROUTINE lpStartAddress, //进程处理函数
_In_opt_ LPVOID lpParameter, //进程参数
_In_ DWORD dwCreationFlags, //默认创建后的状态
_Out_opt_ LPDWORD lpThreadId //所创建的线程的ID
);
注入过程:
1.提权
2.根据进程ID打开对方进程OpenProcess,得到进程句柄
3.根据进程句柄在目标进程中申请内存VirtualAllocEx
4.在目标进程中刚刚申请的内存空间中写入所需参数(Dll的完整路径)WriteProcessMemory
5.用GetProcAddress得到LoadLibraryW的模块加载地址
6.启动远程线程CreateRemoteThread,并在第四参数传入该线程需要执行的函数名(即loadlibrary)
BOOL InjectDllByRemoteThread(ULONG32 ulProcessID, WCHAR* wzDllFullPath) { HANDLE ProcessHandle = NULL; ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ulProcessID); if (ProcessHandle==NULL) { return FALSE; } WCHAR* VirtualAddress = NULL; ULONG32 ulDllLength = (ULONG32)_tcslen(wzDllFullPath) + 1; VirtualAddress = (WCHAR*)VirtualAllocEx(ProcessHandle, NULL, ulDllLength * sizeof(WCHAR), MEM_COMMIT, PAGE_READWRITE); if (VirtualAddress==NULL) { CloseHandle(ProcessHandle); return FALSE; } // 在目标进程的内存空间中写入所需参数(模块名) if (!WriteProcessMemory(ProcessHandle, VirtualAddress, (LPVOID)wzDllFullPath, ulDllLength * sizeof(WCHAR), NULL)) { VirtualFreeEx(ProcessHandle, VirtualAddress, ulDllLength, MEM_DECOMMIT); CloseHandle(ProcessHandle); return FALSE; } LPTHREAD_START_ROUTINE FunctionAddress = NULL; FunctionAddress = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(_T("Kernel32")), "LoadLibraryW"); HANDLE ThreadHandle = INVALID_HANDLE_VALUE; //启动远程线程 ThreadHandle = ::CreateRemoteThread(ProcessHandle, NULL, 0, FunctionAddress, VirtualAddress, 0, NULL); if (ThreadHandle==FALSE) { VirtualFreeEx(ProcessHandle, VirtualAddress, ulDllLength, MEM_DECOMMIT); CloseHandle(ProcessHandle); return FALSE; } // 等待远程线程结束 WaitForSingleObject(ThreadHandle, INFINITE); // 清理 VirtualFreeEx(ProcessHandle, VirtualAddress, ulDllLength, MEM_DECOMMIT); CloseHandle(ThreadHandle); CloseHandle(ProcessHandle); return TRUE; }
BOOL EnableDebugPrivilege() { HANDLE TokenHandle = NULL; TOKEN_PRIVILEGES TokenPrivilege; LUID uID; //打开权限令牌 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle)) { return FALSE; } if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &uID)) { CloseHandle(TokenHandle); TokenHandle = INVALID_HANDLE_VALUE; return FALSE; } TokenPrivilege.PrivilegeCount = 1; TokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; TokenPrivilege.Privileges[0].Luid = uID; //在这里我们进行调整权限 if (!AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivilege, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) { CloseHandle(TokenHandle); TokenHandle = INVALID_HANDLE_VALUE; return FALSE; } CloseHandle(TokenHandle); TokenHandle = INVALID_HANDLE_VALUE; return TRUE; }
但是如此有个问题,如果目标进程为32位,但是注入了一个64的Dll则会出问题。
为此我们应当判断目标进程的位数。
我们可以通过解析exe文件(magic数)判断进程是x64还是x86。
根据PE知识,所有的PE文件必须以一个DOS MZ header开始,其实它是一个IMAGE_DOS_HEADER类型的结构,
//DOS头 typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // Magic number ‘MZ’ WORD e_cblp; // Bytes on last page of file WORD e_cp; // Pages in file WORD e_crlc; // Relocations WORD e_cparhdr; // Size of header in paragraphs WORD e_minalloc; // Minimum extra paragraphs needed WORD e_maxalloc; // Maximum extra paragraphs needed WORD e_ss; // Initial (relative) SS value WORD e_sp; // Initial SP value WORD e_csum; // Checksum WORD e_ip; // Initial IP value WORD e_cs; // Initial (relative) CS value WORD e_lfarlc; // File address of relocation table WORD e_ovno; // Overlay number WORD e_res[4]; // Reserved words WORD e_oemid; // OEM identifier (for e_oeminfo) WORD e_oeminfo; // OEM information; e_oemid specific WORD e_res2[10]; // Reserved words LONG e_lfanew; // File address of new exe header NT头偏移 } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
这是PE文件的大致结构,而我们需要的magic成员在_IMAGE_NT_HEADERS结构体中的选项头中_IMAGE_OPTIONAL_HEADER
_IMAGE_DOS_HEADER结构体的最后一个元素e_lfanew 便是指向_IMAGE_NT_HEADERS的偏移
//NT头 typedef struct _IMAGE_NT_HEADERS { DWORD Signature; //‘PE’ IMAGE_FILE_HEADER FileHeader; //PE文件头 IMAGE_OPTIONAL_HEADER32 OptionalHeader; //PE选项头 } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
//选项头 typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; //我们需要的成员 BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
所以判断目标进程的代码为:
enum TargetType { WOW_86, WOW_64, WOW_ERROR }; //通过解析exe文件(magic数)判断进程是x64还是x86 TargetType GetWowByReadFile(ULONG32 ulProcessID) { HANDLE ProcessHandle = INVALID_HANDLE_VALUE; ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ulProcessID); if (ProcessHandle == NULL) { return WOW_ERROR; } //获得Exe模块基地址 ULONG64 ulModuleBaseAddress = (ULONG64)GetModuleBaseAddressByProcessHandle(ProcessHandle); if (ulModuleBaseAddress == NULL) { CloseHandle(ProcessHandle); return WOW_ERROR; } IMAGE_DOS_HEADER DosHeader = { 0 }; //读取Dos头 if (ReadProcessMemory(ProcessHandle, (PVOID)ulModuleBaseAddress, &DosHeader, sizeof(IMAGE_DOS_HEADER), NULL) == FALSE) { CloseHandle(ProcessHandle); return WOW_ERROR; } WORD wMagic = 0; //模块加载基地址+Dos头部e_lfanew成员(PE头相对于文件的偏移 4字节)+标准PE头+4字节 if (ReadProcessMemory(ProcessHandle, (PVOID)(ulModuleBaseAddress + DosHeader.e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER)), &wMagic, sizeof(WORD), NULL) == FALSE) { CloseHandle(ProcessHandle); return WOW_ERROR; } CloseHandle(ProcessHandle); if (wMagic == 0x20b)//x64 { return WOW_64; } else if (wMagic == 0x10b)//x86 { return WOW_86; } else { return WOW_ERROR; } }
结合这个功能,主函数为:
int main() { if (EnableDebugPrivilege() == FALSE) { return 0; } ULONG32 ulProcessID = 0; printf("Input A ProcessID to Inject: "); scanf_s("%d", &ulProcessID, sizeof(ULONG32)); DWORD iOk = GetWowByReadFile(ulProcessID); switch (iOk) { case WOW_64: if (InjectDllByRemoteThread(ulProcessID, L"InjectDll.dll")) { printf("Inject Success! "); break; } case WOW_86: if (InjectDllByRemoteThread(ulProcessID, L"InjectTest32.dll")) { printf("Inject Success! "); break; } default: break; } return 0; }