/* * Process Hacker - * mapped image * * Copyright (C) 2010 wj32 * * This file is part of Process Hacker. * * Process Hacker is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Process Hacker is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Process Hacker. If not, see . */ /* * This file contains functions to load and retrieve information for image files (exe, dll). The * file format for image files is explained in the PE/COFF specification located at: * * http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx */ #include #include #include VOID PhpMappedImageProbe( _In_ PPH_MAPPED_IMAGE MappedImage, _In_ PVOID Address, _In_ SIZE_T Length ); ULONG PhpLookupMappedImageExportName( _In_ PPH_MAPPED_IMAGE_EXPORTS Exports, _In_ PSTR Name ); NTSTATUS PhInitializeMappedImage( _Out_ PPH_MAPPED_IMAGE MappedImage, _In_ PVOID ViewBase, _In_ SIZE_T Size ) { PIMAGE_DOS_HEADER dosHeader; ULONG ntHeadersOffset; MappedImage->ViewBase = ViewBase; MappedImage->Size = Size; dosHeader = (PIMAGE_DOS_HEADER)ViewBase; __try { PhpMappedImageProbe(MappedImage, dosHeader, sizeof(IMAGE_DOS_HEADER)); } __except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } // Check the initial MZ. if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) return STATUS_INVALID_IMAGE_NOT_MZ; // Get a pointer to the NT headers and probe it. ntHeadersOffset = (ULONG)dosHeader->e_lfanew; if (ntHeadersOffset == 0) return STATUS_INVALID_IMAGE_FORMAT; if (ntHeadersOffset >= 0x10000000 || ntHeadersOffset >= Size) return STATUS_INVALID_IMAGE_FORMAT; MappedImage->NtHeaders = (PIMAGE_NT_HEADERS)PTR_ADD_OFFSET(ViewBase, ntHeadersOffset); __try { PhpMappedImageProbe( MappedImage, MappedImage->NtHeaders, FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) ); PhpMappedImageProbe( MappedImage, MappedImage->NtHeaders, FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + MappedImage->NtHeaders->FileHeader.SizeOfOptionalHeader + MappedImage->NtHeaders->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER) ); } __except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } // Check the signature and verify the magic. if (MappedImage->NtHeaders->Signature != IMAGE_NT_SIGNATURE) return STATUS_INVALID_IMAGE_FORMAT; MappedImage->Magic = MappedImage->NtHeaders->OptionalHeader.Magic; if ( MappedImage->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC && MappedImage->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC ) return STATUS_INVALID_IMAGE_FORMAT; // Get a pointer to the first section. MappedImage->NumberOfSections = MappedImage->NtHeaders->FileHeader.NumberOfSections; MappedImage->Sections = (PIMAGE_SECTION_HEADER)( ((PCHAR)&MappedImage->NtHeaders->OptionalHeader) + MappedImage->NtHeaders->FileHeader.SizeOfOptionalHeader ); return STATUS_SUCCESS; } NTSTATUS PhLoadMappedImage( _In_opt_ PWSTR FileName, _In_opt_ HANDLE FileHandle, _In_ BOOLEAN ReadOnly, _Out_ PPH_MAPPED_IMAGE MappedImage ) { NTSTATUS status; status = PhMapViewOfEntireFile( FileName, FileHandle, ReadOnly, &MappedImage->ViewBase, &MappedImage->Size ); if (NT_SUCCESS(status)) { status = PhInitializeMappedImage( MappedImage, MappedImage->ViewBase, MappedImage->Size ); if (!NT_SUCCESS(status)) { NtUnmapViewOfSection(NtCurrentProcess(), MappedImage->ViewBase); } } return status; } NTSTATUS PhUnloadMappedImage( _Inout_ PPH_MAPPED_IMAGE MappedImage ) { return NtUnmapViewOfSection( NtCurrentProcess(), MappedImage->ViewBase ); } NTSTATUS PhMapViewOfEntireFile( _In_opt_ PWSTR FileName, _In_opt_ HANDLE FileHandle, _In_ BOOLEAN ReadOnly, _Out_ PVOID *ViewBase, _Out_ PSIZE_T Size ) { NTSTATUS status; BOOLEAN openedFile = FALSE; LARGE_INTEGER size; HANDLE sectionHandle = NULL; SIZE_T viewSize; PVOID viewBase; if (!FileName && !FileHandle) return STATUS_INVALID_PARAMETER_MIX; // Open the file if we weren't supplied a file handle. if (!FileHandle) { status = PhCreateFileWin32( &FileHandle, FileName, ((FILE_EXECUTE | FILE_READ_ATTRIBUTES | FILE_READ_DATA) | (!ReadOnly ? (FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA) : 0)) | SYNCHRONIZE, 0, FILE_SHARE_READ, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ); if (!NT_SUCCESS(status)) return status; openedFile = TRUE; } // Get the file size and create the section. status = PhGetFileSize(FileHandle, &size); if (!NT_SUCCESS(status)) goto CleanupExit; status = NtCreateSection( §ionHandle, SECTION_ALL_ACCESS, NULL, &size, ReadOnly ? PAGE_EXECUTE_READ : PAGE_EXECUTE_READWRITE, SEC_COMMIT, FileHandle ); if (!NT_SUCCESS(status)) goto CleanupExit; // Map the section. viewSize = (SIZE_T)size.QuadPart; viewBase = NULL; status = NtMapViewOfSection( sectionHandle, NtCurrentProcess(), &viewBase, 0, 0, NULL, &viewSize, ViewShare, 0, ReadOnly ? PAGE_EXECUTE_READ : PAGE_EXECUTE_READWRITE ); if (!NT_SUCCESS(status)) goto CleanupExit; *ViewBase = viewBase; *Size = (SIZE_T)size.QuadPart; CleanupExit: if (sectionHandle) NtClose(sectionHandle); if (openedFile) NtClose(FileHandle); return status; } VOID PhpMappedImageProbe( _In_ PPH_MAPPED_IMAGE MappedImage, _In_ PVOID Address, _In_ SIZE_T Length ) { PhProbeAddress(Address, Length, MappedImage->ViewBase, MappedImage->Size, 1); } PIMAGE_SECTION_HEADER PhMappedImageRvaToSection( _In_ PPH_MAPPED_IMAGE MappedImage, _In_ ULONG Rva ) { ULONG i; for (i = 0; i < MappedImage->NumberOfSections; i++) { if ( (Rva >= MappedImage->Sections[i].VirtualAddress) && (Rva < MappedImage->Sections[i].VirtualAddress + MappedImage->Sections[i].SizeOfRawData) ) { return &MappedImage->Sections[i]; } } return NULL; } PVOID PhMappedImageRvaToVa( _In_ PPH_MAPPED_IMAGE MappedImage, _In_ ULONG Rva, _Out_opt_ PIMAGE_SECTION_HEADER *Section ) { PIMAGE_SECTION_HEADER section; section = PhMappedImageRvaToSection(MappedImage, Rva); if (!section) return NULL; if (Section) *Section = section; return (PVOID)( (ULONG_PTR)MappedImage->ViewBase + (Rva - section->VirtualAddress) + section->PointerToRawData ); } BOOLEAN PhGetMappedImageSectionName( _In_ PIMAGE_SECTION_HEADER Section, _Out_writes_opt_z_(Count) PSTR Buffer, _In_ ULONG Count, _Out_opt_ PULONG ReturnCount ) { BOOLEAN result; SIZE_T returnCount; result = PhCopyBytesZ( Section->Name, IMAGE_SIZEOF_SHORT_NAME, Buffer, Count, &returnCount ); if (ReturnCount) *ReturnCount = (ULONG)returnCount; return result; } NTSTATUS PhGetMappedImageDataEntry( _In_ PPH_MAPPED_IMAGE MappedImage, _In_ ULONG Index, _Out_ PIMAGE_DATA_DIRECTORY *Entry ) { if (MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { PIMAGE_OPTIONAL_HEADER32 optionalHeader; optionalHeader = (PIMAGE_OPTIONAL_HEADER32)&MappedImage->NtHeaders->OptionalHeader; if (Index >= optionalHeader->NumberOfRvaAndSizes) return STATUS_INVALID_PARAMETER_2; *Entry = &optionalHeader->DataDirectory[Index]; } else if (MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { PIMAGE_OPTIONAL_HEADER64 optionalHeader; optionalHeader = (PIMAGE_OPTIONAL_HEADER64)&MappedImage->NtHeaders->OptionalHeader; if (Index >= optionalHeader->NumberOfRvaAndSizes) return STATUS_INVALID_PARAMETER_2; *Entry = &optionalHeader->DataDirectory[Index]; } else { return STATUS_INVALID_PARAMETER; } return STATUS_SUCCESS; } FORCEINLINE NTSTATUS PhpGetMappedImageLoadConfig( _In_ PPH_MAPPED_IMAGE MappedImage, _In_ USHORT Magic, _In_ ULONG ProbeLength, _Out_ PVOID *LoadConfig ) { NTSTATUS status; PIMAGE_DATA_DIRECTORY entry; PVOID loadConfig; if (MappedImage->Magic != Magic) return STATUS_INVALID_PARAMETER; status = PhGetMappedImageDataEntry(MappedImage, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &entry); if (!NT_SUCCESS(status)) return status; loadConfig = PhMappedImageRvaToVa(MappedImage, entry->VirtualAddress, NULL); if (!loadConfig) return STATUS_INVALID_PARAMETER; __try { PhpMappedImageProbe(MappedImage, loadConfig, ProbeLength); } __except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } *LoadConfig = loadConfig; return STATUS_SUCCESS; } NTSTATUS PhGetMappedImageLoadConfig32( _In_ PPH_MAPPED_IMAGE MappedImage, _Out_ PIMAGE_LOAD_CONFIG_DIRECTORY32 *LoadConfig ) { return PhpGetMappedImageLoadConfig( MappedImage, IMAGE_NT_OPTIONAL_HDR32_MAGIC, sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32), LoadConfig ); } NTSTATUS PhGetMappedImageLoadConfig64( _In_ PPH_MAPPED_IMAGE MappedImage, _Out_ PIMAGE_LOAD_CONFIG_DIRECTORY64 *LoadConfig ) { return PhpGetMappedImageLoadConfig( MappedImage, IMAGE_NT_OPTIONAL_HDR64_MAGIC, sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64), LoadConfig ); } NTSTATUS PhLoadRemoteMappedImage( _In_ HANDLE ProcessHandle, _In_ PVOID ViewBase, _Out_ PPH_REMOTE_MAPPED_IMAGE RemoteMappedImage ) { NTSTATUS status; IMAGE_DOS_HEADER dosHeader; ULONG ntHeadersOffset; IMAGE_NT_HEADERS32 ntHeaders; ULONG ntHeadersSize; RemoteMappedImage->ViewBase = ViewBase; status = NtReadVirtualMemory( ProcessHandle, ViewBase, &dosHeader, sizeof(IMAGE_DOS_HEADER), NULL ); if (!NT_SUCCESS(status)) return status; // Check the initial MZ. if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) return STATUS_INVALID_IMAGE_NOT_MZ; // Get a pointer to the NT headers and read it in for some basic information. ntHeadersOffset = (ULONG)dosHeader.e_lfanew; if (ntHeadersOffset == 0 || ntHeadersOffset >= 0x10000000) return STATUS_INVALID_IMAGE_FORMAT; status = NtReadVirtualMemory( ProcessHandle, PTR_ADD_OFFSET(ViewBase, ntHeadersOffset), &ntHeaders, sizeof(IMAGE_NT_HEADERS32), NULL ); if (!NT_SUCCESS(status)) return status; // Check the signature and verify the magic. if (ntHeaders.Signature != IMAGE_NT_SIGNATURE) return STATUS_INVALID_IMAGE_FORMAT; RemoteMappedImage->Magic = ntHeaders.OptionalHeader.Magic; if ( RemoteMappedImage->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC && RemoteMappedImage->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC ) return STATUS_INVALID_IMAGE_FORMAT; // Get the real size and read in the whole thing. RemoteMappedImage->NumberOfSections = ntHeaders.FileHeader.NumberOfSections; ntHeadersSize = FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + ntHeaders.FileHeader.SizeOfOptionalHeader + RemoteMappedImage->NumberOfSections * sizeof(IMAGE_SECTION_HEADER); if (ntHeadersSize > 1024 * 1024) // 1 MB return STATUS_INVALID_IMAGE_FORMAT; RemoteMappedImage->NtHeaders = PhAllocate(ntHeadersSize); status = NtReadVirtualMemory( ProcessHandle, PTR_ADD_OFFSET(ViewBase, ntHeadersOffset), RemoteMappedImage->NtHeaders, ntHeadersSize, NULL ); if (!NT_SUCCESS(status)) { PhFree(RemoteMappedImage->NtHeaders); return status; } RemoteMappedImage->Sections = (PIMAGE_SECTION_HEADER)( (PCHAR)RemoteMappedImage->NtHeaders + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + ntHeaders.FileHeader.SizeOfOptionalHeader ); return STATUS_SUCCESS; } NTSTATUS PhUnloadRemoteMappedImage( _Inout_ PPH_REMOTE_MAPPED_IMAGE RemoteMappedImage ) { PhFree(RemoteMappedImage->NtHeaders); return STATUS_SUCCESS; } NTSTATUS PhGetMappedImageExports( _Out_ PPH_MAPPED_IMAGE_EXPORTS Exports, _In_ PPH_MAPPED_IMAGE MappedImage ) { NTSTATUS status; PIMAGE_EXPORT_DIRECTORY exportDirectory; Exports->MappedImage = MappedImage; // Get a pointer to the export directory. status = PhGetMappedImageDataEntry( MappedImage, IMAGE_DIRECTORY_ENTRY_EXPORT, &Exports->DataDirectory ); if (!NT_SUCCESS(status)) return status; exportDirectory = PhMappedImageRvaToVa( MappedImage, Exports->DataDirectory->VirtualAddress, NULL ); if (!exportDirectory) return STATUS_INVALID_PARAMETER; __try { PhpMappedImageProbe(MappedImage, exportDirectory, sizeof(IMAGE_EXPORT_DIRECTORY)); } __except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } Exports->ExportDirectory = exportDirectory; Exports->NumberOfEntries = exportDirectory->NumberOfFunctions; // Get pointers to the various tables and probe them. Exports->AddressTable = (PULONG)PhMappedImageRvaToVa( MappedImage, exportDirectory->AddressOfFunctions, NULL ); Exports->NamePointerTable = (PULONG)PhMappedImageRvaToVa( MappedImage, exportDirectory->AddressOfNames, NULL ); Exports->OrdinalTable = (PUSHORT)PhMappedImageRvaToVa( MappedImage, exportDirectory->AddressOfNameOrdinals, NULL ); if ( !Exports->AddressTable || !Exports->NamePointerTable || !Exports->OrdinalTable ) return STATUS_INVALID_PARAMETER; __try { PhpMappedImageProbe( MappedImage, Exports->AddressTable, exportDirectory->NumberOfFunctions * sizeof(ULONG) ); PhpMappedImageProbe( MappedImage, Exports->NamePointerTable, exportDirectory->NumberOfNames * sizeof(ULONG) ); PhpMappedImageProbe( MappedImage, Exports->OrdinalTable, exportDirectory->NumberOfFunctions * sizeof(USHORT) ); } __except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } // The ordinal and name tables are parallel. // Getting an index into the name table (e.g. by doing a binary // search) and indexing into the ordinal table will produce the // ordinal for that name, *unbiased* (unlike in the specification). // The unbiased ordinal is an index into the address table. return STATUS_SUCCESS; } NTSTATUS PhGetMappedImageExportEntry( _In_ PPH_MAPPED_IMAGE_EXPORTS Exports, _In_ ULONG Index, _Out_ PPH_MAPPED_IMAGE_EXPORT_ENTRY Entry ) { PSTR name; if (Index >= Exports->ExportDirectory->NumberOfFunctions) return STATUS_PROCEDURE_NOT_FOUND; Entry->Ordinal = Exports->OrdinalTable[Index] + (USHORT)Exports->ExportDirectory->Base; if (Index < Exports->ExportDirectory->NumberOfNames) { name = PhMappedImageRvaToVa( Exports->MappedImage, Exports->NamePointerTable[Index], NULL ); if (!name) return STATUS_INVALID_PARAMETER; // TODO: Probe the name. Entry->Name = name; } else { Entry->Name = NULL; } return STATUS_SUCCESS; } NTSTATUS PhGetMappedImageExportFunction( _In_ PPH_MAPPED_IMAGE_EXPORTS Exports, _In_opt_ PSTR Name, _In_opt_ USHORT Ordinal, _Out_ PPH_MAPPED_IMAGE_EXPORT_FUNCTION Function ) { ULONG rva; if (Name) { ULONG index; index = PhpLookupMappedImageExportName(Exports, Name); if (index == -1) return STATUS_PROCEDURE_NOT_FOUND; Ordinal = Exports->OrdinalTable[index] + (USHORT)Exports->ExportDirectory->Base; } Ordinal -= (USHORT)Exports->ExportDirectory->Base; if (Ordinal >= Exports->ExportDirectory->NumberOfFunctions) return STATUS_PROCEDURE_NOT_FOUND; rva = Exports->AddressTable[Ordinal]; if ( (rva >= Exports->DataDirectory->VirtualAddress) && (rva < Exports->DataDirectory->VirtualAddress + Exports->DataDirectory->Size) ) { // This is a forwarder RVA. Function->ForwardedName = PhMappedImageRvaToVa( Exports->MappedImage, rva, NULL ); if (!Function->ForwardedName) return STATUS_INVALID_PARAMETER; // TODO: Probe the name. Function->Function = NULL; } else { Function->Function = PhMappedImageRvaToVa( Exports->MappedImage, rva, NULL ); Function->ForwardedName = NULL; } return STATUS_SUCCESS; } NTSTATUS PhGetMappedImageExportFunctionRemote( _In_ PPH_MAPPED_IMAGE_EXPORTS Exports, _In_opt_ PSTR Name, _In_opt_ USHORT Ordinal, _In_ PVOID RemoteBase, _Out_ PVOID *Function ) { ULONG rva; if (Name) { ULONG index; index = PhpLookupMappedImageExportName(Exports, Name); if (index == -1) return STATUS_PROCEDURE_NOT_FOUND; Ordinal = Exports->OrdinalTable[index] + (USHORT)Exports->ExportDirectory->Base; } Ordinal -= (USHORT)Exports->ExportDirectory->Base; if (Ordinal >= Exports->ExportDirectory->NumberOfFunctions) return STATUS_PROCEDURE_NOT_FOUND; rva = Exports->AddressTable[Ordinal]; if ( (rva >= Exports->DataDirectory->VirtualAddress) && (rva < Exports->DataDirectory->VirtualAddress + Exports->DataDirectory->Size) ) { // This is a forwarder RVA. Not supported for remote lookup. return STATUS_NOT_SUPPORTED; } else { *Function = PTR_ADD_OFFSET(RemoteBase, rva); } return STATUS_SUCCESS; } ULONG PhpLookupMappedImageExportName( _In_ PPH_MAPPED_IMAGE_EXPORTS Exports, _In_ PSTR Name ) { LONG low; LONG high; LONG i; if (Exports->ExportDirectory->NumberOfNames == 0) return -1; low = 0; high = Exports->ExportDirectory->NumberOfNames - 1; do { PSTR name; INT comparison; i = (low + high) / 2; name = PhMappedImageRvaToVa( Exports->MappedImage, Exports->NamePointerTable[i], NULL ); if (!name) return -1; // TODO: Probe the name. comparison = strcmp(Name, name); if (comparison == 0) return i; else if (comparison < 0) high = i - 1; else low = i + 1; } while (low <= high); return -1; } NTSTATUS PhGetMappedImageImports( _Out_ PPH_MAPPED_IMAGE_IMPORTS Imports, _In_ PPH_MAPPED_IMAGE MappedImage ) { NTSTATUS status; PIMAGE_DATA_DIRECTORY dataDirectory; PIMAGE_IMPORT_DESCRIPTOR descriptor; ULONG i; Imports->MappedImage = MappedImage; Imports->Flags = 0; status = PhGetMappedImageDataEntry( MappedImage, IMAGE_DIRECTORY_ENTRY_IMPORT, &dataDirectory ); if (!NT_SUCCESS(status)) return status; descriptor = PhMappedImageRvaToVa( MappedImage, dataDirectory->VirtualAddress, NULL ); if (!descriptor) return STATUS_INVALID_PARAMETER; Imports->DescriptorTable = descriptor; // Do a scan to determine how many import descriptors there are. i = 0; __try { while (TRUE) { PhpMappedImageProbe(MappedImage, descriptor, sizeof(IMAGE_IMPORT_DESCRIPTOR)); if (descriptor->OriginalFirstThunk == 0 && descriptor->FirstThunk == 0) break; descriptor++; i++; } } __except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } Imports->NumberOfDlls = i; return STATUS_SUCCESS; } NTSTATUS PhGetMappedImageImportDll( _In_ PPH_MAPPED_IMAGE_IMPORTS Imports, _In_ ULONG Index, _Out_ PPH_MAPPED_IMAGE_IMPORT_DLL ImportDll ) { ULONG i; if (Index >= Imports->NumberOfDlls) return STATUS_INVALID_PARAMETER_2; ImportDll->MappedImage = Imports->MappedImage; ImportDll->Flags = Imports->Flags; if (!(ImportDll->Flags & PH_MAPPED_IMAGE_DELAY_IMPORTS)) { ImportDll->Descriptor = &Imports->DescriptorTable[Index]; ImportDll->Name = PhMappedImageRvaToVa( ImportDll->MappedImage, ImportDll->Descriptor->Name, NULL ); if (!ImportDll->Name) return STATUS_INVALID_PARAMETER; // TODO: Probe the name. if (ImportDll->Descriptor->OriginalFirstThunk) { ImportDll->LookupTable = PhMappedImageRvaToVa( ImportDll->MappedImage, ImportDll->Descriptor->OriginalFirstThunk, NULL ); } else { ImportDll->LookupTable = PhMappedImageRvaToVa( ImportDll->MappedImage, ImportDll->Descriptor->FirstThunk, NULL ); } } else { ImportDll->DelayDescriptor = &((PImgDelayDescr)Imports->DelayDescriptorTable)[Index]; ImportDll->Name = PhMappedImageRvaToVa( ImportDll->MappedImage, ((PImgDelayDescr)ImportDll->DelayDescriptor)->rvaDLLName, NULL ); if (!ImportDll->Name) return STATUS_INVALID_PARAMETER; // TODO: Probe the name. ImportDll->LookupTable = PhMappedImageRvaToVa( ImportDll->MappedImage, ((PImgDelayDescr)ImportDll->DelayDescriptor)->rvaINT, NULL ); } if (!ImportDll->LookupTable) return STATUS_INVALID_PARAMETER; // Do a scan to determine how many entries there are. i = 0; if (ImportDll->MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { PULONG entry; entry = (PULONG)ImportDll->LookupTable; __try { while (TRUE) { PhpMappedImageProbe( ImportDll->MappedImage, entry, sizeof(ULONG) ); if (*entry == 0) break; entry++; i++; } } __except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } } else if (ImportDll->MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { PULONG64 entry; entry = (PULONG64)ImportDll->LookupTable; __try { while (TRUE) { PhpMappedImageProbe( ImportDll->MappedImage, entry, sizeof(ULONG64) ); if (*entry == 0) break; entry++; i++; } } __except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } } else { return STATUS_INVALID_PARAMETER; } ImportDll->NumberOfEntries = i; return STATUS_SUCCESS; } NTSTATUS PhGetMappedImageImportEntry( _In_ PPH_MAPPED_IMAGE_IMPORT_DLL ImportDll, _In_ ULONG Index, _Out_ PPH_MAPPED_IMAGE_IMPORT_ENTRY Entry ) { PIMAGE_IMPORT_BY_NAME importByName; if (Index >= ImportDll->NumberOfEntries) return STATUS_INVALID_PARAMETER_2; if (ImportDll->MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { ULONG entry; entry = ((PULONG)ImportDll->LookupTable)[Index]; // Is this entry using an ordinal? if (entry & IMAGE_ORDINAL_FLAG32) { Entry->Name = NULL; Entry->Ordinal = (USHORT)IMAGE_ORDINAL32(entry); return STATUS_SUCCESS; } else { importByName = PhMappedImageRvaToVa( ImportDll->MappedImage, entry, NULL ); } } else if (ImportDll->MappedImage->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { ULONG64 entry; entry = ((PULONG64)ImportDll->LookupTable)[Index]; // Is this entry using an ordinal? if (entry & IMAGE_ORDINAL_FLAG64) { Entry->Name = NULL; Entry->Ordinal = (USHORT)IMAGE_ORDINAL64(entry); return STATUS_SUCCESS; } else { importByName = PhMappedImageRvaToVa( ImportDll->MappedImage, (ULONG)entry, NULL ); } } else { return STATUS_INVALID_PARAMETER; } if (!importByName) return STATUS_INVALID_PARAMETER; __try { PhpMappedImageProbe( ImportDll->MappedImage, importByName, sizeof(IMAGE_IMPORT_BY_NAME) ); } __except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } Entry->Name = (PSTR)importByName->Name; Entry->NameHint = importByName->Hint; // TODO: Probe the name. return STATUS_SUCCESS; } NTSTATUS PhGetMappedImageDelayImports( _Out_ PPH_MAPPED_IMAGE_IMPORTS Imports, _In_ PPH_MAPPED_IMAGE MappedImage ) { NTSTATUS status; PIMAGE_DATA_DIRECTORY dataDirectory; PImgDelayDescr descriptor; ULONG i; Imports->MappedImage = MappedImage; Imports->Flags = PH_MAPPED_IMAGE_DELAY_IMPORTS; status = PhGetMappedImageDataEntry( MappedImage, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, &dataDirectory ); if (!NT_SUCCESS(status)) return status; descriptor = PhMappedImageRvaToVa( MappedImage, dataDirectory->VirtualAddress, NULL ); if (!descriptor) return STATUS_INVALID_PARAMETER; Imports->DelayDescriptorTable = descriptor; // Do a scan to determine how many import descriptors there are. i = 0; __try { while (TRUE) { PhpMappedImageProbe(MappedImage, descriptor, sizeof(ImgDelayDescr)); if (descriptor->rvaIAT == 0 && descriptor->rvaINT == 0) break; descriptor++; i++; } } __except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } Imports->NumberOfDlls = i; return STATUS_SUCCESS; } USHORT PhCheckSum( _In_ ULONG Sum, _In_reads_(Count) PUSHORT Buffer, _In_ ULONG Count ) { while (Count--) { Sum += *Buffer++; Sum = (Sum >> 16) + (Sum & 0xffff); } Sum = (Sum >> 16) + Sum; return (USHORT)Sum; } ULONG PhCheckSumMappedImage( _In_ PPH_MAPPED_IMAGE MappedImage ) { ULONG checkSum; USHORT partialSum; PUSHORT adjust; partialSum = PhCheckSum(0, (PUSHORT)MappedImage->ViewBase, (ULONG)(MappedImage->Size + 1) / 2); // This is actually the same for 32-bit and 64-bit executables. adjust = (PUSHORT)&MappedImage->NtHeaders->OptionalHeader.CheckSum; // Subtract the existing check sum (with carry). partialSum -= partialSum < adjust[0]; partialSum -= adjust[0]; partialSum -= partialSum < adjust[1]; partialSum -= adjust[1]; checkSum = partialSum + (ULONG)MappedImage->Size; return checkSum; }