If you're reading this, then you've probably wanted to call some
internal ntdll function that isn't exported and easily callable, right?
If no, then let me explain what I mean. If we start the Visual Studio
Command Prompt, we can use the dumpbin command to display all of the
exported functions available in the ntdll.dll library. On the picture
below, we can see that we've passed the /EXPORTS parameter to the
dumpbin command, which dumps all the exported functions and pipes them
into the C:exports.txt file.

Now we can open the C:exports.txt file and observe
what was inputted in the file. We can see some of the contents of the
file presented on the picture below:

Notice that the dumping first presents the DLL file that we're dumping
the function names from, and also lists the number of functions, which
is 1316 in our case. Next are the ordinals, names and their relative
value addresses where the functions can be accessed. The problem with
this is that only some of the functions are actually exported, but some
are hidden and cannot be called directly by the program. If we open the
URL address
http://msdn.microsoft.com/en-us/library/bb432381(v=vs.85).aspx we can
stumble upon the NtOpenFile function that is part of the ntdll.dll
library, but isn't exported by it. Let's dump all the entries from the
C:exports.txt file whose names start with NtOpen.
On the picture below, we can see all of the names that start with
NtOpen, and the NtOpenFile function is among the listed functions.

The problem is that some of the functions cannot be called directly
because they are not exposed and are only used internally by the library
itself. If we want to call those functions nevertheless, we need to
obtain their addresses via the GetProcAddress function and call them
manually. In the following article, we'll take a look how to actually do
that and call some function.

Let's take a look at the following program that calls the NtOpenFile
function directly. Basically, we've initializing some of the arguments
that we have to pass to the NtOpenFile function. We need to construct
the Unicode version of the string, which is why we're using the
RtlInitUnicodeString function to pass the name of the file we're trying
to write to. We also have to call the InitializeObjectAttributes to
initialize the object that we're also passing to the NtOpenFile
function.

The errors mean that the linker wasn't able to compile the executable,
because some symbols were not resolved correctly. This further implies
that the NtOpenFile function cannot be called directly.

Getting the Address with GetProcAddress

The first thing that we need to do when trying to call some internal
function of the ntdll.dll library is to call the GetProcAddress. The
syntax of the function can be obtained from [1] and looks like this:

We need to pass two parameters to the function. The first parameter is a
handle to the DLL module that contains the function we're trying to
find. This is why we must call the GetModuleHandle on the ntdll.dll
library to get the handle to the library. In the second argument, we
need to pass the name of the function of which we're trying to obtain
the address. The actual code that gets the address of the NtOpenFile
function in the ntdll.dll library is the following:

#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <Winternl.h>
int_tmain(intargc,_TCHAR*argv[]){typedefNTSTATUS(__stdcall*NT_OPEN_FILE)(OUTPHANDLEFileHandle,INACCESS_MASKDesiredAccess,INPOBJECT_ATTRIBUTESObjectAttributes,OUTPIO_STATUS_BLOCKIoStatusBlock,INULONGShareAccess,INULONGOpenOptions);NT_OPEN_FILENtOpenFileStruct;/* load the ntdll.dll */PVOIDInfo;HMODULEhModule=LoadLibrary(_T("ntdll.dll"));NtOpenFileStruct=(NT_OPEN_FILE)GetProcAddress(hModule,"NtOpenFile");if(NtOpenFileStruct==NULL){printf("Error: could not find the function NtOpenFile in libraryntdll.dll.");exit(-1);}printf("NtOpenFile is located at 0x%08x in ntdll.dll.n",(unsignedint)NtOpenFileStruct);getchar();return0;}

Once we run the program above, the following will be displayed on the
screen:

Notice that the RVA address 0x000d59e is the same as we already
identified when dumping the functions from ntdll library with the
dumpbin command. This is exactly what we're doing in a program: we're
determining the address of the NtOpenFile function.

But the program above is not easily understandable, so we must further
explain what it does. The typedef line defines a function prototype
NT_OPEN_FILE, which we must later use and cast the address into. If we
don't do this, we only have an address that's pointing to the some
function in memory, but we can't call it directly. We can understand
that line a little better if we take a look at the NtOPenFile prototype
accessible at [2] and can be seen on the picture below:

Do you notice that the typedef is constructed exactly from the
arguments that we pass to the NtOpenFile function? The _Out_ keyword
needs to be changed into OUT, the _In_ needs to be changed into the IN
and the _Inout_ needs to be changed into IN OUT. The names of the
arguments as well as their types remain the same. Let's take a look at
the typedef line again, this time keeping the parameters in their own
lines as follows:

Notice that we must use the "__stdcall *NAME" directive and pass the
same arguments to it as listed in the function that we're trying to
call. When defining a new type *NAME, we need to pass it the same
arguments to make the address of the function callable with the same
arguments. And since we're talking about the same function, we can
understand why this must be so. Remember that this must be done only
because we're dealing with the address of the function that can't be
directly used as a function's pointer to call the needed function. Also
remember that the *NAME is arbitrary and we can choose whatever we
want; it's a good practice to choose the name that resembles the
function that we're trying to call, which is why I choose the name of
NT_OPEN_FILE.

After that, we're calling the function LoadLibrary and passing it the
name of the DLL that we're trying to get address of: actually, this is a
handle to the DLL in the memory. The handle to the ntdll library
together with the name of the function needs to be passed to the
GetProcAddress function to get back the address of the function. At the
same time, we must also cast the pointer to NT_OPEN_FILE to make it a
function pointer. If the GetProcAddress returns NULL, there is no
specified function in the ntdll library; otherwise, we're printing the
address of the function on the screen (as we've already seen in one of
the previous pictures).

Calling the NtOpenFile Function

We've obtained the address of the NtOpenFile function that we can call.
But we still have to take a look at what we actually need to pass to
that function as parameters. Let's present the prototype of the function
again:

Let's describe each of the parameters in turn:

FileHandle: A pointer where the handle for the open file will be
saved.

DesiredAccess: one or many of the following parameters taken from
[3]:

ObjectAttributes: a pointer to another structure that can be
initialized with the InitializeObjectAttributes.

IoStatusBlock: a pointer to the IO_STATUS_BLOCK structure.

ShareAccess: one of the following options taken from [3]:

OpenOptions: options to be applied when opening a file.

From the above description, it's not very easy to do what's requested of
us. To provide a new storage space in memory for FileHandle, we can
simply create a new variable like so:

HANDLEfile;

DesiredAccess can be FILE_WRITE_DATA or something else, depending what
we're trying to do. But the things get complicated with the third
parameter. So far, we know that we must pass a pointer to some structure
as ObjectAttributes. We also know that we can use the
InitializeObjectAttributes function to create such a structure easily.
We'll take a look at this a little bit later. For now, let's continue
with the parameters we must pass to the NtOpenFile function. For
simplicity, we don't really need the IoStatusBlock parameter, so we can
pass NULL. We can use FILE_SHARE_WRITE as the ShareAccess parameter
and last parameter OpenOptions can also be NULL.

The ObjectAttributes Parameter

So far, we've seen that we must pass the ObjectAttributes parameter as a
pointer to the NtOpenFile function. First, we can take a look at the
InitializeObjectAttributes function. This is actually a macro and not a
function, and its prototype can be seen below (taken from [4]):

The first parameter InitializedAttributes accepts the pointer to the
OBJECT_ATTRIBUTES structure. We can create such a structure by
something as simple as the following instruction:

OBJECT_ATTRIBUTESobja;

The ObjectName parameter must be a Unicode string that contains the name
of the file that we want to write to. In our case, this is
"C:/temp.txt," but the problem is that we can't
simply create Unicode strings in WinAPI32. The name of the file can
contain a relative path, but in such a case, the RootDirectory must
specify the rest of the path. But we can also specify the absolute path
in the ObjectName in which case we can pass a NULL value to the
RootDirectory parameter, which simplifies things a little bit. In
Attributes parameter, we must pass one of the following options taken
from [4]:

The SecurityDescriptor parameter can be NULL to accept the default
security for the object.

Creating the Unicode String

The ObjectName parameter passed to the InitializeObjectAttributes macro
needs to be specified in unicode formatting, which complicates things,
because we can't simply embed the name in double quotes as is normally
the case. Let's take a look at the [5] where we can see the syntax of
the UNICODE_STRING structure that is used to define Unicode strings.
The prototype of the function can be seen below:

Do you notice that the UNICODE_STRING and *PUNICODE_STRING are
actually aliases of the _UNICODE_STRING structure? This means that we
can use whichever name we like to define the unicode string. We should
use the RtlInitUnicodeString to initialize the unicode representation of
the string that we need. The prototype of that function can be seen at
[6] and is presented on the picture below:

When the above code is executed, the variable filename will contain
the Unicode representation of the string
"C:/temp.txt ", which is exactly what we want to
achieve. After that we can pass the filename variable to the
InitializeObjectAttributes function. If we want to compile the code
above, we can quickly notice that we get an error like the following:

error LNK2001: unresolved external symbol _RtlInitUnicodeString@8

This means that the _RtlInitUnicodeString function couldn't be
resolved, because the appropriate library wasn't found. To solve this,
we need to add ntdll.lib to the "Additional Dependencies" under the
Linker – Input in the project properties. We can see that on the picture
below:

image14| If the ntdll.lib can't be found, we also need to specify the
right path in the "Additional Library Directories" under the Linker –
General as can be seen below (note that the C:masm32lib directory was
used, which contains the needed library, but in this case you have to
have the Masm32 installed).

The whole code is presented below, but keep in mind that it's not
complete, because there are too many details, which really aren't
relevant here:

#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <winternl.h>
int_tmain(intargc,_TCHAR*argv[]){typedefNTSTATUS(__stdcall*NT_OPEN_FILE)(OUTPHANDLEFileHandle,INACCESS_MASKDesiredAccess,INPOBJECT_ATTRIBUTESObjectAttributes,OUTPIO_STATUS_BLOCKIoStatusBlock,INULONGShareAccess,INULONGOpenOptions);NT_OPEN_FILENtOpenFileStruct;/* load the ntdll.dll */PVOIDInfo;HMODULEhModule=LoadLibrary(_T("ntdll.dll"));NtOpenFileStruct=(NT_OPEN_FILE)GetProcAddress(hModule,"NtOpenFile");if(NtOpenFileStruct==NULL){printf("Error: could not find the function NtOpenFile in libraryntdll.dll.");exit(-1);}printf("NtOpenFile is located at 0x%08x in ntdll.dll.n",(unsignedint)NtOpenFileStruct);/* create the string in the right format */UNICODE_STRINGfilename;RtlInitUnicodeString(&filename,L"C:temp.txt");/* initialize OBJECT_ATTRIBUTES */OBJECT_ATTRIBUTESobja;InitializeObjectAttributes(&obja,&filename,OBJ_CASE_INSENSITIVE,NULL,NULL);/* call NtOpenFile */IO_STATUS_BLOCKiostatusblock;HANDLEfile=NULL;NTSTATUSstat=NtOpenFileStruct(&file,FILE_WRITE_DATA,&obja,NULL,NULL,NULL);if(NT_SUCCESS(stat)){printf("File successfully opened.n");}else{printf("File could not be opened.n");}getchar();return0;}

As you can see the point of the article was presenting how one would go
about calling functions in ntdll DLL library directly. We need to have a
detailed knowledge about Windows internals and we must also often
reference the WinAPI32.