This is a simple program that checks whether image relocations will be applied in kernel or user mode for a particular DLL. Requires PHNT headers to compile.
Last active
December 1, 2021 08:29
-
-
Save diversenok/72292f6123103b79c6e349500cd4d82d to your computer and use it in GitHub Desktop.
Testing image relocations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <phnt_windows.h> | |
| #define PHNT_VERSION PHNT_21H1 | |
| #include <phnt.h> | |
| #include <stdio.h> | |
| #define HEADER_FIELD(NtHeaders, Field) (((NtHeaders)->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) \ | |
| ? ((PIMAGE_NT_HEADERS64)(NtHeaders))->OptionalHeader.Field \ | |
| : ((PIMAGE_NT_HEADERS32)(NtHeaders))->OptionalHeader.Field) | |
| typedef struct _IMAGE_BASE_RELOCATION_ENTRY | |
| { | |
| USHORT Offset : 12; | |
| USHORT Type : 4; | |
| } IMAGE_BASE_RELOCATION_ENTRY, *PIMAGE_BASE_RELOCATION_ENTRY; | |
| int wmain(int argc, wchar_t* argv[]) | |
| { | |
| NTSTATUS status; | |
| HANDLE hFile = NULL; | |
| HANDLE hImageSection = NULL; | |
| HANDLE hDataSection = NULL; | |
| PVOID dllBase = NULL; | |
| PVOID dataBase = NULL; | |
| UNICODE_STRING fileName = { 0 }; | |
| wprintf_s(L"A demo for verifying where image relocations happen by diversenok.\r\n\r\n"); | |
| if (argc < 2) | |
| { | |
| wprintf_s(L"Usage: TestRelocs.exe [Filename]\r\n"); | |
| return STATUS_INVALID_PARAMETER; | |
| } | |
| // Convert the name to NT format | |
| status = RtlDosPathNameToNtPathName_U_WithStatus( | |
| argv[1], | |
| &fileName, | |
| NULL, | |
| NULL | |
| ); | |
| if (!NT_SUCCESS(status)) | |
| { | |
| wprintf_s(L"RtlDosPathNameToNtPathName_U_WithStatus failed with %x\r\n", status); | |
| goto CLEANUP; | |
| } | |
| // Open the target DLL | |
| OBJECT_ATTRIBUTES objAttrib; | |
| IO_STATUS_BLOCK isb; | |
| InitializeObjectAttributes(&objAttrib, &fileName, OBJ_CASE_INSENSITIVE, NULL, NULL); | |
| status = NtOpenFile( | |
| &hFile, | |
| FILE_READ_DATA | SYNCHRONIZE, | |
| &objAttrib, | |
| &isb, | |
| FILE_SHARE_READ, | |
| FILE_SYNCHRONOUS_IO_NONALERT | |
| ); | |
| if (!NT_SUCCESS(status)) | |
| { | |
| wprintf_s(L"NtOpenFile failed with %x\r\n", status); | |
| goto CLEANUP; | |
| } | |
| // Enable mandatory ASLR | |
| PROCESS_MITIGATION_POLICY_INFORMATION mitigation = { 0 }; | |
| mitigation.Policy = ProcessASLRPolicy; | |
| mitigation.ASLRPolicy.EnableForceRelocateImages = TRUE; | |
| mitigation.ASLRPolicy.EnableBottomUpRandomization = TRUE; | |
| mitigation.ASLRPolicy.EnableHighEntropy = TRUE; | |
| NtSetInformationProcess( | |
| NtCurrentProcess(), | |
| ProcessMitigationPolicy, | |
| &mitigation, | |
| sizeof(mitigation) | |
| ); | |
| // Create an image section form the DLL | |
| status = NtCreateSection( | |
| &hImageSection, | |
| SECTION_ALL_ACCESS, | |
| NULL, | |
| NULL, | |
| PAGE_READONLY, | |
| SEC_IMAGE, | |
| hFile | |
| ); | |
| if (!NT_SUCCESS(status)) | |
| { | |
| wprintf_s(L"NtCreateSection for image failed with %x\r\n", status); | |
| goto CLEANUP; | |
| } | |
| // Map the DLL as an image | |
| SIZE_T dllSize; | |
| dllSize = 0; | |
| status = NtMapViewOfSection( | |
| hImageSection, | |
| NtCurrentProcess(), | |
| &dllBase, | |
| 0, | |
| 0, | |
| NULL, | |
| &dllSize, | |
| ViewShare, | |
| 0, | |
| PAGE_READONLY | |
| ); | |
| if (!NT_SUCCESS(status)) | |
| { | |
| wprintf_s(L"NtMapViewOfSection for image failed with %x\r\n", status); | |
| goto CLEANUP; | |
| } | |
| // Create a regular section form the DLL | |
| status = NtCreateSection( | |
| &hDataSection, | |
| SECTION_ALL_ACCESS, | |
| NULL, | |
| NULL, | |
| PAGE_READONLY, | |
| SEC_COMMIT, | |
| hFile | |
| ); | |
| if (!NT_SUCCESS(status)) | |
| { | |
| wprintf_s(L"NtCreateSection for data failed with %x\r\n", status); | |
| goto CLEANUP; | |
| } | |
| // Map the DLL as an regular file | |
| SIZE_T dataSize; | |
| dataSize = 0; | |
| status = NtMapViewOfSection( | |
| hDataSection, | |
| NtCurrentProcess(), | |
| &dataBase, | |
| 0, | |
| 0, | |
| NULL, | |
| &dataSize, | |
| ViewShare, | |
| 0, | |
| PAGE_READONLY | |
| ); | |
| if (!NT_SUCCESS(status)) | |
| { | |
| wprintf_s(L"NtMapViewOfSection for data failed with %x\r\n", status); | |
| goto CLEANUP; | |
| } | |
| // Find the NT header for the image | |
| PIMAGE_NT_HEADERS imageNtHeader; | |
| status = RtlImageNtHeaderEx(0, dllBase, dllSize, &imageNtHeader); | |
| if (!NT_SUCCESS(status)) | |
| { | |
| wprintf_s(L"RtlImageNtHeaderEx for the image failed with %x\r\n", status); | |
| goto CLEANUP; | |
| } | |
| // Find image relocation table | |
| IMAGE_DATA_DIRECTORY relocDirectory; | |
| relocDirectory = HEADER_FIELD(imageNtHeader, DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]); | |
| if (!relocDirectory.Size) | |
| { | |
| wprintf_s(L"The image does not have relocations.\r\n"); | |
| status = STATUS_ILLEGAL_DLL_RELOCATION; | |
| goto CLEANUP; | |
| } | |
| // Find the first block of relocations | |
| PIMAGE_BASE_RELOCATION relocEntry; | |
| relocEntry = RtlOffsetToPointer(dllBase, relocDirectory.VirtualAddress); | |
| PVOID targetPage; | |
| targetPage = RtlOffsetToPointer(dllBase, relocEntry->VirtualAddress); | |
| PIMAGE_BASE_RELOCATION_ENTRY typeOffsets; | |
| typeOffsets = RtlOffsetToPointer(relocEntry, sizeof(IMAGE_BASE_RELOCATION)); | |
| // Save RVA for the first relocation | |
| ULONG firstRelocationRVA; | |
| firstRelocationRVA = relocEntry->VirtualAddress + typeOffsets->Offset; | |
| // Find the NT header for the file | |
| PIMAGE_NT_HEADERS fileNtHeader; | |
| status = RtlImageNtHeaderEx(0, dataBase, dataSize, &fileNtHeader); | |
| if (!NT_SUCCESS(status)) | |
| { | |
| wprintf_s(L"RtlImageNtHeaderEx for the file failed with %x\r\n", status); | |
| goto CLEANUP; | |
| } | |
| // Expand RVA when mapped as data | |
| PSIZE_T firstRelocationInData; | |
| firstRelocationInData = RtlAddressInSectionTable(fileNtHeader, dataBase, firstRelocationRVA); | |
| if (!firstRelocationInData) | |
| { | |
| wprintf_s(L"Error parsing the DLL\r\n"); | |
| status = STATUS_INVALID_IMAGE_FORMAT; | |
| goto CLEANUP; | |
| } | |
| wprintf_s(L"The original ImageBase of the file is 0x%zx\r\n", fileNtHeader->OptionalHeader.ImageBase); | |
| wprintf_s(L"The first relocation is at RVA %x\r\n", firstRelocationRVA); | |
| wprintf_s(L"The origial content at the relocation is 0x%zx\r\n\r\n", *firstRelocationInData); | |
| wprintf_s(L"Mapped SEC_IMAGE at %p\r\n", dllBase); | |
| wprintf_s(L"The ImageBase we got when mapped as SEC_IMAGE is 0x%zx\r\n", imageNtHeader->OptionalHeader.ImageBase); | |
| wprintf_s(L"Memory at first relocation when mapped as SEC_IMAGE is 0x%zx\r\n", *(PSIZE_T)RtlOffsetToPointer(dllBase, firstRelocationRVA)); | |
| CLEANUP: | |
| if (fileName.Buffer) | |
| RtlFreeUnicodeString(&fileName); | |
| if (hFile) | |
| NtClose(hFile); | |
| if (hImageSection) | |
| NtClose(hImageSection); | |
| if (dllBase) | |
| NtUnmapViewOfSection(NtCurrentProcess(), dllBase); | |
| if (hDataSection) | |
| NtClose(hDataSection); | |
| if (dataBase) | |
| NtUnmapViewOfSection(NtCurrentProcess(), dataBase); | |
| return status; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment