NTSTATUS NtQueryInformationProcess(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL);
Wird in ProcessInformationClass der Wert ProcessBasicInformation sowie in ProcessHandle ein gültiges Handle auf einen Prozeß übergeben, füllt NtQueryInformationProcess() eine PROCESS_BASIC_INFORMATION-Struktur aus, deren Adresse im Parameter ProcessInformation steht. Im Member InheritedFromUniqueProcessId der PROCESS_BASIC_INFORMATION-Struktur ist nach einem erfolgreichen Aufruf die ID des Prozesses eingetragen, der den durch ProcessHandle spezifizierten Prozeß erzeugt hat.
Um die Prozeß-ID des besitzenden Prozesses zu ermitteln, muß man nur einen Zeiger auf die Funktion NtQueryInformationProcess() erzeugen und diese Funktion mit den korrekten Parametern aufrufen. Als Hülle dafür wird folgend die Service-Funktion GetParentProcessID() implementiert, die als Parameter die ID des vorgegebenen Prozesses erwartet und zu diesem die ID des besitzenden Prozesses als Funktionsergebnis returniert bzw. im Fehlerfall den Wert 0xffffffff.
DWORD GetParentProcessID(DWORD dwPID)
{
NTSTATUS ntStatus;
DWORD dwParentPID =0xffffffff;
HANDLE hProcess;
PROCESS_BASIC_INFORMATION pbi;
ULONG ulRetLen;
Den Einsprungpunkt für die Funktion NtQueryInformationProcess() erzeugt das Makro CREATE_DYNFUNC_5(), auf das weiter unten eingegangen wird.
CREATE_DYNFUNC_5(NtQueryInformationProcess,
NtQueryInformationProcess,
ntdll,
NTSTATUS,
__stdcall,
HANDLE,
PROCESSINFOCLASS,
PVOID,
ULONG,
PULONG);
Das Handle des Prozesses liefert:
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,
FALSE, dwPID);
Da dieser Aufruf fehlschlagen kann, wenn ein ungültiges Prozeßhandle in dwPID übergeben wird oder aber nicht ausreichende Zugriffsrechte vorliegen, ist das Handle zu verifizieren, bevor darauf zurückgegriffen werden darf.
if (!hProcess)
return 0xffffffff;
Über den zuvor per Makro erzeugten Einsprungpunkt können jetzt die Informationen ermittelt werden.
ntStatus = NtQueryInformationProcess(
hProcess,
ProcessBasicInformation,
(void*) &pbi,
sizeof(PROCESS_BASIC_INFORMATION),
&ulRetLen);
Im Erfolgsfall wird die ID des Erzeugers kopiert...
if (!ntStatus)
dwParentPID = pbi.InheritedFromUniqueProcessId;
...der Prozeß geschlossen...
CloseHandle(hProcess);
...und das Ergebnis returniert.
return dwParentPID;
}
Der auf den ersten Blick vielleicht etwas ungewohnte Codeblock ist der Aufruf der Funktion CREATE_DYNFUNC_5(), die de facto ein Makro ist, mit dessen Hilfe das Definieren und laden einer Funktion mit den richtigen Typendeklarationen leichter vonstatten geht. Das Anhängsel „_5“ zeigt an, daß die Funktion 5 Parameter übernehmen soll, deren Typen in den Parametern ab Index 5 spezifiziert sind.
In den ersten drei Parametern werden der Name des neuen Zeigers sowie der Name der Funktion in der externen Bibliothek sowie der Name der Bibliothek, die üblicherweise eine DLL ist, selbst übergeben.
Das Makro ist wie folgt definiert:
#define DYNLOADED_FPTR( ptrname, procname, dllname)\
FPTR_##procname ptrname = \
( FPTR_##procname ) GetProcAddress ( GetModuleHandle (\
_TEXT( #dllname)), #procname);
#define CREATE_DYNFUNC_5( ptrname, procname, dllname, rettype, \
callconv, a1, a2, a3, a4, a5)\
typedef rettype (callconv *FPTR_##procname) (a1,a2,a3,a4,a5);\
DYNLOADED_FPTR( ptrname, procname, dllname);
Im oben gezeigten Beispiel expandiert das Makro zu:
|