1

I am not really sure how to read this code:

typedef NTSTATUS(NTAPI* QUERYINFORMATIONPROCESS)(
  IN   HANDLE ProcessHandle,
  IN   PROCESSINFOCLASS ProcessInformationClass,
  OUT  PVOID ProcessInformation,
  IN   ULONG ProcessInformationLength,
  OUT  PULONG ReturnLength OPTIONAL
  );

Is NTSTATUS or QUERYINFORMATIONPROCESS the name of the typedef? And, if so, what is the actual type? Is this a function pointer type? The usage is this:

QUERYINFORMATIONPROCESS QueryInformationProcess =
    (QUERYINFORMATIONPROCESS)GetProcAddress(
    hDll, "NtQueryInformationProcess");

  if (QueryInformationProcess)
  {
    NTSTATUS ntStatus = QueryInformationProcess(
      processInformation.hProcess,
      PROCESSINFOCLASS::ProcessBasicInformation,
      &pbi, sizeof(pbi), &uLength);
  […]

This comes from a the "C++ Multithreading Cookbook", which does not explain this piece of code. I appreciate your help!

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Giuliano
  • 53
  • 5

5 Answers5

4
typedef NTSTATUS(NTAPI* QUERYINFORMATIONPROCESS)(
  IN    HANDLE ProcessHandle,
  IN    PROCESSINFOCLASS ProcessInformationClass,
  OUT  PVOID ProcessInformation,
  IN    ULONG ProcessInformationLength,
  OUT  PULONG ReturnLength OPTIONAL
  );

The hard part here is that this is a function pointer:

When declaring a function pointer, to distinguish it from a function returning a pointer, we need parenthesis around it's name:

(*QUERYINFORMATIONPROCESS)

The NTAPI before the pointer is there to tell the compiler the calling convention (how arguments are passed [registers, on stack, order of arguments], how the cleanup of arguments is done, etc).

The remaining stuff is just the return type (NTSTATUS) and the arguments to the function.

A much simpler example is:

 typedef int (*funcptr)(int x); 

Now, that's relatively easy to see that it's a function returning int, and it's a pointer to the function, and it's taking one argument that is int x.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • Thank you for replying. Could you expand a little bit on what the NTAPI is doing there? In addition what are the IN/OUT doing there? – Giuliano Jan 23 '16 at 20:28
  • What do you want to know. It's defining the calling convention for the Windows NT system calls - which is different from "standard" C calling convention - and only for 32-bit systems. In 64-bit machines, it turns into "nothing" (or at least, using a calling convention will have no effect, since there is only one calling convention for normal functions) – Mats Petersson Jan 23 '16 at 20:31
  • I see, so if I were to rewrite this on an operating system like Linux I would not need the IN/OUT or NTAPI, this is a declaration syntax typical of Windows? - Thank you! – Giuliano Jan 23 '16 at 20:33
  • Yes, and the calls to fetch something from a shared library, and the query for processes is completely and utterly different... – Mats Petersson Jan 23 '16 at 20:36
  • In other words, there is NOTHING useful on the page of code you just posted. – Mats Petersson Jan 23 '16 at 20:37
4

Writing it without the Microsoft macros, it's

typedef NTSTATUS (*QUERYINFORMATIONPROCESS)(
  HANDLE ProcessHandle,
  PROCESSINFOCLASS ProcessInformationClass,
  PVOID ProcessInformation,
  ULONG ProcessInformationLength,
  PULONG ReturnLength
  );

which is a function pointer type called "QUERYINFORMATIONPROCESS".

Specifically, it's a pointer to NtQueryInformationProcess, which needs to be loaded from a DLL.

(And it appears the author of your book is having spelling problems.)

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
molbdnilo
  • 64,751
  • 3
  • 43
  • 82
  • 1
    I see, the usage of the NTAPI, IN, OUT is the fact that this is MS macro! Thank you so much! and yes! the author of the book does have spelling problems :) – Giuliano Jan 23 '16 at 20:35
2

There is a rule which helps reading such elaborate type definitions called the Clockwise/Spiral rule. The rule is a three step algorithm, citing the original article the steps are:

  1. Starting with the unknown element, move in a spiral/clockwise direction; when ecountering the following elements replace them with the corresponding english statements:
    • [X] or [] => Array X size of... or Array undefined size of...
    • (type1, type2) => function passing type1 and type2 returning...
    • * => pointer(s) to...
  2. Keep doing this in a spiral/clockwise direction until all tokens have been covered.
  3. Always resolve anything in parenthesis first!

It is generally useful to know which identifier corresponds to a type in order to identify unknown elements (there may be multiple such elements). In your case - knowing that all of (NTSTATUS, NTAPI, HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG) are types.

The article provides useful examples on applying the rule.

Besides, I've found an image in Google which illustrates the rule: Applying the spiral/clockwise rule

Identify unknown elements:

  • void is a type
  • signal, hmm, looks like an unknown element. Besides, it is located to the right of the asterisk and cannot be anything but an variable/typedef name as far as I am concerned
  • int, void - types
  • fp, the second unknown element (remember, I've mentioned earlier that there can be multiple unknown elements)
  • int, int - types

Now start by applying the rule from the leftmost unknown element, namely signal:

  1. Move from signal to the right and encounter the first opening parenthesis: signal is a function taking an int and something elaborate
  2. Let's analyze the second unknown element fp which is also the second argument to signal. Move in a clockwise spiral from fp and encounter the closing parenthesis and then the asterisk: fp is a pointer to something.
  3. Continue moving from *fp and encounter the opening parenthesis: fp is a pointer to a function taking an int and returning something.
  4. Continue moving and encounter void: fp is a pointer to a function taking an int and returning void.
  5. Now that we've decoded the second argument to signal: signal is a function taking an int, and a pointer to a function taking anintand returningvoid`
  6. Continue moving from signal( and encounter the asterisk: signal is a function returning a pointer to...
  7. Continue moving from (* and encounter the rightmost opening parenthesis: signal is a function (I'm omitting the arguments here) returning a pointer to a function taking an int
  8. Finally, encounter void: signal is a function (I'm omitting the arguments here) returning a pointer to a function taking an int and returning void.

P.S. The typedef from your question can be written in modern C++ with the using declaration:

using QUERYINFORMATIONPROCESS = NTSTATUS(NTAPI*)(
  IN   HANDLE ProcessHandle,
  IN   PROCESSINFOCLASS ProcessInformationClass,
  OUT  PVOID ProcessInformation,
  IN   ULONG ProcessInformationLength,
  OUT  PULONG ReturnLength OPTIONAL
  );

It does not change much, but the typedef-name there is more separated from the actual definition and makes it a bit more readable. When writing new code, consider using using instead of typedef :-)

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Hertz
  • 101
  • 1
  • 5
1

It's a typedef on a pointer to a function, called QUERYINFORMATIONPROCESS. this function take 5 parameters, and return a NTSTATUS.

Jérémie B
  • 10,611
  • 1
  • 26
  • 43
1

That would a function pointer typedef, which defines the type QUERYINFORMATIONPROCESS as an alias for this type:

NTSTATUS(NTAPI*)(
  IN   HANDLE ProcessHandle,
  IN   PROCESSINFOCLASS ProcessInformationClass,
  OUT  PVOID ProcessInformation,
  IN   ULONG ProcessInformationLength,
  OUT  PULONG ReturnLength OPTIONAL
  );

When used as in the example,

QUERYINFORMATIONPROCESS QueryInformationProcess =
    (QUERYINFORMATIONPROCESS)GetProcAddress(
    hDll, "NtQueryInformationProcess");

it expands into something like this:

NTSTATUS(NTAPI* QueryInformationProcess)(
  IN   HANDLE ProcessHandle,
  IN   PROCESSINFOCLASS ProcessInformationClass,
  OUT  PVOID ProcessInformation,
  IN   ULONG ProcessInformationLength,
  OUT  PULONG ReturnLength OPTIONAL
  ) = 
    (NTSTATUS(NTAPI*)(
      IN    HANDLE ProcessHandle,
      IN    PROCESSINFOCLASS ProcessInformationClass,
      OUT  PVOID ProcessInformation,
      IN    ULONG ProcessInformationLength,
      OUT  PULONG ReturnLength OPTIONAL
    ))GetProcAddress(hDll, "NtQueryInformationProcess");

(Not 100% sure if that's the correct expansion, I find function pointers awkward in general.)

Function pointer typedefs are used because function pointer syntax is really awkward in and of itself, while function pointer aliases like that use the standard syntax.

For more information on function pointers, see this answer.


Moreover, it's a pointer to a Windows function, which uses standard Windows identifiers (NTSTATUS, NTAPI, IN, OUT, HANDLE, PROCESSINFOCLASS, PVOID, ULONG, and PULONG).

According to Windows files, these identifiers are:

// ntdef.h
typedef __success(return >= 0) LONG NTSTATUS;
// See https://stackoverflow.com/questions/3378622/how-to-understand-the-ntstatus-nt-success-typedef-in-windows-ddk

// WinNT.h
#define NTAPI __stdcall
// __stdcall is a Windows calling convention, used by Visual Studio when compiling Windows code.

typedef void *PVOID;

#ifdef STRICT
    typedef void *HANDLE;
    #if 0 && (_MSC_VER > 1000)
        #define DECLARE_HANDLE(name) struct name##__; typedef struct name##__ *name
    #else
        #define DECLARE_HANDLE(name) struct name##__{int unused;}; typedef struct name##__ *name
    #endif
#else
    typedef PVOID HANDLE;
    #define DECLARE_HANDLE(name) typedef HANDLE name
#endif


// WinDef.h
typedef unsigned long ULONG;
typedef ULONG *PULONG;

#ifndef IN
    #define IN
#endif

#ifndef OUT
    #define OUT
#endif

IN and OUT are used as programmer aids, while the types are to give programmers a consistent interface. Not entirely sure about PROCESSINFOCLASS; it seems to be defined in ntdll.dll, along with NtQueryInformationProcess itself. According to this MSDN thread, it's defined as follows:

private enum PROCESSINFOCLASS : int
{
  ProcessBasicInformation = 0,
  ProcessQuotaLimits,
  ProcessIoCounters,
  ProcessVmCounters,
  ProcessTimes,
  ProcessBasePriority,
  ProcessRaisePriority,
  ProcessDebugPort,
  ProcessExceptionPort,
  ProcessAccessToken,
  ProcessLdtInformation,
  ProcessLdtSize,
  ProcessDefaultHardErrorMode,
  ProcessIoPortHandlers, // Note: this is kernel mode only
  ProcessPooledUsageAndLimits,
  ProcessWorkingSetWatch,
  ProcessUserModeIOPL,
  ProcessEnableAlignmentFaultFixup,
  ProcessPriorityClass,
  ProcessWx86Information,
  ProcessHandleCount,
  ProcessAffinityMask,
  ProcessPriorityBoost,
  MaxProcessInfoClass
} ;

This appears to be outdated, but this site seems to have a more recent version, which appears to be consistent with official MS NtQueryInformationProcess() documentation.

Hope this helps.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459