/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    peb.c

Abstract:

    WinDbg Extension Api

Author:

    Ramon J San Andres (ramonsa) 5-Nov-1993

Environment:

    User Mode.

Revision History:

--*/

#include "precomp.h"
#pragma hdrstop
#include <time.h>

BOOL
GetTeb32FromWowTeb(ULONG64 Teb, PULONG64 pTeb32)
{
    if (pTeb32) {
        return ReadPointer(Teb, pTeb32);
    }
    return FALSE;
}

BOOL
GetPeb32FromWowTeb(ULONG64 Teb, PULONG64 pPeb32)
{
    ULONG Peb32;
    ULONG64 Teb32=0;
    ULONG err;

    if (GetTeb32FromWowTeb(Teb, &Teb32) && Teb32) {
        if (!(err =GetFieldValue(Teb32, "nt!TEB32", "ProcessEnvironmentBlock", Peb32))) {
            *pPeb32 = Peb32;
            return TRUE;
        } else if (err == SYMBOL_TYPE_INFO_NOT_FOUND) {
            if (!(err =GetFieldValue(Teb32, "wow64!TEB32", "ProcessEnvironmentBlock", Peb32))) {
                *pPeb32 = Peb32;
                return TRUE;
            }
        }
    }
    return FALSE;
}

HRESULT
DumpPeb(ULONG64 peb, BOOL IsWow64Peb)
{
    ULONG64 ldr;
    ULONG64 err;
    ULONG64 ldr_Initialized;
    ULONG64 ldr_InInitializationOrderModuleList_Flink;
    ULONG64 ldr_InInitializationOrderModuleList_Blink;
    ULONG64 ldr_InLoadOrderModuleList_Flink;
    ULONG64 ldr_InLoadOrderModuleList_Blink;
    ULONG   ldr_InMemoryOrderModuleList_Offset;
    ULONG64 ldr_InMemoryOrderModuleList_Flink;
    ULONG64 ldr_InMemoryOrderModuleList_Blink;
    ULONG   ldr_DataTableEntry_InMemoryOrderLinks_Offset;
    ULONG64 peb_SubSystemData;
    ULONG64 peb_ProcessHeap;
    ULONG64 peb_ProcessParameters;
    PCHAR   ldrdata = "nt!_PEB_LDR_DATA";
    PCHAR   ldrEntry = "nt!_LDR_DATA_TABLE_ENTRY";
    PCHAR   processparam = "nt!_RTL_USER_PROCESS_PARAMETERS";
    HRESULT hr = S_OK;

    if (IsWow64Peb) {
        // try and load types from nt
        if ( err = InitTypeRead( peb, nt!PEB32 ) )   {
            if (err == SYMBOL_TYPE_INFO_NOT_FOUND) {
                // try load types from wow64
                if ( !( err = InitTypeRead( peb, wow64!PEB32 )) )   {
                    ldrdata = "wow64!_PEB_LDR_DATA32";
                    ldrEntry = "wow64!_LDR_DATA_TABLE_ENTRY32";
                    processparam = "wow64!_RTL_USER_PROCESS_PARAMETERS32";
                } else {
                    dprintf( "error %d InitTypeRead( wow64!PEB32 at %p)...\n", (ULONG) err, peb);
                    return E_INVALIDARG;
                }
            } else {
                dprintf( "error %d InitTypeRead( nt!PEB32 at %p)...\n", (ULONG) err, peb);
                return E_INVALIDARG;
            }
        } else {
            ldrdata = "nt!_PEB_LDR_DATA32";
            ldrEntry = "nt!_LDR_DATA_TABLE_ENTRY32";
            processparam = "nt!_RTL_USER_PROCESS_PARAMETERS32";
        }
    } else {
        if ( err = InitTypeRead( peb, PEB ) )   {
           dprintf( "error %d InitTypeRead( nt!PEB32 at %p)...\n", (ULONG) err, peb);
           return E_INVALIDARG;
        }
    }

    dprintf( 
        "    InheritedAddressSpace:    %s\n"
        "    ReadImageFileExecOptions: %s\n"
        "    BeingDebugged:            %s\n"
        "    ImageBaseAddress:         %p\n"
        "    Ldr                       %p\n",
        ReadField( InheritedAddressSpace )    ? "Yes" : "No",
        ReadField( ReadImageFileExecOptions ) ? "Yes" : "No",
        ReadField( BeingDebugged )            ? "Yes" : "No",
        ReadField( ImageBaseAddress ),
        (ldr = ReadField( Ldr ))
        );

    peb_SubSystemData     = ReadField( SubSystemData );
    peb_ProcessHeap       = ReadField( ProcessHeap );
    peb_ProcessParameters = ReadField( ProcessParameters );

    err = GetFieldOffset( ldrdata,
                          "InMemoryOrderModuleList", 
                          &ldr_InMemoryOrderModuleList_Offset
                        );
    if ( err )   {
        dprintf( "    ***  _PEB_LDR_DATA%s was not found...\n",
                 ( err == FIELDS_DID_NOT_MATCH ) ? ".InMemoryModuleList field" :
                 " type"
               );
    }
    else   {
       err = GetFieldOffset( ldrEntry,
                             "InMemoryOrderLinks", 
                             &ldr_DataTableEntry_InMemoryOrderLinks_Offset
                           );
       if (err )  {
           dprintf( "    ***  _LDR_DATA_TABLE_ENTRY%s was not found...\n",
                    ( err == FIELDS_DID_NOT_MATCH ) ? ".InMemoryOrderLinks field" :
                   " type"
                  );
       }
    }

    if ( err || GetFieldValue( ldr, ldrdata, "Initialized", ldr_Initialized ) )   {
        dprintf( "    *** unable to read Ldr table at %p\n", ldr );
    }
    else  {
        ULONG64 next, head;
        BOOL First = TRUE;

        GetFieldValue( ldr, ldrdata, "InInitializationOrderModuleList.Flink", ldr_InInitializationOrderModuleList_Flink );
        GetFieldValue( ldr, ldrdata, "InInitializationOrderModuleList.Blink", ldr_InInitializationOrderModuleList_Blink );
        GetFieldValue( ldr, ldrdata, "InLoadOrderModuleList.Flink", ldr_InLoadOrderModuleList_Flink );
        GetFieldValue( ldr, ldrdata, "InLoadOrderModuleList.Blink", ldr_InLoadOrderModuleList_Blink );
        GetFieldValue( ldr, ldrdata, "InMemoryOrderModuleList.Flink", ldr_InMemoryOrderModuleList_Flink );
        GetFieldValue( ldr, ldrdata, "InMemoryOrderModuleList.Blink", ldr_InMemoryOrderModuleList_Blink );

        dprintf( 
            "    Ldr.Initialized:          %s\n"
            "    Ldr.InInitializationOrderModuleList: %p . %p\n"
            "    Ldr.InLoadOrderModuleList:           %p . %p\n"
            "    Ldr.InMemoryOrderModuleList:         %p . %p\n",
                  ldr_Initialized ? "Yes" : "No",
                  ldr_InInitializationOrderModuleList_Flink,
                  ldr_InInitializationOrderModuleList_Blink,
                  ldr_InLoadOrderModuleList_Flink,
                  ldr_InLoadOrderModuleList_Blink,
                  ldr_InMemoryOrderModuleList_Flink,
                  ldr_InMemoryOrderModuleList_Blink
               );
        head = ldr + (ULONG64)ldr_InMemoryOrderModuleList_Offset;
        next = ldr_InMemoryOrderModuleList_Flink;
        while( next != head )    {
            ULONG64 entry, dllBase;
            UNICODE_STRING64 u;
            ULONG Timestamp;
            const char *time;

            entry = next - ldr_DataTableEntry_InMemoryOrderLinks_Offset;
            if (GetFieldValue( entry, ldrEntry, "DllBase", dllBase )) {
                dprintf("Cannot read %s at %p\n",ldrEntry, entry);
                break;
            }
            GetFieldValue( entry, ldrEntry, "TimeDateStamp", Timestamp );
            GetFieldValue( entry, ldrEntry, "FullDllName.Buffer", u.Buffer );
            GetFieldValue( entry, ldrEntry, "FullDllName.Length", u.Length );
            GetFieldValue( entry, ldrEntry, "FullDllName.MaximumLength", u.MaximumLength );
            if (First) {
                if (IsPtr64()) {
                    dprintf("                    Base TimeStamp / Module\n");
                } else {
                    dprintf("            Base TimeStamp                     Module\n");
                }
                First = FALSE;
            }
            if (IsPtr64()) {
                dprintf("        ");
            }
            dprintf( "%16p ", dllBase );
            if ((time = ctime((time_t *) &Timestamp)) != NULL) {
                dprintf( "%08x %-20.20s ", Timestamp, time+4);
            }
            if ( u.Buffer )  {
                if (IsPtr64()) {
                    dprintf("\n                         ");
                }
                DumpUnicode64( u );
            }
            dprintf( "\n");
            GetFieldValue( entry, ldrEntry, "InMemoryOrderLinks.Flink", next );

            if (CheckControlC()) {
                break;
            }
        }
    }

    dprintf( 
        "    SubSystemData:     %p\n"
        "    ProcessHeap:       %p\n"
        "    ProcessParameters: %p\n", 
                  peb_SubSystemData,
                  peb_ProcessHeap,
                  peb_ProcessParameters
            );
    if ( peb_ProcessParameters )   { 
        ULONG64 peb_ProcessParameters_Environment;
        ULONG64 peb_ProcessParameters_Flags;
        UNICODE_STRING64 windowTitle;
        UNICODE_STRING64 imagePathName;
        UNICODE_STRING64 commandLine;
        UNICODE_STRING64 dllPath;

        GetFieldValue( peb_ProcessParameters, processparam, "Environment", peb_ProcessParameters_Environment );
        GetFieldValue( peb_ProcessParameters, processparam, "Flags", peb_ProcessParameters_Flags );
        GetFieldValue( peb_ProcessParameters, processparam, "WindowTitle.Buffer", windowTitle.Buffer );
        GetFieldValue( peb_ProcessParameters, processparam, "WindowTitle.Length", windowTitle.Length );
        GetFieldValue( peb_ProcessParameters, processparam, "WindowTitle.MaximumLength", windowTitle.MaximumLength );
        GetFieldValue( peb_ProcessParameters, processparam, "ImagePathName.Buffer", imagePathName.Buffer );
        GetFieldValue( peb_ProcessParameters, processparam, "ImagePathName.Length", imagePathName.Length );
        GetFieldValue( peb_ProcessParameters, processparam, "ImagePathName.MaximumLength", imagePathName.MaximumLength );
        GetFieldValue( peb_ProcessParameters, processparam, "CommandLine.Buffer", commandLine.Buffer );
        GetFieldValue( peb_ProcessParameters, processparam, "CommandLine.Length", commandLine.Length );
        GetFieldValue( peb_ProcessParameters, processparam, "CommandLine.MaximumLength", commandLine.MaximumLength );
        GetFieldValue( peb_ProcessParameters, processparam, "DllPath.Buffer", dllPath.Buffer );
        GetFieldValue( peb_ProcessParameters, processparam, "DllPath.Length", dllPath.Length );
        GetFieldValue( peb_ProcessParameters, processparam, "DllPath.MaximumLength", dllPath.MaximumLength );
        if ( !(peb_ProcessParameters_Flags & RTL_USER_PROC_PARAMS_NORMALIZED) )   {
             windowTitle.Buffer   += peb_ProcessParameters;
             imagePathName.Buffer += peb_ProcessParameters;
             commandLine.Buffer   += peb_ProcessParameters;
             dllPath.Buffer       += peb_ProcessParameters;
        }
        dprintf( 
            "    WindowTitle:  '" );
        DumpUnicode64( windowTitle );
        dprintf("'\n");
        dprintf( 
            "    ImageFile:    '" );
        DumpUnicode64( imagePathName );
        dprintf("'\n");
        dprintf( 
            "    CommandLine:  '" );
        DumpUnicode64( commandLine );
        dprintf("'\n");
        dprintf( 
            "    DllPath:      '" );
        DumpUnicode64( dllPath );
        dprintf("'\n"
                "        Environment:  %p\n", peb_ProcessParameters_Environment );

    }
    else   {
        dprintf( "    *** unable to read process parameters\n" );

    }
    return S_OK;
}

DECLARE_API( peb )

/*++

Routine Description:

    This function is called to dump the PEB

    Called as:

        !peb

Arguments:

    None

Return Value:

    None

--*/

{
    ULONG64 pebAddress;
    ULONG64 peb;
    HRESULT hr = S_OK;
    
    INIT_API();
    if ( *args ) {
       pebAddress = GetExpression( args );
    } else {
        ULONG64 tebAddress;
        GetTebAddress( &tebAddress );
        if (TargetMachine == IMAGE_FILE_MACHINE_IA64 && tebAddress) {
            ULONG64 Peb32=0;
            if (GetPeb32FromWowTeb(tebAddress, &Peb32) && Peb32) {
                dprintf("Wow64 PEB32 at %lx\n", Peb32);
                DumpPeb(Peb32, TRUE);
                dprintf("\n\nWow64 ");
            }
        }
        
        GetPebAddress( 0, &pebAddress );
    }

    if ( pebAddress ) {
       dprintf( "PEB at %p\n", pebAddress );
    }
    else  {
       dprintf( "PEB NULL...\n" );
       return E_INVALIDARG;
    }

    peb = IsPtr64() ? pebAddress : (ULONG64)(LONG64)(LONG)pebAddress;

    hr = DumpPeb(peb, FALSE);
    EXIT_API();
    return hr;


} // PebExtension()


HRESULT
DumpTeb(ULONG64 tebAddress, BOOL IsWow64Teb)
{

    ULONG64 teb;
    ULONG64 tib_ExceptionList;
    ULONG64 tib_StackBase;
    ULONG64 tib_StackLimit;
    ULONG64 tib_StackSusSystemTib;
    ULONG64 tib_FiberData;
    ULONG64 tib_ArbitraryUserPointer;
    ULONG64 tib_Self;
    ULONG64 tib_EnvironmentPointer;
    ULONG64 teb_ClientId_UniqueProcess;
    ULONG64 teb_ClientId_UniqueThread;
    ULONG64 teb_RealClientId_UniqueProcess;
    ULONG64 teb_RealClientId_UniqueThread;
    ULONG64 DeallocationBStore;
    HRESULT hr = S_OK;
    ULONG64 err;

    teb = tebAddress;
    if (!IsWow64Teb) {
        if ( InitTypeRead( teb, TEB ) )   {
           dprintf( "error InitTypeRead( TEB )...\n");
           return E_INVALIDARG;
        }
    } else {
        if ( err = InitTypeRead( teb, nt!TEB32 ) )   {
            if (err == SYMBOL_TYPE_INFO_NOT_FOUND) {
                if ( InitTypeRead( teb, wow64!TEB32 ) )   {
                    dprintf( "error InitTypeRead( wow64!TEB32 )...\n");
                    return E_INVALIDARG;
                }
            } else {
                dprintf( "error InitTypeRead( TEB32 )...\n");
                return E_INVALIDARG;
            }
        }
    }

    dprintf( 
        "    ExceptionList:        %p\n"
        "    StackBase:            %p\n"
        "    StackLimit:           %p\n"
        "    SubSystemTib:         %p\n"
        "    FiberData:            %p\n"
        "    ArbitraryUserPointer: %p\n"
        "    Self:                 %p\n"
        "    EnvironmentPointer:   %p\n",
                    GetShortField(0, "NtTib.ExceptionList", 0),
                    ReadField( NtTib.StackBase ),
                    GetShortField(0, "NtTib.StackLimit", 0),
                    GetShortField(0, "NtTib.SubsystemTib", 0),
                    GetShortField(0, "NtTib.FiberData", 0),
                    GetShortField(0, "NtTib.ArbitraryUsetPointer", 0),
                    GetShortField(0, "NtTib.Self", 0),
                    GetShortField(0, "NtTib.EnvironmentPointer", 0)
            );

     teb_ClientId_UniqueProcess     = GetShortField( 0, "ClientId.UniqueProcess", 0 );
     teb_ClientId_UniqueThread      = GetShortField( 0, "ClientId.UniqueThread",  0 );
     teb_RealClientId_UniqueProcess = GetShortField( 0, "RealClientId.UniqueProcess", 0 );
     teb_RealClientId_UniqueThread  = GetShortField( 0, "RealClientId.UniqueThread",  0 );
     dprintf( 
         "    ClientId:             %p . %p\n", teb_ClientId_UniqueProcess, teb_ClientId_UniqueThread );
     if ( teb_ClientId_UniqueProcess != teb_RealClientId_UniqueProcess ||
          teb_ClientId_UniqueThread  != teb_RealClientId_UniqueThread )
     {
        dprintf( 
            "    Real ClientId:        %p . %p\n", teb_RealClientId_UniqueProcess, teb_RealClientId_UniqueThread );
     }

    dprintf(  
        "    RpcHandle:            %p\n"
        "    Tls Storage:          %p\n"
        "    PEB Address:          %p\n"
        "    LastErrorValue:       %u\n"
        "    LastStatusValue:      %x\n"
        "    Count Owned Locks:    %u\n"
        "    HardErrorsMode:       %u\n", 
            ReadField( ActiveRpcHandle ),
            ReadField( ThreadLocalStoragePointer ),
            ReadField( ProcessEnvironmentBlock ),
            (ULONG)ReadField( LastErrorValue ),
            (ULONG)ReadField( LastStatusValue ),
            (ULONG)ReadField( CountOfOwnedCriticalSections ),
            (ULONG)ReadField( HardErrorsAreDisabled ) 
            );
    if  (TargetMachine == IMAGE_FILE_MACHINE_IA64 && !IsWow64Teb)   {
        dprintf(
            "    DeallocationBStore:   %p\n"
            "    BStoreLimit:          %p\n",
            ReadField(DeallocationBStore),
            ReadField( BStoreLimit )
           );
    }
    return hr;
} // DumpTeb()



DECLARE_API( teb )

/*++

Routine Description:

    This function is called to dump the TEB

    Called as:

        !teb

--*/

{

    ULONG64 tebAddress;
    HRESULT hr = S_OK;
    
    INIT_API();
    if ( *args )   {
       tebAddress = GetExpression( args );
    } else {
        GetTebAddress( &tebAddress );
    }

    if ( tebAddress )   {
        if (TargetMachine == IMAGE_FILE_MACHINE_IA64 && tebAddress) {
            ULONG64 Teb32=0;
            if (GetTeb32FromWowTeb(tebAddress, &Teb32) && Teb32) {
                dprintf("Wow64 TEB32 at %p\n", Teb32);
                DumpTeb(Teb32, TRUE);
                dprintf("\n\nWow64 ");
            }
        }
       dprintf( "TEB at %p\n", tebAddress );
    } else  {
       dprintf( "TEB NULL...\n" );
       hr = E_INVALIDARG;
       goto ExitTeb;
    }
    tebAddress = IsPtr64() ? tebAddress : (ULONG64)(LONG64)(LONG)tebAddress;

    hr = DumpTeb(tebAddress, FALSE);
ExitTeb:
    EXIT_API();
    return hr;
}