本文最后更新于:2021-11-22 晚上
内存直接加载运行
内存直接加载运行就是,模拟PE加载器的功能,把DLL或者exe等PE文件从内存中直接加载到病毒木马的内存中去运行,不需要通过loadlibrary等现成的API函数去操作。
实现原理
构造一个PE装载器,将PE文件加载到内存中。大致过程,首先要申请一块内存,然后将PE文件按照映像对齐大小映射到内存中;根据重定位表,重定位硬编码数据;获取导入表中的函数及其地址;如果是DLL,获取导出表的相关数据(EXE一般没有导出表);获取入口点的地址,若为EXE,直接跳到入口点即可执行,DLL文件的话还需要构造一个DLLMAIN函数,实现DLL加载。
具体实现
打开文件并且获取大小
| char* FileName = "自己的文件路径";
HANDLE hFile = CreateFileA(FileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);
DWORD dwFileSize = GetFileSize(hFile, NULL);
PBYTE pData = new BYTE[dwFileSize]; DWORD dwRet = 0;
ReadFile(hFile, pData, dwFileSize, &dwRet, NULL); CloseHandle(hFile);
|
获取sizeofimage
|
DWORD GetImageSize(LPVOID lpData) { DWORD dwSizeOfImage = 0; PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpData; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG32)pDosHeader + pDosHeader->e_lfanew); dwSizeOfImage = pNtHeaders->OptionalHeader.SizeOfImage; return dwSizeOfImage; }
|
根据获取的sizeofimage,在进程中开辟一个内存块,权限可读可写可执行。
| LPVOID lpBaseAddr = VirtualAlloc(NULL, dwImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); RtlZeroMemory(lpBaseAddr, dwImageSize);
|
加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
BOOL LoadSection(LPVOID lpData, LPVOID lpBaseAddr) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpData; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG32)pDosHeader + pDosHeader->e_lfanew); DWORD dwSizeOfHeaders = pNtHeaders->OptionalHeader.SizeOfHeaders; int NumberOfSections = pNtHeaders->FileHeader.NumberOfSections; PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pNtHeaders + sizeof(IMAGE_NT_HEADERS)); RtlCopyMemory(lpBaseAddr, lpData, dwSizeOfHeaders); LPVOID lpSrc = NULL; LPVOID lpDest = NULL; DWORD dwSizeOfRawData = 0; for (int i = 0; i < NumberOfSections; i++) { if ((pSectionHeader->VirtualAddress == 0) || (pSectionHeader->SizeOfRawData == 0)) { pSectionHeader++; continue; } lpSrc = (LPVOID)((DWORD)lpData + pSectionHeader->PointerToRawData); lpDest = (LPVOID)((DWORD)lpBaseAddr + pSectionHeader->VirtualAddress); dwSizeOfRawData = pSectionHeader->SizeOfRawData; RtlCopyMemory(lpDest, lpSrc, dwSizeOfRawData); pSectionHeader++; } return TRUE; }
|
重定位数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
BOOL DoRelocationTable(LPVOID lpBaseAddr) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBaseAddr; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG32)pDosHeader + pDosHeader->e_lfanew); PIMAGE_BASE_RELOCATION pReloc = (PIMAGE_BASE_RELOCATION)((unsigned long)pDosHeader + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); if ((PVOID)pReloc == (PVOID)pDosHeader) { return TRUE; } while ((pReloc->VirtualAddress + pReloc->SizeOfBlock) != 0) { WORD* pRelocData = (WORD*)((PBYTE)pReloc + sizeof(IMAGE_BASE_RELOCATION)); int nNumberOfReloc = (pReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
for (int i = 0; i < nNumberOfReloc; i++) { if ((DWORD)(pRelocData[i] & 0x0000F000) == 0x00003000) { DWORD* pAddress = (DWORD*)((PBYTE)pDosHeader + pReloc->VirtualAddress + (pRelocData[i] & 0x0FFF)); DWORD dwDelta = (DWORD)pDosHeader - pNtHeaders->OptionalHeader.ImageBase; *pAddress += dwDelta; } } pReloc = (PIMAGE_BASE_RELOCATION)((PBYTE)pReloc + pReloc->SizeOfBlock); } return TRUE; }
|
导入表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
|
BOOL DoImportTable(LPVOID lpBaseAddr) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBaseAddr; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG32)pDosHeader + pDosHeader->e_lfanew); PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDosHeader + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); char* lpDllName = NULL; HMODULE hDll = NULL; PIMAGE_THUNK_DATA lpImportNameArray = NULL; PIMAGE_IMPORT_BY_NAME lpImportByName = NULL; PIMAGE_THUNK_DATA lpImportFuncAddrArray = NULL; FARPROC lpFuncAddress = NULL; DWORD i = 0; while (TRUE) { if (0 == pImportTable->OriginalFirstThunk) { break; } lpDllName = (char*)((DWORD)pDosHeader + pImportTable->Name); hDll = GetModuleHandleA(lpDllName); if (NULL == hDll) { hDll = LoadLibraryA(lpDllName); if (NULL == hDll) { pImportTable++; continue; } } i = 0; lpImportNameArray = (PIMAGE_THUNK_DATA)((DWORD)pDosHeader + pImportTable->OriginalFirstThunk); lpImportFuncAddrArray = (PIMAGE_THUNK_DATA)((DWORD)pDosHeader + pImportTable->FirstThunk); while (TRUE) { if (0 == lpImportNameArray[i].u1.AddressOfData) { break; } lpImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pDosHeader + lpImportNameArray[i].u1.AddressOfData); if (0x80000000 & lpImportNameArray[i].u1.Ordinal) { lpFuncAddress = GetProcAddress(hDll, (LPCSTR)(lpImportNameArray[i].u1.Ordinal & 0x0000FFFF)); } else { lpFuncAddress = GetProcAddress(hDll, (LPCSTR)lpImportByName->Name); } lpImportFuncAddrArray[i].u1.Function = (DWORD)lpFuncAddress; i++; } pImportTable++; } return TRUE; }
|
修改ImageBase
| BOOL SetImage(LPVOID lpBaseAddr) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBaseAddr; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG32)pDosHeader + pDosHeader->e_lfanew); pNtHeaders->OptionalHeader.SizeOfImage = (ULONG32)lpBaseAddr; return TRUE; }
|
获取入口点
如果是EXE,这一步,获取addressOfEntryPoint之后跳到入口点即可直接执行。
| BOOL Entry(LPVOID lpBaseAddr) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBaseAddr; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG32)pDosHeader + pDosHeader->e_lfanew); LPVOID Entry = (LPVOID)((ULONG32)pDosHeader + pNtHeaders->OptionalHeader.AddressOfEntryPoint); __asm { mov eax,Entry jmp eax } }
|
现在来测试一下直接运行一个EXE,测试文件为桌面上的TestProcess.exe。源代码如下:
| #include <stdio.h> int main(){ printf("b1ackie!!!\n"); return 0; }
|
运行程序查看效果,可以看到直接加载运行TestProcess.exe。
若是DLL文件,还需要构造一下DLLMAIN
| BOOL CallDllMain(LPVOID lpBaseAddr) { typedef_DllMain DllMain = NULL; PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBaseAddr; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG32)pDosHeader + pDosHeader->e_lfanew); DllMain = (typedef_DllMain)((ULONG32)pDosHeader + pNtHeaders->OptionalHeader.AddressOfEntryPoint); BOOL bRet = DllMain((HINSTANCE)lpBaseAddr,DLL_PROCESS_ATTACH,NULL); if (bRet == NULL) { printf("构造入口点失败\n"); return bRet; } return bRet; }
|
导出表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
LPVOID GetExFuncAddr(LPVOID lpBaseAddr,char* lpszFuncName) { LPVOID lpFunc = NULL; PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBaseAddr; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG32)pDosHeader + pDosHeader->e_lfanew); PIMAGE_EXPORT_DIRECTORY pExportTable = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pDosHeader + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); char* lpFuncName = NULL; PDWORD lpAddressOfNamesArray = (PDWORD)((DWORD)pDosHeader + pExportTable->AddressOfNames); PWORD lpAddressOfNameOrdinalArray = (PWORD)((DWORD)pDosHeader + pExportTable->AddressOfNameOrdinals); WORD wHint = 0; PDWORD lpAddressOfFuncArray = (PDWORD)((DWORD)pDosHeader + pExportTable->AddressOfFunctions); DWORD dwNumberOfNames = pExportTable->NumberOfNames; for (int i = 0; i < dwNumberOfNames; i++) { lpFuncName = (PCHAR)((DWORD)pDosHeader + lpAddressOfNamesArray[i]); if (strcmpi(lpFuncName, lpszFuncName) == 0) { wHint = lpAddressOfNameOrdinalArray[i]; lpFunc = (LPVOID)((DWORD)pDosHeader + lpAddressOfFuncArray[wHint]); break; } } return lpFunc; }
|
运行加载桌面上的TestDll.dll文件,此DLL导出函数是一个messagebox函数。
参考
参考《Windows黑客编程技术详解》一书