内存写入 隐藏模块
远线程注入dll的局限
注入前
注入后
这个dll赫然在列,当然我们也可以理解,通过GetProcAddress得到了LoadLibrary函数的地址,用这个地址去加载了我们自己的dll,导入表中当然有这个dll的名称。
当我们使用内存写入这种方式后,无法再通过普通的进程查看模块方式找到我们的模块,将更加隐蔽
思路
我们向一个exe中正确的写入数据,如何是正确的:修复重定位表,修复IAT表
为什么修复重定位表?
在虚拟的内存空间中,exe的ImageBase为0x400000,已经被占用,如果我们想写入一个exe或者dll的话,首先exe肯定贴不到0x400000那个位置,dll也不一定可以加载到0x1000000,所以我们需要重定位表来修复一些全局变量,那些固定的值.
为什么修复IAT表?
也许有人有这样的疑问,重定位表中已经包含了IAT表的地址,修复重定位表也就是修复了IAT表的地址,这没有错,问题在于修复的是IAT表的地址,我们知道IAT表里存的是一个地址,地址里面的值才是正在的函数地址,函数地址的值是在运行的时候才会确定下来(这也与重定位有关),而我们运行时系统给我们写的函数地址的值,并不是基于我们希望的那个位置写的,我们希望的位置是在被写入进程中创建空间的首地址,所以这个代码应该在被写入的进程中执行,创建一个线程就可以了.
写入过程
修复IAT表
1 DWORD WINAPI FixIATTable(LPVOID ImageBase) 2 { 3 PIMAGE_DOS_HEADER pDosHeader = NULL; 4 PIMAGE_NT_HEADERS pNTHeader = NULL; 5 PIMAGE_FILE_HEADER pPEHeader = NULL; 6 PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; 7 PIMAGE_SECTION_HEADER pSectionHeader = NULL; 8 PIMAGE_IMPORT_DESCRIPTOR pIMPORT_DESCRIPTOR = NULL; 9 PIMAGE_IMPORT_BY_NAME pImage_IMPORT_BY_NAME = NULL; 10 11 PDWORD OriginalFirstThunk = NULL; 12 PDWORD FirstThunk = NULL; 13 14 PIMAGE_THUNK_DATA pImageThunkData = NULL; 15 16 DWORD Original = 0; 17 18 pDosHeader = (PIMAGE_DOS_HEADER)ImageBase; 19 pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)ImageBase + pDosHeader->e_lfanew); 20 pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4); 21 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); 22 23 //每个导入表的相关信息占20个字节 24 pIMPORT_DESCRIPTOR = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)ImageBase + pOptionHeader->DataDirectory[1].VirtualAddress); 25 26 //这里可以进行while操作,这里while的判断依据为pIMPORT_DESCRIPTOR个数 27 DWORD dwFuncAddr = 0; 28 HMODULE hModule; 29 TCHAR Buffer[100] = {0}; 30 while (pIMPORT_DESCRIPTOR->FirstThunk && pIMPORT_DESCRIPTOR->OriginalFirstThunk) 31 { 32 const char* pModuleAddr = (const char*)((DWORD)ImageBase + (DWORD)pIMPORT_DESCRIPTOR->Name); 33 mbstowcs(Buffer, pModuleAddr, 100); 34 hModule = LoadLibrary(Buffer); 35 printf("%s", (LPCWCHAR)((DWORD)ImageBase + (DWORD)pIMPORT_DESCRIPTOR->Name)); 36 if (hModule == NULL) 37 { 38 printf("hModuleError:%d",::GetLastError()); 39 return 0; 40 } 41 // FirstThunk 指向 IMAGE_THUNK_DATA 结构数组 42 OriginalFirstThunk = (PDWORD)((DWORD)ImageBase + (DWORD)pIMPORT_DESCRIPTOR->OriginalFirstThunk); 43 FirstThunk = (PDWORD)((DWORD)ImageBase + (DWORD)pIMPORT_DESCRIPTOR->FirstThunk); 44 45 while (*OriginalFirstThunk) 46 { 47 if (*OriginalFirstThunk & 0x80000000) { 48 //高位为1 则 除去最高位的值就是函数的导出序号 49 Original = *OriginalFirstThunk & 0xFFF; //去除最高标志位。 50 dwFuncAddr = (DWORD)GetProcAddress(hModule, (PCHAR)Original); 51 } 52 else 53 { 54 //高位不为1 则指向IMAGE_IMPORT_BY_NAME; 55 pImage_IMPORT_BY_NAME = (PIMAGE_IMPORT_BY_NAME)((DWORD)ImageBase + *OriginalFirstThunk); 56 dwFuncAddr = (DWORD)GetProcAddress(hModule, (PCHAR)pImage_IMPORT_BY_NAME->Name); 57 } 58 *FirstThunk = dwFuncAddr; 59 OriginalFirstThunk++; 60 } 61 62 pIMPORT_DESCRIPTOR++; 63 } 64 65 while (TRUE) 66 { 67 MessageBox(NULL, NULL, NULL, NULL); 68 Sleep(3000); 69 } 70 return 1; 71 }
如果修复成功会隔三秒弹出一个MessageBox
注入和修复重定位
1 VOID inject(LPCWSTR InjetName) 2 { 3 DWORD _size = 0; 4 LPVOID pCurrentAddr = NULL; 5 LPVOID pFarAddr = NULL; 6 TCHAR path[MAX_PATH] = { 0 }; 7 HANDLE hprocess = NULL; 8 HANDLE hThread = NULL; 9 //获取当前进程路径 10 ::GetModuleFileName(NULL, path, MAX_PATH); 11 //获取自身模块的基址 12 HMODULE imagebase = ::GetModuleHandle(NULL); 13 //获得sizeofimage 14 PIMAGE_DOS_HEADER pDosHeader = NULL; 15 PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; 16 pDosHeader = (PIMAGE_DOS_HEADER)imagebase; 17 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pDosHeader + pDosHeader->e_lfanew + IMAGE_SIZEOF_FILE_HEADER + 4); 18 _size = pOptionHeader->SizeOfImage; 19 //自身内存声请空间 20 pCurrentAddr = VirtualAlloc(NULL, _size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 21 if (pCurrentAddr == NULL) 22 { 23 printf("pCurrentAddrError"); 24 return; 25 } 26 memcpy(pCurrentAddr, imagebase, _size); 27 //打开要注入的进程 28 hprocess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _getProcessPid(InjetName)); 29 if (hprocess == NULL) 30 { 31 printf("OpenProcessError"); 32 return; 33 } 34 //远程空间申请内存 35 pFarAddr = ::VirtualAllocEx(hprocess, NULL, _size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 36 if (pFarAddr == NULL) 37 { 38 printf("VirtualAllocExError"); 39 return; 40 } 41 //修复重定位表 42 43 if (GetRelocAddr(pCurrentAddr) == 0) 44 { 45 printf("没有重定位表"); 46 return; 47 } 48 PIMAGE_BASE_RELOCATION pRelocationDirectory = (PIMAGE_BASE_RELOCATION)((DWORD)pCurrentAddr + GetRelocAddr(pCurrentAddr)); 49 50 while (pRelocationDirectory->SizeOfBlock != 0 && pRelocationDirectory->VirtualAddress != 0) 51 { 52 DWORD sizeOfWord = (pRelocationDirectory->SizeOfBlock - 8) / 2; 53 PWORD pWord = (PWORD)((DWORD)pRelocationDirectory + 8); 54 for (int i = 0; i < sizeOfWord; i++) 55 { 56 if (*pWord >> 12 != 0) 57 { 58 PDWORD offsetAddr = (PDWORD)(pRelocationDirectory->VirtualAddress + (*pWord & 0xFFF) + (DWORD)pCurrentAddr); 59 *offsetAddr = *offsetAddr + (DWORD)pFarAddr - GetImageBase(imagebase); 60 pWord++; 61 continue; 62 } 63 pWord++; 64 } 65 pRelocationDirectory = (PIMAGE_BASE_RELOCATION)((DWORD)pRelocationDirectory + pRelocationDirectory->SizeOfBlock); 66 } 67 BOOL x = ::WriteProcessMemory(hprocess, pFarAddr, pCurrentAddr, _size, NULL); 68 if (FALSE == x) 69 { 70 printf("WriteProcessMemoryERROR"); 71 return; 72 } 73 //执行修复IAT表里存的值和执行自己的功能 74 DWORD FixNewIATTable = (DWORD)FixIATTable + (DWORD)pFarAddr - (DWORD)imagebase; 75 hThread = ::CreateRemoteThread(hprocess, NULL, 0, (LPTHREAD_START_ROUTINE)FixNewIATTable, pFarAddr, 0, NULL); 76 if (hThread == NULL) 77 { 78 printf("CreateRemoteThreadError"); 79 return; 80 } 81 WaitForSingleObject(hThread, -1); 82 CloseHandle(hThread); 83 }
这里有个细节 74行,我们修复IAT表的函数地址也需要重定位
要用到的其他函数
1 //根据进程名找对应的pid(快照方式) 2 DWORD _getProcessPid(LPCTSTR lpProcessName) 3 { 4 DWORD dwRet = 0; 5 HANDLE hSnapShot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 6 if (hSnapShot == INVALID_HANDLE_VALUE) 7 { 8 printf("\n获得进程快照失败,返回的GetLastError():%d", ::GetLastError()); 9 return dwRet; 10 } 11 12 PROCESSENTRY32 pe32; 13 pe32.dwSize = sizeof(PROCESSENTRY32); 14 ::Process32First(hSnapShot, &pe32); 15 do 16 { 17 if (!lstrcmp(pe32.szExeFile, lpProcessName)) 18 { 19 dwRet = pe32.th32ProcessID; 20 break; 21 } 22 } while (::Process32Next(hSnapShot, &pe32)); 23 ::CloseHandle(hSnapShot); 24 return dwRet; 25 } 26 27 DWORD GetImageBase(LPVOID pFileBuffer) 28 { 29 PIMAGE_DOS_HEADER pDosHeader = NULL; 30 PIMAGE_NT_HEADERS pNTHeader = NULL; 31 PIMAGE_FILE_HEADER pPEHeader = NULL; 32 PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; 33 PIMAGE_SECTION_HEADER pSectionHeader = NULL; 34 pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; 35 if (*(PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew) != IMAGE_NT_SIGNATURE) 36 { 37 printf("不是有效的PE签名"); 38 free(pFileBuffer); 39 return 0; 40 } 41 pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew); 42 pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4); 43 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); 44 45 return pOptionHeader->ImageBase; 46 47 } 48 DWORD GetRelocAddr(LPVOID pFileBuffer) 49 { 50 PIMAGE_DOS_HEADER pDosHeader = NULL; 51 PIMAGE_NT_HEADERS pNTHeader = NULL; 52 PIMAGE_FILE_HEADER pPEHeader = NULL; 53 PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; 54 PIMAGE_SECTION_HEADER pSectionHeader = NULL; 55 pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; 56 if (*(PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew) != IMAGE_NT_SIGNATURE) 57 { 58 printf("不是有效的PE签名"); 59 free(pFileBuffer); 60 return 0; 61 } 62 pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew); 63 pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4); 64 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); 65 pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); 66 67 return pOptionHeader->DataDirectory[5].VirtualAddress; 68 69 }
代码
// 内存写入,隐藏模块.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include <iostream> #include <windows.h> #include <TlHelp32.h> #include <stdlib.h> #include <comdef.h> DWORD WINAPI FixIATTable(LPVOID ImageBase); DWORD _getProcessPid(LPCTSTR lpProcessName); DWORD GetRelocAddr(LPVOID pFileBuffer); DWORD GetImageBase(LPVOID pFileBuffer); VOID inject(LPCWSTR InjetPath); DWORD WINAPI FixIATTable(LPVOID ImageBase) { PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNTHeader = NULL; PIMAGE_FILE_HEADER pPEHeader = NULL; PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; PIMAGE_SECTION_HEADER pSectionHeader = NULL; PIMAGE_IMPORT_DESCRIPTOR pIMPORT_DESCRIPTOR = NULL; PIMAGE_IMPORT_BY_NAME pImage_IMPORT_BY_NAME = NULL; PDWORD OriginalFirstThunk = NULL; PDWORD FirstThunk = NULL; PIMAGE_THUNK_DATA pImageThunkData = NULL; DWORD Original = 0; pDosHeader = (PIMAGE_DOS_HEADER)ImageBase; pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)ImageBase + pDosHeader->e_lfanew); pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4); pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); //每个导入表的相关信息占20个字节 pIMPORT_DESCRIPTOR = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)ImageBase + pOptionHeader->DataDirectory[1].VirtualAddress); //这里可以进行while操作,这里while的判断依据为pIMPORT_DESCRIPTOR个数 DWORD dwFuncAddr = 0; HMODULE hModule; TCHAR Buffer[100] = {0}; while (pIMPORT_DESCRIPTOR->FirstThunk && pIMPORT_DESCRIPTOR->OriginalFirstThunk) { const char* pModuleAddr = (const char*)((DWORD)ImageBase + (DWORD)pIMPORT_DESCRIPTOR->Name); mbstowcs(Buffer, pModuleAddr, 100); hModule = LoadLibrary(Buffer); printf("%s", (LPCWCHAR)((DWORD)ImageBase + (DWORD)pIMPORT_DESCRIPTOR->Name)); if (hModule == NULL) { printf("hModuleError:%d",::GetLastError()); return 0; } // FirstThunk 指向 IMAGE_THUNK_DATA 结构数组 OriginalFirstThunk = (PDWORD)((DWORD)ImageBase + (DWORD)pIMPORT_DESCRIPTOR->OriginalFirstThunk); FirstThunk = (PDWORD)((DWORD)ImageBase + (DWORD)pIMPORT_DESCRIPTOR->FirstThunk); while (*OriginalFirstThunk) { if (*OriginalFirstThunk & 0x80000000) { //高位为1 则 除去最高位的值就是函数的导出序号 Original = *OriginalFirstThunk & 0xFFF; //去除最高标志位。 dwFuncAddr = (DWORD)GetProcAddress(hModule, (PCHAR)Original); } else { //高位不为1 则指向IMAGE_IMPORT_BY_NAME; pImage_IMPORT_BY_NAME = (PIMAGE_IMPORT_BY_NAME)((DWORD)ImageBase + *OriginalFirstThunk); dwFuncAddr = (DWORD)GetProcAddress(hModule, (PCHAR)pImage_IMPORT_BY_NAME->Name); } *FirstThunk = dwFuncAddr; OriginalFirstThunk++; } pIMPORT_DESCRIPTOR++; } while (TRUE) { MessageBox(NULL, NULL, NULL, NULL); Sleep(3000); } return 1; } //根据进程名找对应的pid(快照方式) DWORD _getProcessPid(LPCTSTR lpProcessName) { DWORD dwRet = 0; HANDLE hSnapShot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapShot == INVALID_HANDLE_VALUE) { printf("\n获得进程快照失败,返回的GetLastError():%d", ::GetLastError()); return dwRet; } PROCESSENTRY32 pe32; pe32.dwSize = sizeof(PROCESSENTRY32); ::Process32First(hSnapShot, &pe32); do { if (!lstrcmp(pe32.szExeFile, lpProcessName)) { dwRet = pe32.th32ProcessID; break; } } while (::Process32Next(hSnapShot, &pe32)); ::CloseHandle(hSnapShot); return dwRet; } DWORD GetImageBase(LPVOID pFileBuffer) { PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNTHeader = NULL; PIMAGE_FILE_HEADER pPEHeader = NULL; PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; PIMAGE_SECTION_HEADER pSectionHeader = NULL; pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; if (*(PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew) != IMAGE_NT_SIGNATURE) { printf("不是有效的PE签名"); free(pFileBuffer); return 0; } pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew); pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4); pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); return pOptionHeader->ImageBase; } DWORD GetRelocAddr(LPVOID pFileBuffer) { PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNTHeader = NULL; PIMAGE_FILE_HEADER pPEHeader = NULL; PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; PIMAGE_SECTION_HEADER pSectionHeader = NULL; pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; if (*(PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew) != IMAGE_NT_SIGNATURE) { printf("不是有效的PE签名"); free(pFileBuffer); return 0; } pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew); pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4); pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); return pOptionHeader->DataDirectory[5].VirtualAddress; } VOID inject(LPCWSTR InjetName) { DWORD _size = 0; LPVOID pCurrentAddr = NULL; LPVOID pFarAddr = NULL; TCHAR path[MAX_PATH] = { 0 }; HANDLE hprocess = NULL; HANDLE hThread = NULL; //获取当前进程路径 ::GetModuleFileName(NULL, path, MAX_PATH); //获取自身模块的基址 HMODULE imagebase = ::GetModuleHandle(NULL); //获得sizeofimage PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; pDosHeader = (PIMAGE_DOS_HEADER)imagebase; pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pDosHeader + pDosHeader->e_lfanew + IMAGE_SIZEOF_FILE_HEADER + 4); _size = pOptionHeader->SizeOfImage; //自身内存声请空间 pCurrentAddr = VirtualAlloc(NULL, _size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (pCurrentAddr == NULL) { printf("pCurrentAddrError"); return; } memcpy(pCurrentAddr, imagebase, _size); //打开要注入的进程 hprocess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _getProcessPid(InjetName)); if (hprocess == NULL) { printf("OpenProcessError"); return; } //远程空间申请内存 pFarAddr = ::VirtualAllocEx(hprocess, NULL, _size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (pFarAddr == NULL) { printf("VirtualAllocExError"); return; } //修复重定位表 if (GetRelocAddr(pCurrentAddr) == 0) { printf("没有重定位表"); return; } PIMAGE_BASE_RELOCATION pRelocationDirectory = (PIMAGE_BASE_RELOCATION)((DWORD)pCurrentAddr + GetRelocAddr(pCurrentAddr)); while (pRelocationDirectory->SizeOfBlock != 0 && pRelocationDirectory->VirtualAddress != 0) { DWORD sizeOfWord = (pRelocationDirectory->SizeOfBlock - 8) / 2; PWORD pWord = (PWORD)((DWORD)pRelocationDirectory + 8); for (int i = 0; i < sizeOfWord; i++) { if (*pWord >> 12 != 0) { PDWORD offsetAddr = (PDWORD)(pRelocationDirectory->VirtualAddress + (*pWord & 0xFFF) + (DWORD)pCurrentAddr); *offsetAddr = *offsetAddr + (DWORD)pFarAddr - GetImageBase(imagebase); pWord++; continue; } pWord++; } pRelocationDirectory = (PIMAGE_BASE_RELOCATION)((DWORD)pRelocationDirectory + pRelocationDirectory->SizeOfBlock); } BOOL x = ::WriteProcessMemory(hprocess, pFarAddr, pCurrentAddr, _size, NULL); if (FALSE == x) { printf("WriteProcessMemoryERROR"); return; } //执行修复IAT表里存的值和执行自己的功能 DWORD FixNewIATTable = (DWORD)FixIATTable + (DWORD)pFarAddr - (DWORD)imagebase; hThread = ::CreateRemoteThread(hprocess, NULL, 0, (LPTHREAD_START_ROUTINE)FixNewIATTable, pFarAddr, 0, NULL); if (hThread == NULL) { printf("CreateRemoteThreadError"); return; } WaitForSingleObject(hThread, -1); CloseHandle(hThread); } int main() { inject(L"Test.exe"); return 0; }
执行效果