SYSCALL API EDR Hooking

Hi Hacker

There is a long discussion on the EDR evasion techniques available in many communities. I am interested in writing this in my blog and making this become very easy to understand. Now let see how EDRs do their analyses by hijacking the API Syscall flow. I am using Bitdefender for this tutorial

Initially if you run the application without bitdefender, the function will look like below. Normal syscall stub will usually start with this four bytes 4c 8b d1 b8

When the EDR is installed on the operating system then it will do inline patching into windows syscall stub. Lets take an example of NtAdjustPrivilegesToken that will look like this after bitdefender is installed

So the idea of EDR inline patching into syscall stub is to make the application API call come into the EDR detection engine first before the actual API process is served so that the EDR place a jump statement at the very beginning of every syscall like the image above

,

A tale of EDR bypass methods | S3cur3Th1sSh1t

Below is simple illustration before and after of EDR hook. NtReadVirtualMemory starts with opcodes e9 0f 64 f8 rather than 4c 8b d1 b8, meaning it’s most likely hooked
NtWriteVirtualMemory starts with opcodes 4c 8b d1 b8, meaning it has not been hooked

Below is a C code to check the EDR hook.

#include <iostream>
#include <Windows.h>

int main()
{
	PDWORD functionAddress = (PDWORD)0;

	// Get ntdll base address
	HMODULE libraryBase = LoadLibraryA("ntdll");

	PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)libraryBase;
	PIMAGE_NT_HEADERS imageNTHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)libraryBase + dosHeader->e_lfanew);

	// Locate export address table
	DWORD_PTR exportDirectoryRVA = imageNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
	PIMAGE_EXPORT_DIRECTORY imageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)libraryBase + exportDirectoryRVA);

	// Offsets to list of exported functions and their names
	PDWORD addresOfFunctionsRVA = (PDWORD)((DWORD_PTR)libraryBase + imageExportDirectory->AddressOfFunctions);
	PDWORD addressOfNamesRVA = (PDWORD)((DWORD_PTR)libraryBase + imageExportDirectory->AddressOfNames);
	PWORD addressOfNameOrdinalsRVA = (PWORD)((DWORD_PTR)libraryBase + imageExportDirectory->AddressOfNameOrdinals);

	int iterator = 0;

	// Iterate through exported functions of ntdll
	for (DWORD i = 0; i < imageExportDirectory->NumberOfNames; i++)
	{
		// Resolve exported function name
		DWORD functionNameRVA = addressOfNamesRVA[i];
		DWORD_PTR functionNameVA = (DWORD_PTR)libraryBase + functionNameRVA;
		char* functionName = (char*)functionNameVA;

		// Find the exported function from the DLL
		DWORD_PTR functionAddressRVA = 0;
		functionAddressRVA = addresOfFunctionsRVA[addressOfNameOrdinalsRVA[i]];
		functionAddress = (PDWORD)((DWORD_PTR)libraryBase + functionAddressRVA);

		// Good Syscal should start with these 4 bytes 
		char syscallPrologue[4] = { 0x4c, 0x8b, 0xd1, 0xb8 };

		// Only interested in Nt|Zw functions
		if (strncmp(functionName, (char*)"Nt", 2) == 0 || strncmp(functionName, (char*)"Zw", 2) == 0)
		{
			// Get the first 4 bytes of the function
			if (memcmp(functionAddress, syscallPrologue, 4) != 0) {
				printf("Rio find hook here: %s : %p\n", functionName, functionAddress);
				iterator++;
			}
		}
		else {
			if (memcmp(functionAddress, syscallPrologue, 4) == 0) {
				printf("------ > Rio cannot find hook at this API: %s : %p\n", functionName, functionAddress);
			}
		}
	}

	printf("No Hooked Syscall %d", iterator);

	return 0;
}

The output of the above application is below

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s