AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Tutorials Delphi Handle in Objektnamen konvertieren
Tutorial durchsuchen
Ansicht
Themen-Optionen

Handle in Objektnamen konvertieren

Ein Tutorial von Fridolin Walther · begonnen am 5. Okt 2009 · letzter Beitrag vom 19. Okt 2009
 
Fridolin Walther

Registriert seit: 11. Mai 2008
Ort: Kühlungsborn
446 Beiträge
 
Delphi 2009 Professional
 
#1

Handle in Objektnamen konvertieren

  Alt 5. Okt 2009, 19:05
Hallo,

ich möchte an dieser Stelle auf ein Problem eingehen, das in dieser oder einer ähnlichen Form meiner Ansicht nach zu den mit häufigsten Fragen in diversen Foren zählt: Wie bekomme ich den Dateinamen aus einem dazugehörigen Dateihandle? Bzw. anders: Wie kann ich den Namen des Objektes ermitteln, auf das ein Handle verweist?

Obwohl die Problemstellung trivial klingt, ist sie im Usermode allein nicht sauber zu lösen. Es existiert zwar mit MSDN-Library durchsuchenNtQueryObject eine mehr oder weniger gut dokumentierte Usermode API um Informationen über ein Handle bzw. dem sich dahinter verborgenen Objekt zu erhalten, allerdings ist die Benutzung der API insbesondere mit dem zur Namensauflösung wichtigen ObjectNameInformation Parameter ausgesprochen heikel und problembehaftet. Generell gilt dieses Problem unter ausschließlicher Verwendung von Usermode Code daher als nicht sauber lösbar.

Aus diesem Grunde stelle ich hier eine Kernel Mode Lösung vor, die mit Hilfe von MSDN-Library durchsuchenObReferenceObjectByHandle, MSDN-Library durchsuchenObQueryNameString und MSDN-Library durchsuchen ObDereferenceObject eine saubere Lösung des Problems ermöglicht, ohne dabei Gefahr zu laufen in irgendwelche Locks zu laufen.

Das Herzstück stellt dabei ein kleiner Treiber dar (h2on.sys -> Handle To Object Name). Der vollständige Code des Treibers kann dem Attachment entnommen werden. Ebenfalls enthalten sind 2 vorkompilierte Treiber die mit dem XP x86 DDK (builds\x86) bzw. dem Windows 2003 x64 DDK (builds\x64) compiliert wurden. Um den x64 Treiber aber effektiv nutzen zu können, ist der Besitz eines Code Signing Zertifikats notwendig. Der Treiber ist übrigens in der Lage auf x64 Systemen sowohl Handles von 32bit als auch von 64bit Prozessen aufzulösen. Ebenfalls steht die Auflösung sowohl für 32bit als auch für 64bit Prozesse zur Verfügung. Der Treiber ist also vollständig bitdepth agnostic.

Nachfolgend die Kernel Mode Routine, die das übergebene Handle hObject des Prozesses ulProcessID in den dazugehörigen Objektnamen auflöst inkl. einiger Erklärungen:
Code:
NTSTATUS
H2ONGetObjectNameFromHandle (
    __in_opt       ULONG  ulProcessId,
    __in           HANDLE hObject,
    __inout_opt    PWCHAR objName,
    __in           ULONG  ulBuffSize,
    __out          PULONG pulBuffSizeRequired
    )
{
    NTSTATUS                   ntStatus           = STATUS_SUCCESS;
    PEPROCESS                  pProcess           = NULL;
    KAPC_STATE                 apcState           = {0,};
    BOOLEAN                    bAttached          = FALSE;
    HANDLE                     hProcessId         = 0;
    PVOID                      pObject            = NULL;
    ULONG                      ulInfoBuffSize     = 0;
    ULONG                      ulInfoBuffSizeReq  = 0;
    POBJECT_NAME_INFORMATION   pNameInfo          = NULL;

    if (!hObject)
    {
        return STATUS_INVALID_PARAMETER_2;
    }
    if (!pulBuffSizeRequired)
    {
        return STATUS_INVALID_PARAMETER_5;
    }

    *pulBuffSizeRequired = 0;

    if (ulProcessId)
    {
        /* Der Caller hat eine Process ID angegeben. Aus diesem Grunde müssen wir uns den entsprechenden EPROCESS Pointer besorgen. */

        /*
         * Microsoft hat einer etwas merkwürdige Art mit Process IDs umzugehen. Sie möchten sie zwar grundsätzlich als HANDLE übergeben
         * haben, liefern sie aber stets als ULONG zurück. Um uns hässliche Casts zu ersparen, erstellen wir lieber eine temporäre Variable
         * die die Process ID als HANDLE speichert.
         */

        *((PULONG)(&hProcessId)) = ulProcessId;
       
        ntStatus = PsLookupProcessByProcessId (hProcessId, &pProcess);
        if (!NT_SUCCESS (ntStatus))
        {
            goto cleanup;
        }
    }
    else
    {
        pProcess = PsGetCurrentProcess ();
    }

    /* Wir müssen uns nur mit der übergebenen Prozess ID verbinden, wenn dies nicht bereits unser aktueller Prozess ist. */

    if (PsGetCurrentProcess () != pProcess)
    {
        /*
         * Und da wir in diesem Fall kein Handle unseres eigenen Prozesses auflösen wollen, verbinden wir uns mit dem
         * gewünschten Prozess um unsere Magie wirken zu lassen :).
         */

        KeStackAttachProcess (pProcess, &apcState);
        bAttached = TRUE;
    }

    /* 
     * Jetzt, da wir uns im korrekten Prozesskontext befinden, können wir das Handle endlich auflösen und holen uns eine
     * Referenz zum gewünschten Objekt.
     */
    ntStatus = ObReferenceObjectByHandle (hObject,
                                          0,
                                          NULL,
                                          KernelMode,
                                          &pObject,
                                          NULL);
    if (!NT_SUCCESS (ntStatus))
    {
        goto cleanup;
    }

    /* 
     * Da wir nicht genau wissen wie lang der ObjectName sein wird, raten wir einfach und erstellen vorerst einen Buffer
     * unserer Standardgröße.
     */

    ulInfoBuffSize = H2ON_DEFAULT_BUFF_SIZE;

    do
    {
        pNameInfo = ExAllocatePoolWithTag (PagedPool,
                                           ulInfoBuffSize,
                                           H2ON_TAG);
       
        if (!pNameInfo)
        {
            /* Speicher ist aus wie es scheint, in dem Fall gibts einen entsprechenden Fehlercode zurück und es wird aufgeräumt. */
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
            goto cleanup;
        }

        /* Jetzt versuchen wir uns den Namen des referenzierten Objects zu holen */
        ntStatus = ObQueryNameString (pObject,
                                      pNameInfo,
                                      ulInfoBuffSize,
                                      &ulInfoBuffSizeReq);

        if (ntStatus == STATUS_INFO_LENGTH_MISMATCH)
        {
            /* Falls unser Buffer zu klein war, geben wir den alten Buffer frei und setzen die korrekte Größe für den Buffer. */
            ExFreePoolWithTag (pNameInfo, H2ON_TAG);
            pNameInfo = NULL;
            ulInfoBuffSize = ulInfoBuffSizeReq;
        }
    /* Und das Ganze wird wiederholt, bis die Größe passt :) */
    } while (STATUS_INFO_LENGTH_MISMATCH == ntStatus);

    /* Eine kleine Überprüfung des Rückgabewerts von ObQueryNameString bringt Gewissheit, das wir den korrekten Namen jetzt haben. */
    if (!NT_SUCCESS (ntStatus))
    {
        goto cleanup;
    }

    /* Also gehts los damit den Namen in den Usermode zu schaffen ... zuerst mal schauen ob da genug Platz ist. */
    if ((pNameInfo->Name.Length + sizeof (WCHAR)) > ulBuffSize)
    {
        /* Was in diesem Falle leider nicht der Fall ist. Fehler ausspucken und aufräumen. */
        ntStatus = STATUS_BUFFER_TOO_SMALL;
        *pulBuffSizeRequired = pNameInfo->Name.Length;
        goto cleanup;
    }
    else
    {
        /* Ist in diesem Falle vorhanden, sehr schön. Also wird rüberkopiert. */
        if (objName)
        {
            RtlCopyMemory (objName, pNameInfo->Name.Buffer, pNameInfo->Name.Length);
            RtlCopyMemory (((PUCHAR)objName) + pNameInfo->Name.Length, L"", sizeof (WCHAR));
        }
    }

    /* Und zu guter letzt wird aufgeräumt ... */
cleanup:
    if (pNameInfo)
    {
        ExFreePoolWithTag (pNameInfo, H2ON_TAG);
    }
    if (pObject)
    {
        ObDereferenceObject (pObject);
    }
    if (bAttached)
    {
        KeUnstackDetachProcess (&apcState);
    }
   
    if (pProcess && ulProcessId)
    {
        ObDereferenceObject (pProcess);
    }
    return ntStatus;
}
Nun ist die Kernel Mode Implementierung natürlich nur eine Hälfte der Medallie. Irgendwie möchte man als Programmierer jetzt natürlich auch auf den Treiber zugreifen um seine Handles aufzulösen. Entsprechend kommt der Treiber kombiniert mit einer kleinen Interface Unit (include\h2on.pas), die die Treiber Funktionalität in eine simple API kapselt:
Delphi-Quellcode:
function GetObjectNameFromHandle(dwProcessId: DWORD; hObject: THandle;
  objName: PWideChar; dwBuffSize: DWORD; pdwBuffSizeRequired: PDWORD): DWORD;

{
dwProcessId:
Die Prozess ID des Prozesses dem das aufzulösende Handle gehört. Kann Null sein, wenn ein Handle des eigenen Prozesses aufgelöst werden soll.

hObject:
Das eigentliche Handle.

objName:
Ein Pointer auf einen Buffer, der den aufgelösten Objektnamen erhält.

dwBuffSize:
Die Größe des mit objName übergebenen Buffers in Bytes.

pdwBuffSizeRequired:
Pointer auf ein DWORD, daß die Größe des Objektnamen zurückliefert, falls der mit objName übergebene Buffer zu klein sein sollte.

Rückgabewert:
Falls die Operation erfolgreich gewesen sein sollte, wird ERROR_SUCCESS zurück geliefert. Ansonsten ein entsprechender Fehlercode.
}
Dazu gesellen sich 2 Helferfunktionen um den Treiber im System zu installieren bzw. zu deinstallieren:
Delphi-Quellcode:
function InstallAndStartDriver(): Boolean;
function UninstallAndStopDriver(): Boolean;
Beide Funktionen liefern einen Boolean Wert zurück, der angibt ob der Treiber korrekt installiert und gestartet bzw. gestoppt und deinstalliert wurde.
Angehängte Dateien
Dateityp: rar h2on_740.rar (119,6 KB, 36x aufgerufen)
Fridolin Walther
"While Mr. Kim, by virtue of youth and naiveté, has fallen prey to the inexplicable need for human contact, let me step in and assure you that my research will go on uninterrupted, and that social relationships will continue to baffle and repulse me."
  Mit Zitat antworten Zitat
 


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:41 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz