Skip to content

Instantly share code, notes, and snippets.

@diversenok
Last active December 1, 2021 08:29
Show Gist options
  • Select an option

  • Save diversenok/72292f6123103b79c6e349500cd4d82d to your computer and use it in GitHub Desktop.

Select an option

Save diversenok/72292f6123103b79c6e349500cd4d82d to your computer and use it in GitHub Desktop.
Testing image relocations

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.

#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