I recently arrived upon a post on the breakdev blog that talked about a technique for bypassing user-mode hooks that I hadn’t seen before. The writer of the post hadn’t yet written an implementation of this technique (I asked him the other day), so I took it upon myself to write a proof-of-concept. Someone in the comments by the name of Xenocite suggested an alternate way of going about the process, which I will talk about further down.

For those who have not already read the breakdev post, the technique that the poster talked about was bypassing user-mode winapi hooks by loading a completely fresh copy of ntdll.dll. (ntdll.dll specifically because it doesn’t require any imports — all its exported functions are ultimately wrappers for syscalls.) As the post pointed out, it would be possible to load a fresh copy of kernel32.dll or user32.dll, but those would require a bit more code to recursively load all the imports. As a result, loading ntdll.dll is much simpler.

Even though ntdll doesn’t have any imports, one still has to parse the pe format and load everything where it’s supposed to be. This is not difficult, just a bit annoying. This annoyingness can be circumvented, as Xenocite pointed out, by loading the ntdll from the knowndlls global section.

Before I explain how to access the knowndlls section, I need to give a bit of background on windows sections. Right from msdn, a section object is defined as “a section of memory that can be shared. A process can use a section object to share parts of its memory address space (memory sections) with other processes. Section objects also provide the mechanism by which a process can map a file into its memory address space.”

Therefore, you can open “\\KnownDlls\

tdll.dll” with NtOpenSection and then map it into the current process with NtMapViewOfSection. Overall, this is quite straightforward. The undocumented ntapi can be a bit finicky, but after a bit wrangling, the ntapi can be beaten into submission.

The initializing routine goes as follows:

NtOpenSection = ( fnNtOpenSection ) ResolveFunction ( peb_ntdll , " NtOpenSection " ) ; NtMapViewOfSection = ( fnNtMapViewOfSection ) ResolveFunction ( peb_ntdll , " NtMapViewOfSection " ) ; NewRtlInitUnicodeString ( & ntSectionName , section_path ) ; InitializeObjectAttributes ( & ObjAttrs , & ntSectionName , OBJ_CASE_INSENSITIVE , NULL , NULL ) ;

Now, you could use GetModuleHandleW(L"ntdll.dll") to get a handle to ntdll, but GetModuleHandleW could be hooked and it just not as cool as parsing the PEB. That’s technique used in most (all?) windows shellcode.

The code to parse the peb to get a handle to ntdll in c is as follows:

// resolve PEB # ifdef _WIN64 peb_navigator = __readgsqword ( 0x60 ) ; # else # ifdef _WIN32 peb_navigator = __readfsdword ( 0x30 ) ; # endif # endif peb_navigator = ( ULONG_PTR ) ( ( PPEB ) peb_navigator ) - > Ldr ; peb_navigator = ( ULONG_PTR ) ( ( PPEB_LDR_DATA ) peb_navigator ) - > InMemoryOrderModuleList . Flink ; peb_navigator = DEREF ( peb_navigator ) ; # ifdef _WIN64 peb_ntdll = ( HMODULE ) DEREF ( peb_navigator + 0x20 ) ; # else # ifdef _WIN32 peb_ntdll = ( HMODULE ) DEREF ( peb_navigator + 0x10 ) ; # endif # endif

Since this needs to work on both x86 and x64, I can’t use inline asm and have to use compiler intrinsics to access the gs and fs registers.

Here’s a link to the github repository.

If anyone has any suggestions, criticism, or needs help, please post a comment or contact me as per the contact page. Also, if anyone has any ideas for future posts, please let me know.