Skip to content

Help! I need to write code in C! – Part 2: Portable Executable and NT Functions

Preamble

Welcome to part 2 of this writing stuff in C blogpost series. If you haven’t read part 1, I would strongly advise you to start there πŸ™‚ . In part 2 of this series we are going to take a look at Windows Executables also known as Portable Executables or PE’s and the characteristics. As this is not a deep dive into PE’s, I will not go too in depth into this topic, this is about C after all :).

An excellent resource if you wanna learn more about PE is https://0xrick.github.io/win-internals/pe8/ or, if you want expert video tutorial and voice overed explanations about PE files, I highly recommend sektor7 courses. You will not only learn more about the PE file structure but also seriously improve your offensive tool development game. I recommend https://institute.sektor7.net/view/courses/rto-maldev-intermediate/463262-pe-madness/1707143-revisiting-pe-file-format or if you are looking for an in person live class led by industry professionals and want to win a nice and shiny coin, I recommend the new 6 level SANS course: https://www.sans.org/cyber-security-courses/red-team-operations-developing-custom-tools-windows/

RESOURCES! RESOURCES EVERYWHERE! - Buzz and Woody (Toy Story) Meme | Make a  Meme

PE File Format

Every Windows executable (yes, this includes DLL’s as well), has a specific file structure.
There is quite lenghty official documentation available on the MSDN https://docs.microsoft.com/en-us/windows/win32/debug/pe-format in case you are interested. I think it is best to just see a practical use case and if you want to learn more about it follow the links I already mentioned. I will go over a few cool PE sections but again, it’s not the sake of this blogpost series to educate you on all of these items.

There are a lot of PE parsers available online. I highly recommend CFF Explorer and PEBear either is fine and will do the trick. In this blog, I will be using CFF Explorer.
Below is a screenshot of CFF Explorer parsing PE headers from calc.exe

calc.exe X64 overview

Immediately, from the screenshot, we can tell that a PE file has multiple “headers” and “directories”. The PE file format is truly a treasure trove of information and when you first learn about it, it can be quite daunting as there is a lot of ground to cover. What is important to understand for this blogpost series is that the PE file structure is well documented, and can obviously be parsed (else CFF Explorer and PE Bear wouldn’t exist). This means that we can access data from this file format programmatically and interpret it.

If we go take a look at the “File Header” section, we can see that the file header contains an entry called Machine, which has 8664 as a value. This translates to AMD64 as seen in CFF Explorer, but also documented on MSDN https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_file_header

Machine field in the Nt Header of calc x64

Let’s compare this with the SYSWOW64 variant of calculator, which is the x86 version of this program:

machine header of calc x86 – see the difference?

Notice a difference πŸ™‚ ?

Indeed, using the machine flag that resides in the File Header section of the PE file format, we can deduce if the program is a 32 bit or 64 bit program. Which is exactly what we need for our little C program!

Navigating through the PE Maze

mouse maze Memes & GIFs - Imgflip

Alrighty, so you might ask yourself the question at this point, awesome that we have these parsers, but how the heck will I do this myself? How do I get from point A to point B in this complex beast. The answer is pointers and structures πŸ™‚
Small problem though, some parts of the PE data structures are not documented.
At least… not Officially. There are several ways to find undocumented structures online.


A common approach would be to start looking into the ReactOS project on github, grepping through mingw header files on GitHub or and once again thanks to my colleague Santiago for pointing me to this website, using the vergilius project website
On this website you can find undocumented data structures such as _IMAGE_DOS_HEADER

structure definition of the dos header

What is really nice is you also get the offsets and the total size of the structure in the comments.

The e_lfanew entry gives you a pointer to the relative virtual address of the next header. Don’t believe me? See for yourself!

In the below screenshot we see e_lfanew point to E8

e_lfanew points to the first entry in NT header 0E8

Let’s see if we click the “Nt Headers” section if the first entry is indeed at offset E8…

confirmation that I am not a liar

Such amazed, much wow! Indeed, the Nt Headers portion of the PE does start at offset E8!

Next up, the structure of the “NT Headers”, as seen in CFF Explorer

struct definition of nt headers

Indeed, there is a Signature and a File Header and an OptionalHeader. Let’s continue down.
The next structure we are interested in is the File Header structure, we already know that the “machine” data is stored there.

struct definition of file header

Very nice! So we now know that if we somehow can get our hands on the DOS_Header, we can “walk” the structures by following the e_lfanew pointer to land in the Nt Headers, then have the 0x4 offset to get to the _IMAGE_FILE_HEADER start which starts with the Machine field. So knowing that… how the heck do we get to the DOS Header?!

Technically, if you are trying to find the DOS Header of your own process, or any of the modules (read DLLs) loaded into the process, you could get the DOS Header by typecasting the result of the GetModule Handle function like so

PIMAGE_DOS_HEADER DosHeader;
DosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle(NULL);

However that won’t work for our use case, as we would like to figure out the architecture of remote processes, not our own process. So any other ways to deal with this?
(spoiler, the answer is yes)

We could very simply ignore everything we learned so far and call IsWow64Process however there is a problem…

This function will return false if the program is running on x86 systems (as these systems don’t use SYSWOW64, all processes are x86 on an x86 system… duh…

I bet at least one of you is snarking and is like:

ERHM WELL ACTUALLY TRANSVERSE WAVES MOVE PERPENDICULAR TO THE DIRECTION OF  THE WAVE. HOW DID YOU NOT KNOW THAT? HOW DID YOU PASS 6TH GRADE? YOU NEED  TO BE TAUGHT. I OFFER

You can just use the IsWow64Process 2 API call good sir! That will fix the issue as that also returns the machine architecture!
And you would be right, if it was not for this:

MSDN requirements section of iswow64proc2 API call

We want our program to also be able to deduce architecture on Windows machine prior to Win10 and Server 2016 so this API call is of no use to us….

So once again we have to ask ourselves the age old question:
Any other ways to deal with this?
(spoiler, the answer is yes)

NT functions aka more undocumented BS! Woohoo!

This is where things get a little more complicated…
In order to get to the information we need, we will have to call an NT function called NtQueryInformationProcess
The vast majority of Nt functions are not documented (officially anyways) as Microsoft doesn’t like people mucking around, as an offensive tool developer, you will probably want to get acquainted intimately with NT functions as they are the lowest you can go and provide you with syscall function definitions. More about nt functions here:

Anways, I digress… NtQueryInformationProcess is actually documented! w00t!
So if we read the MSDN we need a few things, first of all we need a way to call the function, as this function is an NT function it is not as straightforward as including a header and having access to the function that way as seen in the “Remarks” section, it is recommended to use dynamic linking using GetProcAddress and LoadLibrary

In order to do this, we will need to define a function with the function prototype of NtQueryInformationProcess and later typecast the function pointer to this definition like so:

typedef NTSTATUS (WINAPI* myNtQueryInformationProcess)(HANDLE ProcessHandle, DWORD processInfo, PVOID ProcessInformation, DWORD ProcessInformationLength, DWORD* ReturnLength);

HMODULE hNtdDll = LoadLibrary(L"ntdll.dll");

myNtQueryInformationProcess NtQueryInformationProcess = (myNtQueryInformationProcess)GetProcAddress(hNtdDll, "NtQueryInformationProcess");

Alright so we now know how to call the Nt Function, let’s take a look at what the function actually needs.

The first parameter is a ProcessHandle, we can obtain such a handle using OpenProcess
as we want to be able to get process information and eventually read the processes virtual memory we will need to call the API as follows:

HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);

Next up we have the ProcessInformationClass:

MSDN info about ProcessInformationClass

The InformationClass we are going to use is 0 (ProcessBasicInformation), as this will give us a pointer to the processes PEB (Process Environment Block) – More about that in part 3.

Next, we need to deal with the output that this function gives us, which will be the ProcessBasicInformation structure as we requested those details by specifying 0 as previous parameter. The MSDN gives us the structure definition that we will need in our code.

MSDN structure definition of process_basic_information and some explanations of each field

And then the final two variables required:

final parameters of NTQueryProcessInformation as per MSDN

The first one we can easily get by using the following snippet:

DWORD dwSize = sizeof(PROCESS_BASIC_INFORMATION);

The final parameter is optional so could be NULL. Or if you really wanna do it properly you could define a DWORD dwSizeNeeded = 0

The full code to get a pointer to the PEB structure that holds info about the remote process is as follows (disregarded the best practice to put function definitions in a header file, for readability purposes of this blog)

#include <stdio.h>
#include <Windows.h>

#define RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva)

typedef struct _PROCESS_BASIC_INFORMATION {
	LONG ExitStatus;
	LPVOID PebBaseAddress;
	ULONG_PTR AffinityMask;
	LONG BasePriority;
	ULONG_PTR UniqueProcessId;
	ULONG_PTR InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;

typedef NTSTATUS(WINAPI* myNtQueryInformationProcess)(HANDLE ProcessHandle, DWORD processInfo, PVOID ProcessInformation, DWORD ProcessInformationLength, DWORD* ReturnLength);

NTSTATUS  GetRemotePEB(int pid)
{
	NTSTATUS status; 
	HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
	PROCESS_BASIC_INFORMATION* pbi;
	BOOL success;
	if (!hProcess)
	{
		printf("OpenProcess failed");
		return -1;
	}
	// Try to allocate memory
	DWORD dwSize = sizeof(PROCESS_BASIC_INFORMATION);
	pbi = malloc(dwSize);
	HMODULE hNtdDll = LoadLibrary(L"ntdll.dll");
	myNtQueryInformationProcess NtQueryInformationProcess = (myNtQueryInformationProcess)GetProcAddress(hNtdDll, "NtQueryInformationProcess");
	status = NtQueryInformationProcess(hProcess, 0, pbi, dwSize, NULL);
	if (status)
	{
		printf("NtQueryInformationProcess failed\n");
		return -1;
	}
	printf("PEB pointer is 0x%p\n",pbi->PebBaseAddress);
	return status;
}

int main()
{
	int pid;
	printf("Enter a pid: \n");
	scanf_s("%i", &pid);
	GetRemotePEB(pid);
	return 0;
}

This PEB pointer will form the basis of part 3, where we will use the PEB structure to get the DOS header and ultimately NT header of the remote process.

Conclusion

In this blogpost we have covered multiple slightly advanced topics ranging from an intro to the Portable Executable File structure, as well as how to traverse it and how to find “unofficial” structure definitions.

We also talked about NT functions, what they are and how we can dynamically call them using our own function prototype combined with LoadLibrary and GetProcAddress.
In Part 3 we will dive deeper into the Process Environment Block (PEB) and how we can leverage it to retrieve the DOS header in remote processes and “walk the PE” to retrieve the machine field information that interests us.

We will also address some best practices when writing C code in next blog post as the code above has some “issues” like not closing handles or freeing libraries πŸ™‚ thx Elias for pointing those out to me

Yeah, This Is Big Brain Time - Meming Wiki

Published inTips & Tutorials

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *