/*++

Copyright (c) 1999-2002  Microsoft Corporation

Module Name:

    nt4.c

Abstract:

    NT 4 specific routines.

    The following routine are exported from this file:

        o Nt4OpenThread

        o Nt4GetProcessInfo

        o Nt4EnumProcessModules

        o Nt4GetModuleFileNameExW

Author:

    Matthew D Hendel (math) 10-Sept-1999

Revision History:


Environment:

    NT 4.0 only.
    
--*/

#include "pch.h"

#include "ntx.h"
#include "nt4.h"
#include "nt4p.h"
#include "impl.h"

BOOL
WINAPI
Nt4EnumProcessModules(
    HANDLE hProcess,
    HMODULE *lphModule,
    DWORD cb,
    LPDWORD lpcbNeeded
    );


HANDLE
WINAPI
Nt4OpenThread(
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    DWORD dwThreadId
    )
{
    NTSTATUS Status;
    NT4_OBJECT_ATTRIBUTES Obja;
    HANDLE Handle;
    NT4_CLIENT_ID ClientId;

    ClientId.UniqueThread = (HANDLE)LongToHandle(dwThreadId);
    ClientId.UniqueProcess = (HANDLE)NULL;

    Nt4InitializeObjectAttributes(
        &Obja,
        NULL,
        (bInheritHandle ? NT4_OBJ_INHERIT : 0),
        NULL,
        NULL
        );

    Status = NtOpenThread(
                &Handle,
                (ACCESS_MASK)dwDesiredAccess,
                (POBJECT_ATTRIBUTES)&Obja,
                (PCLIENT_ID)&ClientId
                );
    if ( NT_SUCCESS(Status) ) {
        return Handle;
        }
    else {
        return NULL;
        }
}


BOOL
Nt4GetProcessInfo(
    IN HANDLE hProcess,
    IN ULONG ProcessId,
    IN ULONG DumpType,
    IN MINIDUMP_CALLBACK_ROUTINE CallbackRoutine,
    IN PVOID CallbackParam,
    OUT PINTERNAL_PROCESS * ProcessRet
    )
{
    BOOL Succ;
    ULONG i;
    NTSTATUS Status;
    ULONG BufferSize;
    LPVOID Buffer;
    PNT4_SYSTEM_PROCESS_INFORMATION ProcessInfo;
    PNT4_SYSTEM_THREAD_INFORMATION ThreadInfo;
    PINTERNAL_THREAD Thread;
    PINTERNAL_MODULE Module;
    PINTERNAL_PROCESS Process;
    HMODULE Modules [ 512 ];
    ULONG ModulesSize;
    ULONG NumberOfModules;
    ULONG_PTR Next;


    BufferSize = 64 * KBYTE;
    Buffer = NULL;

    do {

        if (Buffer) {
            FreeMemory (Buffer);
        }
        Buffer = AllocMemory ( BufferSize );

        if ( Buffer == NULL) {
            return FALSE;
        }
    
        Status = NtQuerySystemInformation (
                            Nt4SystemProcessInformation,
                            Buffer,
                            BufferSize,
                            NULL
                            );

        if (!NT_SUCCESS (Status) && Status != STATUS_INFO_LENGTH_MISMATCH) {
            GenAccumulateStatus(MDSTATUS_CALL_FAILED);
            return FALSE;
        }

        BufferSize += (8 * KBYTE);

    } while (Status == STATUS_INFO_LENGTH_MISMATCH);

    //
    // Find the correct process in the process list.
    //
    
    ProcessInfo = (PNT4_SYSTEM_PROCESS_INFORMATION) Buffer;

    while (ProcessInfo->NextEntryOffset &&
           ProcessInfo->UniqueProcessId != (HANDLE) ProcessId) {

        Next = ((ULONG_PTR)ProcessInfo + ProcessInfo->NextEntryOffset);
        ProcessInfo = (PNT4_SYSTEM_PROCESS_INFORMATION) Next;
    }

    //
    // Could not find a matching process in the process list.
    //
    
    if (ProcessInfo->UniqueProcessId != (HANDLE) ProcessId) {
        Succ = FALSE;
        GenAccumulateStatus(MDSTATUS_INTERNAL_ERROR);
        goto Exit;
    }

    //
    // Create an INTERNAL_PROCESS object and copy the process information
    // into it.
    //
    
    Process = GenAllocateProcessObject ( hProcess, ProcessId );
                                
    if ( Process == NULL ) {
        return FALSE;
    }

    //
    // Walk the thread list for this process, copying thread information
    // for each thread. Walking the thread list also suspends all the threads
    // in the process. This should be done before walking the module list
    // to minimize the number of race conditions.
    //
    
    ThreadInfo = (PNT4_SYSTEM_THREAD_INFORMATION)(ProcessInfo + 1);
    Process->NumberOfThreads = 0;

    for (i = 0; i < ProcessInfo->NumberOfThreads; i++) {

        ULONG WriteFlags;

        if (!GenExecuteIncludeThreadCallback(hProcess,
                                             ProcessId,
                                             DumpType,
                                             (ULONG)ThreadInfo->ClientId.UniqueThread,
                                             CallbackRoutine,
                                             CallbackParam,
                                             &WriteFlags) ||
            IsFlagClear(WriteFlags, ThreadWriteThread)) {
            ThreadInfo++;
            continue;
        }
        
        Status = GenAllocateThreadObject (
                                Process,
                                hProcess,
                                (ULONG) ThreadInfo->ClientId.UniqueThread,
                                DumpType,
                                WriteFlags,
                                &Thread
                                );
        if (FAILED(Status)) {
            Succ = FALSE;
            goto Exit;
        }

        // If Status is S_FALSE it means that the thread
        // couldn't be opened and probably exited before
        // we got to it.  Just continue on.
        if (Status == S_OK) {
            InsertTailList (&Process->ThreadList, &Thread->ThreadsLink);
            Process->NumberOfThreads++;
        }
        
        ThreadInfo++;
    }


    //
    // Get the module information. Use PSAPI since it actually works.
    //

    ModulesSize = 0;
    Succ = Nt4EnumProcessModules (
                    Process->ProcessHandle,
                    Modules,
                    sizeof (Modules),
                    &ModulesSize
                    );
    
    if ( !Succ ) {
        GenAccumulateStatus(MDSTATUS_CALL_FAILED);
        goto Exit;
    }

    NumberOfModules = ModulesSize / sizeof (HMODULE);
    for (i = 0; i < NumberOfModules; i++) {
        ULONG WriteFlags;

        if (!GenExecuteIncludeModuleCallback(hProcess,
                                             ProcessId,
                                             DumpType,
                                             (LONG_PTR)Modules[i],
                                             CallbackRoutine,
                                             CallbackParam,
                                             &WriteFlags) ||
            IsFlagClear(WriteFlags, ModuleWriteModule)) {
            continue;
        }

        Module = NtxAllocateModuleObject (
                                Process,
                                Process->ProcessHandle,
                                (LONG_PTR) Modules [ i ],
                                DumpType,
                                WriteFlags,
				NULL
                                );

        if ( Module == NULL ) {
            Succ = FALSE;
            goto Exit;
        }
        
        InsertTailList (&Process->ModuleList, &Module->ModulesLink);
    }

    Process->NumberOfModules = NumberOfModules;

    Succ = TRUE;

Exit:

    if ( Buffer ) {
        FreeMemory ( Buffer );
        Buffer = NULL;
    }
    
    if ( !Succ && Process != NULL ) {
        GenFreeProcessObject ( Process );
        Process = NULL;
    }
    
    *ProcessRet = Process;

    return Succ;
}
    

//
// From PSAPI
//

BOOL
Nt4FindModule(
    IN HANDLE hProcess,
    IN HMODULE hModule,
    OUT PNT4_LDR_DATA_TABLE_ENTRY LdrEntryData
    )

/*++

Routine Description:

    This function retrieves the loader table entry for the specified
    module.  The function copies the entry into the buffer pointed to
    by the LdrEntryData parameter.

Arguments:

    hProcess - Supplies the target process.

    hModule - Identifies the module whose loader entry is being
        requested.  A value of NULL references the module handle
        associated with the image file that was used to create the
        process.

    LdrEntryData - Returns the requested table entry.

Return Value:

    TRUE if a matching entry was found.

--*/

{
    NT4_PROCESS_BASIC_INFORMATION BasicInfo;
    NTSTATUS Status;
    PNT4_PEB Peb;
    PNT4_PEB_LDR_DATA Ldr;
    PLIST_ENTRY LdrHead;
    PLIST_ENTRY LdrNext;

    Status = NtQueryInformationProcess(
                hProcess,
                Nt4ProcessBasicInformation,
                &BasicInfo,
                sizeof(BasicInfo),
                NULL
                );

    if ( !NT_SUCCESS(Status) ) {
        return(FALSE);
    }

    Peb = BasicInfo.PebBaseAddress;


    if ( hModule == NULL ) {
        if (!ReadProcessMemory(hProcess, &Peb->ImageBaseAddress, &hModule, sizeof(hModule), NULL)) {
            return(FALSE);
        }
    }

    //
    // Ldr = Peb->Ldr
    //

    if (!ReadProcessMemory(hProcess, &Peb->Ldr, &Ldr, sizeof(Ldr), NULL)) {
        return (FALSE);
    }

    if (!Ldr) {
        // Ldr might be null (for instance, if the process hasn't started yet).
        SetLastError(ERROR_INVALID_HANDLE);
        return (FALSE);
    }


    LdrHead = &Ldr->InMemoryOrderModuleList;

    //
    // LdrNext = Head->Flink;
    //

    if (!ReadProcessMemory(hProcess, &LdrHead->Flink, &LdrNext, sizeof(LdrNext), NULL)) {
        return(FALSE);
    }

    while (LdrNext != LdrHead) {

        PNT4_LDR_DATA_TABLE_ENTRY LdrEntry;

        LdrEntry = CONTAINING_RECORD(
                                LdrNext,
                                NT4_LDR_DATA_TABLE_ENTRY,
                                InMemoryOrderLinks);

        if (!ReadProcessMemory(hProcess, LdrEntry, LdrEntryData, sizeof(*LdrEntryData), NULL)) {
            return(FALSE);
        }

        if ((HMODULE) LdrEntryData->DllBase == hModule) {
            return(TRUE);
        }

        LdrNext = LdrEntryData->InMemoryOrderLinks.Flink;
    }

    SetLastError(ERROR_INVALID_HANDLE);
    return(FALSE);
}


BOOL
WINAPI
Nt4EnumProcessModules(
    HANDLE hProcess,
    HMODULE *lphModule,
    DWORD cb,
    LPDWORD lpcbNeeded
    )
{
    NT4_PROCESS_BASIC_INFORMATION BasicInfo;
    NTSTATUS Status;
    PNT4_PEB Peb;
    PNT4_PEB_LDR_DATA Ldr;
    PLIST_ENTRY LdrHead;
    PLIST_ENTRY LdrNext;
    DWORD chMax;
    DWORD ch;

    Status = NtQueryInformationProcess(
                hProcess,
                Nt4ProcessBasicInformation,
                &BasicInfo,
                sizeof(BasicInfo),
                NULL
                );

    if ( !NT_SUCCESS(Status) ) {
        return(FALSE);
        }

    Peb = BasicInfo.PebBaseAddress;

    //
    // Ldr = Peb->Ldr
    //

    if (!ReadProcessMemory(hProcess, &Peb->Ldr, &Ldr, sizeof(Ldr), NULL)) {
        return(FALSE);
        }

    LdrHead = &Ldr->InMemoryOrderModuleList;

    //
    // LdrNext = Head->Flink;
    //

    if (!ReadProcessMemory(hProcess, &LdrHead->Flink, &LdrNext, sizeof(LdrNext), NULL)) {
        return(FALSE);
        }

    chMax = cb / sizeof(HMODULE);
    ch = 0;

    while (LdrNext != LdrHead) {
        PNT4_LDR_DATA_TABLE_ENTRY LdrEntry;
        NT4_LDR_DATA_TABLE_ENTRY LdrEntryData;

        LdrEntry = CONTAINING_RECORD(
                            LdrNext,
                            NT4_LDR_DATA_TABLE_ENTRY,
                            InMemoryOrderLinks);

        if (!ReadProcessMemory(hProcess, LdrEntry, &LdrEntryData, sizeof(LdrEntryData), NULL)) {
            return(FALSE);
            }

        if (ch < chMax) {
            try {
                   lphModule[ch] = (HMODULE) LdrEntryData.DllBase;
                }
            except (EXCEPTION_EXECUTE_HANDLER) {
                return(FALSE);
                }
            }

        ch++;

        LdrNext = LdrEntryData.InMemoryOrderLinks.Flink;
        }

    try {
        *lpcbNeeded = ch * sizeof(HMODULE);
        }
    except (EXCEPTION_EXECUTE_HANDLER) {
        return(FALSE);
        }

    return(TRUE);
}


DWORD
WINAPI
Nt4GetModuleFileNameExW(
    HANDLE hProcess,
    HMODULE hModule,
    LPWSTR lpFilename,
    DWORD nSize
    )

/*++

Routine Description:

    This function retrieves the full pathname of the executable file
    from which the specified module was loaded.  The function copies the
    null-terminated filename into the buffer pointed to by the
    lpFilename parameter.

Routine Description:

    hModule - Identifies the module whose executable file name is being
        requested.  A value of NULL references the module handle
        associated with the image file that was used to create the
        process.

    lpFilename - Points to the buffer that is to receive the filename.

    nSize - Specifies the maximum number of characters to copy.  If the
        filename is longer than the maximum number of characters
        specified by the nSize parameter, it is truncated.

Return Value:

    The return value specifies the actual length of the string copied to
    the buffer.  A return value of zero indicates an error and extended
    error status is available using the GetLastError function.

Arguments:

--*/

{
    NT4_LDR_DATA_TABLE_ENTRY LdrEntryData;
    DWORD cb;

    if (!Nt4FindModule(hProcess, hModule, &LdrEntryData)) {
        return(0);
        }

    nSize *= sizeof(WCHAR);

    cb = LdrEntryData.FullDllName.MaximumLength;
    if ( nSize < cb ) {
        cb = nSize;
        }

    if (!ReadProcessMemory(hProcess, LdrEntryData.FullDllName.Buffer, lpFilename, cb, NULL)) {
        return(0);
        }

    if (cb == LdrEntryData.FullDllName.MaximumLength) {
        cb -= sizeof(WCHAR);
        }

    return(cb / sizeof(WCHAR));
}