Calling Into ntdll.dll
After this short detour to the kernel-mode side of an ntdll.dll function call, let's get back to user-mode. As already noted, Microsoft doesn't give away header files that would make interfacing to ntdll.dll a trivial task. Fortunately, the Platform SDK does at least contain an import library for this component. Without this ntdll.lib file, things would be really tough! So the task of interfacing to ntdll.dll consists of the following two steps:
Write a header file that doesn't conflict with the Platform SDK header files, and contains just enough definitions to call the desired ntdll.dll functions. Typically, this requires a couple of constant, type, and prototype definitions.
Put ntdll.lib into your linker's list of import libraries. In Microsoft Visual C/C++ 6.0, you can achieve this manually by selecting Settings from the Project menu, clicking the Link tab, and adding ntdll.lib to the list of Object/library modules. However, it's usually much more convenient to import this library by means of the linker directive #pragma comment (linker, "/defaultlib:ntdll.lib") that should appear somewhere in your application's header files. The latter technique has the advantage that anyone you send your project files to can rebuild the project without having to change the default compiler and linker settings.
It's high time for a real-world example now. So, which Native API function should we call now? We could start with one of the trivial ones, but I think we won't lose any didactic clarity by delving right into the deep water. To me, the most valuable ntdll.dll functions are NtQuerySystemInformation() and NtSetSystemInformation(). Although several built-in administration utilities coming with Windows 2000/NT make heavy use of this function pair, Microsoft almost entirely conceals its existence. What a pity! The life of the Windows 2000/NT developer would be much easier if they didn't. If you have ever used Microsoft's psapi.dll to query internal operating system information[ms]forget about it! You won't like it anymore after finding out about NtQuerySystemInformation() and NtSetSystemInformation().
Among lots of other things, NtQuerySystemInformation() returns lists of currently running processes and threads, loaded system modules, object handles, ERESOURCE locks, and page files. It also identifies your CPU, and gives you a vast amount of statistical information. NtSetSystemInformation() lets you modify various system parameters, and even loads and unloads kernel-mode device drivers for you. But all of this wonderful stuff is undocumented! It seems that Microsoft wants to keep this precious pearl for its own use.
Fortunately, others have replaced the myth surrounding these functions by evidence in the meantime. I'm proud that I can claim to be the first one who published a detailed description of NtQuerySystemInformation() and NtSetSystemInformation(), along with extensive source code, back in November 1999 in Dr. Dobb's Journal [2]. It didn't take long until Gary Nebbett came up with his outstanding Windows NT/2000 Native API Reference [1] that not only documented this function pair, but all functions that are part of the Native API. Great work, Gary! But I don't want to know how many days (and nights) you have gazed at your favorite disassembler's screen...