Code injection via undocumented Native API functions. Simple C++ example.
Hello, cybersecurity enthusiasts and white hackers!
In the previous posts I wrote about DLL injection via undocumented NtCreateThreadEx and NtAllocateVirtualMemory.
The following post is a result of self-research of malware development technique which is interaction with the undocumented Native API.
Today I tried to replace another function
OpenProcess with undocumented Native API function
First of all, let’s take a look at function
__kernel_entry NTSYSCALLAPI NTSTATUS NtOpenProcess( [out] PHANDLE ProcessHandle, [in] ACCESS_MASK DesiredAccess, [in] POBJECT_ATTRIBUTES ObjectAttributes, [in, optional] PCLIENT_ID ClientId );
Here it is worth paying attention to the
ObjectAttributes - a pointer to an OBJECT_ATTRIBUTES structure that specifies the attributes to apply to the process object handle. This has to be defined and initialized prior to opening the handle.
ClientId - a pointer to a client ID that identifies the thread whose process is to be opened.
In order to use
NtOpenProcess function, we have to define its definition in our code:
PCLIENT_ID need to be defined. These structures are defined under NT Kernel header files.
We can run
WinDBG in local kernel mode and run:
There is one more caveat. Before returning the handle by the
NtOpenProcess function/ routine, the Object Attributes need to be initialized which can be applied to the handle. To initialize the Object Attributes an
IntitializeObjectAttributes macro is defined and invoked which specifies the properties of an object handle to routines that open handles.
Then, loading the
ntdll.dll library to invoke
And then get starting addresses of the our functions:
And finally open process:
And otherwise the main logic is the same.
As shown in this code, the Windows API call
OpenProcess can be replaced with Native API call function
NtOpenProcess. But we need to define the structures which are defined in the NT kernel header files.
The downside to this method is that the function is undocumented so it may change in the future.
Let’s go to see our simple malware in action. Compile
x86_64-w64-mingw32-g++ hack.cpp -o hack.exe -mconsole -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive
Then, run process hacker 2:
For example, the highlighted process
mspaint.exe is our victim.
Let’s run our simple malware:
As you can see our
meow-meow messagebox is popped-up.
Let’s go to investigate properties of our victim process
As you can see, our
meow-meow payload successfully injected as expected!
As you can see the main logic is the same with previous NT API function call techniques but there is a caveat with defining the structures and associated parameters. Without defining this structures the code will not run.
The reason why it’s good to have this technique in your arsenal is because we are not using
OpenProcess which is more popular and suspicious and which is more closely investigated by the blue teamers.
Let’s go to upload our new
hack.exe with encrypted command to Virustotal (13.12.2021):
So, 5 of 65 AV engines detect our file as malicious.
If we want, for better result, we can add payload encryption with key or obfuscate functions, or combine both of this techniques.
I hope this post spreads awareness to the blue teamers of this interesting technique, and adds a weapon to the red teamers arsenal.
WinDBG kernel debugging
source code in Github
This is a practical case for educational purposes only.
Thanks for your time and good bye!
PS. All drawings and screenshots are mine