#include "stdafx.h" #include "DynamicMapping.h" #include // #include #pragma comment(lib, "psapi.lib") LPVOID MapModuleToProcess(HANDLE hProcess, BYTE * dllMemory, bool wipeHeaders) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dllMemory; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDosHeader + pDosHeader->e_lfanew); PIMAGE_SECTION_HEADER pSecHeader = IMAGE_FIRST_SECTION(pNtHeader); if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE || pNtHeader->Signature != IMAGE_NT_SIGNATURE) { return nullptr; } IMAGE_DATA_DIRECTORY relocDir = pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; bool relocatable = (pNtHeader->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0; bool hasRelocDir = pNtHeader->OptionalHeader.NumberOfRvaAndSizes >= IMAGE_DIRECTORY_ENTRY_BASERELOC && relocDir.VirtualAddress > 0 && relocDir.Size > 0; if (!hasRelocDir && (pNtHeader->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)) // A relocation dir is optional, but it must not have been stripped { return nullptr; } ULONG_PTR headersBase = pNtHeader->OptionalHeader.ImageBase; LPVOID preferredBase = relocatable ? nullptr : (LPVOID)headersBase; LPVOID imageRemote = VirtualAllocEx(hProcess, preferredBase, pNtHeader->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); LPVOID imageLocal = VirtualAlloc(nullptr, pNtHeader->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (!imageLocal || !imageRemote) { return nullptr; } // Update the headers to the relocated image base if (relocatable && (ULONG_PTR)imageRemote != pNtHeader->OptionalHeader.ImageBase) pNtHeader->OptionalHeader.ImageBase = (ULONG_PTR)imageRemote; memcpy((LPVOID)imageLocal, (LPVOID)pDosHeader, pNtHeader->OptionalHeader.SizeOfHeaders); SIZE_T imageSize = pNtHeader->OptionalHeader.SizeOfImage; for (WORD i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++) { if (hasRelocDir && i == pNtHeader->FileHeader.NumberOfSections - 1 && pSecHeader->VirtualAddress == relocDir.VirtualAddress && (pSecHeader->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)) imageSize = pSecHeader->VirtualAddress; // Limit the maximum VA to copy to the process to exclude .reloc if it is the last section memcpy((LPVOID)((DWORD_PTR)imageLocal + pSecHeader->VirtualAddress), (LPVOID)((DWORD_PTR)pDosHeader + pSecHeader->PointerToRawData), pSecHeader->SizeOfRawData); pSecHeader++; } if (hasRelocDir) { DWORD_PTR dwDelta = (DWORD_PTR)imageRemote - headersBase; DoBaseRelocation( (PIMAGE_BASE_RELOCATION)((DWORD_PTR)imageLocal + relocDir.VirtualAddress), (DWORD_PTR)imageLocal, dwDelta); } ResolveImports((PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)imageLocal + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress), (DWORD_PTR)imageLocal); SIZE_T skipBytes = wipeHeaders ? pNtHeader->OptionalHeader.SizeOfHeaders : 0; if (WriteProcessMemory(hProcess, (PVOID)((ULONG_PTR)imageRemote + skipBytes), (PVOID)((ULONG_PTR)imageLocal + skipBytes), imageSize - skipBytes, nullptr)) { VirtualFree(imageLocal, 0, MEM_RELEASE); } else { VirtualFree(imageLocal, 0, MEM_RELEASE); VirtualFreeEx(hProcess, imageRemote, 0, MEM_RELEASE); imageRemote = nullptr; } return imageRemote; } bool ResolveImports(PIMAGE_IMPORT_DESCRIPTOR pImport, DWORD_PTR module) { PIMAGE_THUNK_DATA thunkRef; PIMAGE_THUNK_DATA funcRef; while (pImport->FirstThunk) { char * moduleName = (char *)(module + pImport->Name); HMODULE hModule = GetModuleHandleA(moduleName); if (!hModule) { hModule = LoadLibraryA(moduleName); if (!hModule) { return false; } } funcRef = (PIMAGE_THUNK_DATA)(module + pImport->FirstThunk); if (pImport->OriginalFirstThunk) { thunkRef = (PIMAGE_THUNK_DATA)(module + pImport->OriginalFirstThunk); } else { thunkRef = (PIMAGE_THUNK_DATA)(module + pImport->FirstThunk); } while (thunkRef->u1.Function) { if (IMAGE_SNAP_BY_ORDINAL(thunkRef->u1.Function)) { funcRef->u1.Function = (DWORD_PTR)GetProcAddress(hModule, (LPCSTR)IMAGE_ORDINAL(thunkRef->u1.Ordinal)); } else { PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME)(module + thunkRef->u1.AddressOfData); funcRef->u1.Function = (DWORD_PTR)GetProcAddress(hModule, (LPCSTR)thunkData->Name); } if (!funcRef->u1.Function) { MessageBoxA(0, "Function not resolved", moduleName, 0); return false; } thunkRef++; funcRef++; } pImport++; } return true; } void DoBaseRelocation(PIMAGE_BASE_RELOCATION relocation, DWORD_PTR memory, DWORD_PTR dwDelta) { DWORD_PTR * patchAddress; WORD type, offset; while (relocation->VirtualAddress) { PBYTE dest = (PBYTE)(memory + relocation->VirtualAddress); DWORD count = (relocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); WORD * relocInfo = (WORD *)((DWORD_PTR)relocation + sizeof(IMAGE_BASE_RELOCATION)); for (DWORD i = 0; i < count; i++) { type = relocInfo[i] >> 12; offset = relocInfo[i] & 0xfff; switch (type) { case IMAGE_REL_BASED_ABSOLUTE: break; case IMAGE_REL_BASED_HIGHLOW: case IMAGE_REL_BASED_DIR64: patchAddress = (DWORD_PTR *)(dest + offset); *patchAddress += dwDelta; break; default: break; } } relocation = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)relocation + relocation->SizeOfBlock); } } DWORD RVAToOffset(PIMAGE_NT_HEADERS pNtHdr, DWORD dwRVA) { PIMAGE_SECTION_HEADER pSectionHdr = IMAGE_FIRST_SECTION(pNtHdr); for (WORD i = 0; i < pNtHdr->FileHeader.NumberOfSections; i++) { if (pSectionHdr->VirtualAddress <= dwRVA) { if ((pSectionHdr->VirtualAddress + pSectionHdr->Misc.VirtualSize) > dwRVA) { dwRVA -= pSectionHdr->VirtualAddress; dwRVA += pSectionHdr->PointerToRawData; return (dwRVA); } } pSectionHdr++; } return (0); } DWORD GetDllFunctionAddressRVA(BYTE * dllMemory, LPCSTR apiName) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dllMemory; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDosHeader + pDosHeader->e_lfanew); PIMAGE_EXPORT_DIRECTORY pExportDir; DWORD exportDirRVA = pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; if (exportDirRVA == 0) { return 0; } DWORD exportDirOffset = RVAToOffset(pNtHeader, exportDirRVA); pExportDir = (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)dllMemory + exportDirOffset); DWORD * addressOfFunctionsArray = (DWORD *)((DWORD)pExportDir->AddressOfFunctions - exportDirRVA + (DWORD_PTR)pExportDir); DWORD * addressOfNamesArray = (DWORD *)((DWORD)pExportDir->AddressOfNames - exportDirRVA + (DWORD_PTR)pExportDir); WORD * addressOfNameOrdinalsArray = (WORD *)((DWORD)pExportDir->AddressOfNameOrdinals - exportDirRVA + (DWORD_PTR)pExportDir); for (DWORD i = 0; i < pExportDir->NumberOfNames; i++) { char * functionName = (char*)(addressOfNamesArray[i] - exportDirRVA + (DWORD_PTR)pExportDir); if (!_stricmp(functionName, apiName)) { return addressOfFunctionsArray[addressOfNameOrdinalsArray[i]]; } } return 0; } HMODULE GetModuleBaseRemote(HANDLE hProcess, const wchar_t* szDLLName) { DWORD cbNeeded = 0; wchar_t szModuleName[MAX_PATH] = { 0 }; if (EnumProcessModules(hProcess, 0, 0, &cbNeeded)) { HMODULE* hMods = (HMODULE*)malloc(cbNeeded*sizeof(HMODULE)); if (EnumProcessModules(hProcess, hMods, cbNeeded, &cbNeeded)) { for (unsigned int i = 0; i < cbNeeded / sizeof(HMODULE); i++) { szModuleName[0] = 0; if (GetModuleFileNameExW(hProcess, hMods[i], szModuleName, _countof(szModuleName))) { wchar_t* dllName = wcsrchr(szModuleName, L'\\'); if (dllName) { dllName++; if (!_wcsicmp(dllName, szDLLName)) { return hMods[i]; } } } } } free(hMods); } return 0; } /* DWORD StartDllInitFunction(HANDLE hProcess, DWORD_PTR functionAddress, LPVOID imageBase) { NTSTATUS ntStat = 0; DWORD dwExit = 0; HANDLE hThread = 0; t_NtCreateThreadEx _NtCreateThreadEx = (t_NtCreateThreadEx)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtCreateThreadEx"); // if (_NtCreateThreadEx) // { //#define THREAD_ALL_ACCESS_VISTA (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | \ // 0xFFFF) // ntStat = _NtCreateThreadEx(&hThread, THREAD_ALL_ACCESS_VISTA, 0, hProcess, (LPTHREAD_START_ROUTINE)functionAddress, imageBase, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, 0, 0, 0, 0); // } // else { hThread = CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)functionAddress, imageBase, CREATE_SUSPENDED, 0); } if (hThread) { ntStat = NtSetInformationThread(hThread, ThreadHideFromDebugger, 0, 0); //SkipThreadAttach(hProcess, hThread); ResumeThread(hThread); WaitForSingleObject(hThread, INFINITE); GetExitCodeThread(hThread, &dwExit); CloseHandle(hThread); return dwExit; } return -1; } bool SkipThreadAttach(HANDLE hProcess, HANDLE hThread) { USHORT tebFlags = 0; THREAD_BASIC_INFORMATION tbi = { 0 }; if (NtQueryInformationThread(hThread, ThreadBasicInformation, &tbi, sizeof(THREAD_BASIC_INFORMATION), 0) >= 0) { DWORD_PTR tebAddress = (DWORD_PTR)tbi.TebBaseAddress; DWORD_PTR tebFlagAddress = tebAddress + TEB_OFFSET_SAME_TEB_FLAGS; if (ReadProcessMemory(hProcess, (void*)tebFlagAddress, &tebFlags, sizeof(USHORT), 0)) { SameTebFlags * structFlags = (SameTebFlags *)&tebFlags; structFlags->DbgSkipThreadAttach = TRUE; return !!WriteProcessMemory(hProcess, (void*)tebFlagAddress, &tebFlags, sizeof(USHORT), 0); } } return false; } bool StartSystemBreakpointInjection(DWORD threadid, HANDLE hProcess, DWORD_PTR functionAddress, LPVOID imageBase) { CONTEXT ctx = { 0 }; BYTE injectStub[500] = { 0 }; ctx.ContextFlags = CONTEXT_CONTROL; HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, threadid); //wsprintfA(text, "%X %X", hThread, threadi); //MessageBoxA(0,text,"StartSystemBreakpointInjection",0); if (hThread && (NtGetContextThread(hThread, &ctx) >= 0)) { //wsprintfA(text, "%X", ctx.Eip); //MessageBoxA(0,text,"NtGetContextThread",0); LPVOID memory = VirtualAllocEx(hProcess, 0, GetInjectStubSize(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (memory) { //wsprintfA(text, "%X", memory); //MessageBoxA(0, text, text, 0); #ifdef _WIN64 //x64 PrepareInjectStub((DWORD_PTR)memory, (DWORD_PTR)imageBase, ctx.Rip, functionAddress, injectStub); #else //x64 PrepareInjectStub((DWORD_PTR)memory, (DWORD_PTR)imageBase, ctx.Eip, functionAddress, injectStub); #endif //_WIN64 if (WriteProcessMemory(hProcess, memory, injectStub, GetInjectStubSize(), 0)) { #ifdef _WIN64 //x64 ctx.Rip = (DWORD_PTR)memory; #else //x64 ctx.Eip = (DWORD_PTR)memory; #endif //_WIN64 return (NtSetContextThread(hThread, &ctx) >= 0); } } } CloseHandle(hThread); return false; } */ #ifndef _WIN64 //32bit BYTE pushad = 0x60;//PUSHAD BYTE popad = 0x61;//POPAD BYTE pushDword[] = { 0x68, 0x00, 0x00, 0x00, 0x00 }; BYTE callDword[] = { 0xE8, 0x00, 0x00, 0x00, 0x00 }; BYTE jmpDword[] = { 0xE9, 0x00, 0x00, 0x00, 0x00 }; int GetInjectStubSize() { return sizeof(pushDword) + sizeof(callDword) + sizeof(jmpDword) + 2; } void PrepareInjectStub(DWORD memoryAddress, DWORD dllImageBase, DWORD systemBreakpointContinue, DWORD dllInitAddress, BYTE * result) { DWORD * temp = (DWORD *)&pushDword[1]; *temp = dllImageBase; temp = (DWORD *)&callDword[1]; *temp = (DWORD)(dllInitAddress - (memoryAddress + sizeof(pushDword) + 1) - 5); temp = (DWORD *)&jmpDword[1]; *temp = (DWORD)(systemBreakpointContinue - (memoryAddress + sizeof(pushDword) + sizeof(callDword) + 2) - 5); result[0] = pushad; memcpy(result + 1, pushDword, sizeof(pushDword)); memcpy(result + 1 + sizeof(pushDword), callDword, sizeof(callDword)); memcpy(result + 1 + sizeof(pushDword) + sizeof(callDword), &popad, 1); memcpy(result + 1 + sizeof(pushDword) + sizeof(callDword) + 1, jmpDword, sizeof(jmpDword)); } #else //64bit BYTE movRcx[] = { 0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; BYTE callQword[] = { 0xFF, 0x15, 0x00, 0x00, 0x00, 0x00 }; //dll init BYTE jmpQword[] = { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 }; BYTE addressTable[8 * 2] = { 0 }; int GetInjectStubSize() { return sizeof(movRcx)+sizeof(callQword)+sizeof(jmpQword)+sizeof(addressTable); } void PrepareInjectStub(DWORD_PTR memoryAddress, DWORD_PTR dllImageBase, DWORD_PTR systemBreakpointContinue, DWORD_PTR dllInitAddress, BYTE * result) { DWORD_PTR * temp = (DWORD_PTR *)&movRcx[2]; *temp = dllImageBase; temp = (DWORD_PTR *)addressTable; *temp = dllInitAddress; temp++; *temp = systemBreakpointContinue; DWORD * tempDw = (DWORD*)&callQword[2]; *tempDw = sizeof(jmpQword); tempDw = (DWORD*)&jmpQword[2]; *tempDw = sizeof(DWORD_PTR); memcpy(result, movRcx, sizeof(movRcx)); memcpy(result + sizeof(movRcx), callQword, sizeof(callQword)); memcpy(result + sizeof(movRcx)+sizeof(callQword), jmpQword, sizeof(jmpQword)); memcpy(result + sizeof(movRcx)+sizeof(callQword)+sizeof(jmpQword), addressTable, sizeof(addressTable)); } #endif