/*++

Copyright (c) 1989-2000  Microsoft Corporation

Module Name:

    ShimEng.c

Abstract:

    This module implements the shim hooking using IAT thunking. The file
    is shared between the Windows2000 and Whistler implementations.

Author:

    clupu created 11 July 2000

Revision History:

    clupu updated 12 Dec  2000      - one file for both Win2k and Whistler
--*/

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <string.h>

#include <windef.h>
#include <winbase.h>
#include <stdio.h>
#include <apcompat.h>
#include "shimdb.h"
#include "ShimEng.h"

#ifdef SE_WIN2K
#include "NotifyCallback.h"
#endif // SE_WIN2K

#ifndef SE_WIN2K
extern BOOL
LdrInitShimEngineDynamic(
    PVOID pShimengModule
    );
#endif


BOOL       g_bDbgPrintEnabled;
DEBUGLEVEL g_DebugLevel;

//
// The number of SHIMs that can be added dynamically by calling SE_DynamicShim
//
#define MAX_DYNAMIC_SHIMS   128

//
// Flags used in HOOKAPI.dwFlags
//
#define HAF_CHAINED         0x00000004
#define HAF_BOTTOM_OF_CHAIN 0x00000008

//
// Flags used in SHIMINFO.dwFlags
//
#define SIF_RESOLVED        0x00000001

//
// Pipe name for sending messages
//
#define PIPE_NAME           L"\\Device\\NamedPipe\\ShimViewer"

typedef struct tagINEXMOD {
    char*              pszModule;
    struct tagINEXMOD* pNext;
} INEXMOD, *PINEXMOD;

typedef enum tagINEX_MODE {
    INEX_UNINITIALIZED = 0,
    EXCLUDE_SYSTEM32,
    EXCLUDE_ALL,
    INCLUDE_ALL
} INEX_MODE, *PINEX_MODE;

#define MAX_SHIM_NAME_LEN 63

typedef struct tagSHIMINFO {
    DWORD       dwHookedAPIs;       // the number of APIs hooked by this shim DLL
    PVOID       pDllBase;           // the base address for this shim DLL
    DWORD       dwFlags;            // internal flags
    PINEXMOD    pFirstInclude;      // local inclusion/exclusion list
    PINEXMOD    pFirstExclude;      // local inclusion/exclusion list
    INEX_MODE   eInExMode;          // what inclusion mode are we in?

    PLDR_DATA_TABLE_ENTRY pLdrEntry;            // pointer to the loader entry for this
                                                // shim DLL.
    WCHAR       wszName[MAX_SHIM_NAME_LEN + 1]; // name of shim
} SHIMINFO, *PSHIMINFO;

typedef struct tagHOOKEDMODULE {
    PVOID   pDllBase;               // the base address of the loaded module
    ULONG   ulSizeOfImage;          // the size of the DLL image
    char    szModuleName[128];      // the name of the loaded module
    BOOL    bInSystem32;            // tru if the DLL hooked is in system32

} HOOKEDMODULE, *PHOOKEDMODULE;

//
// The prototypes of the internal stubs.
//
typedef PVOID     (*PFNGETPROCADDRESS)(HMODULE hMod, char* pszProc);
typedef HINSTANCE (*PFNLOADLIBRARYA)(LPCSTR lpLibFileName);
typedef HINSTANCE (*PFNLOADLIBRARYW)(LPCWSTR lpLibFileName);
typedef HINSTANCE (*PFNLOADLIBRARYEXA)(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
typedef HINSTANCE (*PFNLOADLIBRARYEXW)(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
typedef BOOL      (*PFNFREELIBRARY)(HMODULE hLibModule);


BOOL
SeiSendDataToPipe(
    void
    );

#ifdef SE_WIN2K

BOOL PatchNewModules(
    BOOL bDynamic
    );

#else

BOOL
SeiDisplayAppHelp(
    HSDB                hSDB,
    PSDBQUERYRESULT     pSdbQuery
    );

#endif // SE_WIN2K


//
// Global function hooks the shim uses to keep from recursing itself
//
PFNRTLALLOCATEHEAP g_pfnRtlAllocateHeap;
PFNRTLFREEHEAP     g_pfnRtlFreeHeap;

// Shim's private heap
PVOID           g_pShimHeap;

// The global inclusion list.
PINEXMOD        g_pGlobalInclusionList = NULL;

// Array with all the HOOKAPI list for all the shim DLLs
PHOOKAPI*       g_pHookArray = NULL;

// this variable will only be valid on dynamic cases
HMODULE         g_hModule = NULL;


// Internal HOOKAPI for the stubs that the shim engine provides

#define IHA_GetProcAddress      0

#ifdef SE_WIN2K
#define IHA_LoadLibraryA        1
#define IHA_LoadLibraryW        2
#define IHA_LoadLibraryExA      3
#define IHA_LoadLibraryExW      4
#define IHA_FreeLibrary         5
#define IHA_COUNT               6
#else
#define IHA_COUNT               1
#endif // SE_WIN2K

HOOKAPI         g_IntHookAPI[IHA_COUNT];

// BUGBUG: comment here
HOOKAPIEX       g_IntHookEx[IHA_COUNT];

// Extra info for all the shim DLLs
PSHIMINFO       g_pShimInfo;

// The number of all shims applied to this process
DWORD           g_dwShimsCount = 0;

// The maximum number of shims that can be applied.
DWORD           g_dwMaxShimsCount = 0;

#define SHIM_MAX_HOOKED_MODULES 512

// The array of hooked modules
HOOKEDMODULE    g_hHookedModules[SHIM_MAX_HOOKED_MODULES];

// The number of modules hooked
DWORD           g_dwHookedModuleCount;

// True if the statically linked modules have been hooked
BOOL            g_bShimInitialized = FALSE;

// Don will comment here
BOOL            g_bShimDuringInit = FALSE;

#define SHIM_MAX_PATCH_COUNT    64

// The array of in memory patches
PBYTE           g_pMemoryPatches[SHIM_MAX_PATCH_COUNT];

// The number of in memory patches
DWORD           g_dwMemoryPatchCount;

// This shim engine's module handle
PVOID           g_pShimEngModHandle;

// The system32 directory
WCHAR           g_szSystem32[MAX_PATH] = L"";

// The length of the System32 directory string;
DWORD           g_dwSystem32StrLen = 0;

// The SxS directory
WCHAR           g_szSxS[MAX_PATH] = L"";

// The length of the SxS directory string;
DWORD           g_dwSxSStrLen = 0;

// The windows directory
WCHAR           g_szWindir[MAX_PATH] = L"";

// The exe name for sending data to our named pipe
WCHAR           g_szExeName[MAX_PATH] = L"";

// The length of the windows directory string;
DWORD           g_dwWindirStrLen = 0;

// Cmd.exe full path
WCHAR           g_szCmdExePath[MAX_PATH];

// Are we using an exe entry to get shims from?
BOOL            g_bUsingExe;

// Are we using a layer entry to get shims from?
BOOL            g_bUsingLayer;

PLDR_DATA_TABLE_ENTRY g_pShimEngLdrEntry;

// This boolean tells if some global vars have been initialized.
BOOL            g_bInitGlobals;

// This boolean tells if we shimmed the internal hooks.
BOOL            g_bInternalHooksUsed;

// Pipe data
WCHAR           g_szPipeData[1024];


#ifndef SE_WIN2K

// The name of the shim DLL that the engine is just about to load.
WCHAR           g_wszShimDllInLoading[128];

PVOID           g_hApphelpDllHelper;

UNICODE_STRING  Kernel32String = RTL_CONSTANT_STRING(L"kernel32.dll");
UNICODE_STRING  NtdllString = RTL_CONSTANT_STRING(L"ntdll.dll");
UNICODE_STRING  VerifierdllString = RTL_CONSTANT_STRING(L"verifier.dll");

// This tells if the image shimmed is a COM+ image.
BOOL            g_bComPlusImage;

#endif // SE_WIN2K


#ifdef DEBUG_SPEW

void
__cdecl
DebugPrintfEx(
    DEBUGLEVEL dwDetail,
    LPSTR      pszFmt,
    ...
    )
/*++
    Return: void

    Desc:   This function prints debug spew in the debugger.
--*/
{
    char    szT[1024];
    va_list arglist;
    int     len;

    va_start(arglist, pszFmt);
    _vsnprintf(szT, 1023, pszFmt, arglist);
    szT[1022] = 0;
    va_end(arglist);

    //
    // Make sure we have a '\n' at the end of the string
    //
    len = strlen(szT);

    if (szT[len - 1] != '\n')  {
        strcpy(szT + len, "\n");
    }

    if (dwDetail <= g_DebugLevel) {
        switch (dwDetail) {
        case dlPrint:
            DbgPrint("[MSG ] ");
            break;

        case dlError:
            DbgPrint("[FAIL] ");
            break;

        case dlWarning:
            DbgPrint("[WARN] ");
            break;

        case dlInfo:
            DbgPrint("[INFO] ");
            break;
        }

        DbgPrint("%s", szT);
    }
}


void
SeiInitDebugSupport(
    void
    )
/*++
    Return: void

    Desc:   This function initializes g_bDbgPrintEnabled based on an env variable.
--*/
{
    NTSTATUS            status;
    UNICODE_STRING      EnvName;
    UNICODE_STRING      EnvValue;
    WCHAR               wszEnvValue[128];

    RtlInitUnicodeString(&EnvName, L"SHIMENG_DEBUG_LEVEL");

    EnvValue.Buffer = wszEnvValue;
    EnvValue.Length = 0;
    EnvValue.MaximumLength = sizeof(wszEnvValue);

    status = RtlQueryEnvironmentVariable_U(NULL, &EnvName, &EnvValue);

    if (NT_SUCCESS(status)) {

        WCHAR c = EnvValue.Buffer[0];

        g_bDbgPrintEnabled = TRUE;

        switch (c) {
        case L'0':
            g_DebugLevel = dlNone;
            g_bDbgPrintEnabled = FALSE;
            break;

        case L'1':
            g_DebugLevel = dlPrint;
            break;

        case L'2':
            g_DebugLevel = dlError;
            break;

        case L'3':
            g_DebugLevel = dlWarning;
            break;

        case L'4':
        default:
            g_DebugLevel = dlInfo;
            break;
        }
    }
}
#endif // DEBUG_SPEW


PHOOKAPI
SeiConstructChain(
    IN  PVOID  pfnOld,          // Original API function pointer to resolve.
    OUT DWORD* pdwDllIndex      // Will receive the index of the shim DLL
                                // that provides the returning PHOOKAPI.
    )
/*++
    Return: Top-of-chain PHOOKAPI structure.

    Desc:   Scans HOOKAPI arrays for pfnOld and either constructs the
            chain or returns the top-of-chain PHOOKAPI if the chain
            already exists.
--*/
{
    LONG     i; // use LONG because we decrement this and compare it for positive
    DWORD    j;
    PHOOKAPI pTopHookAPI    = NULL;
    PHOOKAPI pBottomHookAPI = NULL;

    *pdwDllIndex = 0;

    //
    // Scan all HOOKAPI entries for corresponding function pointer.
    //
    for (i = (LONG)g_dwShimsCount - 1; i >= 0; i--) {
        
        for (j = 0; j < g_pShimInfo[i].dwHookedAPIs; j++) {

            if (g_pHookArray[i][j].pfnOld == pfnOld) {

                if (pTopHookAPI != NULL) {

                    //
                    // The chain has already begun, so tack this one on
                    // to the end.
                    //
                    pBottomHookAPI->pfnOld = g_pHookArray[i][j].pfnNew;
                    if (pBottomHookAPI->pHookEx) {
                        pBottomHookAPI->pHookEx->pNext = &(g_pHookArray[i][j]);
                    }

                    pBottomHookAPI = &(g_pHookArray[i][j]);

                    if (pBottomHookAPI->pHookEx) {
                        pBottomHookAPI->pHookEx->pTopOfChain = pTopHookAPI;
                    }

                    pBottomHookAPI->dwFlags |= HAF_CHAINED;

                    DPF(dlInfo, " 0x%p ->", pBottomHookAPI->pfnNew);

                } else {
                    //
                    // This is the top of the chain. The inclusion/exclusion list
                    // from the DLL at the top of the chain is used to determine if
                    // an import entry in a particular DLL is hooked or not.
                    // See SeiHookImports for use of pdwIndex.
                    //
                    *pdwDllIndex = i;

                    if (g_pHookArray[i][j].pHookEx && g_pHookArray[i][j].pHookEx->pTopOfChain) {

                        //
                        // Chain has already been constructed.
                        //
                        return g_pHookArray[i][j].pHookEx->pTopOfChain;
                    }

                    //
                    // Not hooked yet. Set to top of chain.
                    //
                    pTopHookAPI = &(g_pHookArray[i][j]);

                    if (pTopHookAPI->pHookEx) {
                        pTopHookAPI->pHookEx->pTopOfChain = pTopHookAPI;
                    }

                    pTopHookAPI->dwFlags |= HAF_CHAINED;

                    pBottomHookAPI = pTopHookAPI;

                    if ((ULONG_PTR)pTopHookAPI->pszFunctionName < 0x0000FFFF) {
                        DPF(dlInfo, "[SeiConstructChain] %s!#%d 0x%p ->",
                            pTopHookAPI->pszModule,
                            pTopHookAPI->pszFunctionName,
                            pTopHookAPI->pfnNew);
                    } else {
                        DPF(dlInfo, "[SeiConstructChain] %s!%-20s 0x%p ->",
                            pTopHookAPI->pszModule,
                            pTopHookAPI->pszFunctionName,
                            pTopHookAPI->pfnNew);
                    }
                }
            }
        }
    }

    if (pBottomHookAPI != NULL) {
        pBottomHookAPI->dwFlags |= HAF_BOTTOM_OF_CHAIN;

        DPF(dlInfo, " 0x%p\n", pBottomHookAPI->pfnOld);
    }

    return pTopHookAPI;
}

PVOID
SeiGetPatchAddress(
    IN  PRELATIVE_MODULE_ADDRESS pRelAddress    // A RELATIVE_MODULE_ADDRESS structure
                                                // that defines the memory location as
                                                // an offset from a loaded module.
    )
/*++
    Return: The actual memory address of the specified module + offset.

    Desc:   Resolves a RELATIVE_MODULE_ADDRESS structure into an actual memory address.
--*/
{
    WCHAR           wszModule[MAX_PATH];
    PVOID           ModuleHandle = NULL;
    UNICODE_STRING  UnicodeString;
    NTSTATUS        status;
    PPEB            Peb = NtCurrentPeb();

    if (pRelAddress->moduleName[0] != 0) {

        //
        // Copy the module name from the patch since it will typically be misaligned.
        //
        wcscpy(wszModule, pRelAddress->moduleName);

        RtlInitUnicodeString(&UnicodeString, wszModule);

        //
        // Make sure the module is loaded before calculating address ranges.
        //
        status = LdrGetDllHandle(NULL, NULL, &UnicodeString, &ModuleHandle);

        if (!NT_SUCCESS(status)) {
            DPF(dlWarning,
                "[SeiGetPatchAddress] Dll \"%S\" not yet loaded for memory patching.\n",
                wszModule);
            return NULL;
        }

        //
        // We're done, return the address
        //
        return (PVOID)((ULONG_PTR)ModuleHandle + (ULONG_PTR)pRelAddress->address);
    }

    //
    // This patch is for the main EXE.
    //
    return (PVOID)((ULONG_PTR)Peb->ImageBaseAddress + (ULONG_PTR)pRelAddress->address);
}

int
SeiApplyPatch(
    IN  PBYTE pPatch            // A patch code blob.
    )
/*++
    Return: 1 for success, 0 for failure.

    Desc:   Attempts to execute all commands in a patch code blob. If DLLs are not loaded,
            this function will return 0.
--*/
{
    PPATCHMATCHDATA pMatchData;
    PPATCHWRITEDATA pWriteData;
    PPATCHOP        pPatchOP;
    NTSTATUS        status;
    PVOID           pAddress;
    PVOID           pProtectFuncAddress = NULL;
    SIZE_T          dwProtectSize = 0;
    DWORD           dwOldFlags = 0;

    //
    // Grab the opcode and see what we have to do.
    //
    while (TRUE) {
        pPatchOP = (PPATCHOP)pPatch;

        switch (pPatchOP->dwOpcode) {
        case PEND:
            return 1;

        case PWD:
            //
            // This is a patch write data primitive - write the data.
            //
            pWriteData = (PPATCHWRITEDATA)pPatchOP->data;

            //
            // Grab the physical address to do this operation.
            //
            pAddress = SeiGetPatchAddress(&(pWriteData->rva));

            if (pAddress == NULL) {
                DPF(dlWarning, "[SeiApplyPatch] DLL not loaded for memory patching.\n");
                return 0;
            }

            //
            // Fixup the page attributes.
            //
            dwProtectSize = pWriteData->dwSizeData;
            pProtectFuncAddress = pAddress;
            status = NtProtectVirtualMemory(NtCurrentProcess(),
                                            (PVOID)&pProtectFuncAddress,
                                            &dwProtectSize,
                                            PAGE_READWRITE,
                                            &dwOldFlags);
            if (!NT_SUCCESS(status)) {
                DPF(dlError, "[SeiApplyPatch] NtProtectVirtualMemory failed 0x%X.\n",
                    status);
                return 0;
            }

            //
            // Copy the patch bytes.
            //
            RtlCopyMemory((PVOID)pAddress, (PVOID)pWriteData->data, pWriteData->dwSizeData);

            //
            // Restore the page protection.
            //
            dwProtectSize = pWriteData->dwSizeData;
            pProtectFuncAddress = pAddress;
            status = NtProtectVirtualMemory(NtCurrentProcess(),
                                            (PVOID)&pProtectFuncAddress,
                                            &dwProtectSize,
                                            dwOldFlags,
                                            &dwOldFlags);
            if (!NT_SUCCESS(status)) {
                DPF(dlError, "[SeiApplyPatch] NtProtectVirtualMemory failed 0x%X.\n",
                    status);
                return 0;
            }

            status = NtFlushInstructionCache(NtCurrentProcess(),
                                             pProtectFuncAddress,
                                             dwProtectSize);

            if (!NT_SUCCESS(status)) {
                DPF(dlError,
                    "[SeiApplyPatch] NtFlushInstructionCache failed w/ status 0x%X.\n",
                    status);
            }

            break;

        case PMAT:
            //
            // This is a patch match data at offset primitive.
            //
            pMatchData = (PPATCHMATCHDATA)pPatchOP->data;

            //
            // Grab the physical address to do this operation
            //
            pAddress = SeiGetPatchAddress(&(pMatchData->rva));
            if (pAddress == NULL) {
                DPF(dlWarning, "[SeiApplyPatch] SeiGetPatchAddress failed.\n");
                return 0;
            }

            //
            // Make sure there is a match with what we expect to be there.
            //
            if (!RtlEqualMemory(pMatchData->data, (PBYTE)pAddress, pMatchData->dwSizeData)) {
                DPF(dlError, "[SeiApplyPatch] Failure matching on patch data.\n");
                return 0;
            }

            break;

        default:
            //
            // If this happens we got an unexpected operation and we have to fail.
            //
            DPF(dlError, "[SeiApplyPatch] Unknown patch opcode 0x%X.\n",
                pPatchOP->dwOpcode);
            ASSERT(0);

            return 0;
        }

        //
        // Next opcode.
        //
        pPatch = (PBYTE)(pPatchOP->dwNextOpcode + pPatch);
    }

    return 1;
}

void
SeiAttemptPatches(
    void
    )
/*++
    Return: void.

    Desc:   Attempts all patches in the global array.
--*/
{
    DWORD  i, dwSucceeded = 0;

    for (i = 0; i < g_dwMemoryPatchCount; i++) {
        dwSucceeded += SeiApplyPatch(g_pMemoryPatches[i]);
    }

    if (g_dwMemoryPatchCount > 0) {
        DPF(dlInfo, "[SeiAttemptPatches] Applied %d of %d patches.\n",
            dwSucceeded,
            g_dwMemoryPatchCount);
        
        swprintf(g_szPipeData, L"%s - Applied %d of %d patches",
                 g_szExeName,
                 dwSucceeded,
                 g_dwMemoryPatchCount);
        
        SeiSendDataToPipe();
    }
}

void
SeiResolveAPIs(
    void
    )
/*++
    Return: void

    Desc:   Loops through the array of HOOKAPI and sets pfnOld if it wasn't
            already set.
--*/
{
    DWORD             i, j;
    ANSI_STRING       AnsiString;
    UNICODE_STRING    UnicodeString;
    WCHAR             wszBuffer[MAX_PATH];
    STRING            ProcedureNameString;
    PVOID             pfnOld;
    PVOID             ModuleHandle = NULL;
    NTSTATUS          status;
    BOOL              bAllApisResolved;
    char*             pszFunctionName;

    UnicodeString.Buffer = wszBuffer;

    for (i = 0; i < g_dwShimsCount; i++) {

        //
        // See if we've already resolved all the APIs this shim DLL wanted to hook.
        //
        if (g_pShimInfo[i].dwFlags & SIF_RESOLVED) {
            continue;
        }

        bAllApisResolved = TRUE;

        for (j = 0; j < g_pShimInfo[i].dwHookedAPIs; j++) {
            //
            // Ignore resolved APIs.
            //
            if (g_pHookArray[i][j].pfnOld != NULL) {
                continue;
            }

            //
            // Don't try to load unspecified modules.
            //
            if (g_pHookArray[i][j].pszModule == NULL) {
                continue;
            }

            //
            // Is this DLL mapped in the address space?
            //
            RtlInitAnsiString(&AnsiString, g_pHookArray[i][j].pszModule);

            UnicodeString.MaximumLength = sizeof(wszBuffer);

            if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&UnicodeString,
                                                         &AnsiString,
                                                         FALSE))){

                DPF(dlError,
                    "[SeiResolveAPIs] Failed to convert string \"%s\" to UNICODE.\n",
                    g_pHookArray[i][j].pszModule);
                continue;
            }

            status = LdrGetDllHandle(NULL,
                                     NULL,
                                     &UnicodeString,
                                     &ModuleHandle);

            if (!NT_SUCCESS(status)) {
                bAllApisResolved = FALSE;
                continue;
            }

            //
            // Get the original entry point for this hook.
            //
            pszFunctionName = g_pHookArray[i][j].pszFunctionName;

            if ((ULONG_PTR)pszFunctionName < 0x0000FFFF) {

                status = LdrGetProcedureAddress(ModuleHandle,
                                                NULL,
                                                (ULONG)(ULONG_PTR)pszFunctionName,
                                                &pfnOld);
            } else {
                RtlInitString(&ProcedureNameString, pszFunctionName);

                status = LdrGetProcedureAddress(ModuleHandle,
                                                &ProcedureNameString,
                                                0,
                                                &pfnOld);
            }

            if (!NT_SUCCESS(status) || pfnOld == NULL) {
                bAllApisResolved = FALSE;

                if ((ULONG_PTR)pszFunctionName < 0x0000FFFF) {
                    DPF(dlError, "[SeiResolveAPIs] There is no \"%s!#%d\" !\n",
                        g_pHookArray[i][j].pszModule,
                        pszFunctionName);
                } else {
                    DPF(dlError, "[SeiResolveAPIs] There is no \"%s!%s\" !\n",
                        g_pHookArray[i][j].pszModule,
                        pszFunctionName);
                }

                continue;
            }

            g_pHookArray[i][j].pfnOld = pfnOld;

            if ((ULONG_PTR)pszFunctionName < 0x0000FFFF) {
                DPF(dlInfo, "[SeiResolveAPIs] Resolved \"%s!#%d\" to 0x%p\n",
                    g_pHookArray[i][j].pszModule,
                    pszFunctionName,
                    pfnOld);
            } else {
                DPF(dlInfo, "[SeiResolveAPIs] Resolved \"%s!%s\" to 0x%p\n",
                    g_pHookArray[i][j].pszModule,
                    pszFunctionName,
                    pfnOld);
            }
        }

        //
        // See if all the APIs were resolved for this shim DLL.
        //
        if (bAllApisResolved) {
            g_pShimInfo[i].dwFlags |= SIF_RESOLVED;
        }
    }
}

BOOL
SeiIsExcluded(
    IN  LPCSTR   pszModule,     // The module to test for exclusion.
    IN  PHOOKAPI pTopHookAPI,   // The HOOKAPI for which we test for exclusion.
    IN  BOOL     bInSystem32    // Whether the module is located in the System32 directory.
    )
/*++
    Return: TRUE if the requested module shouldn't be patched.

    Desc:   Checks the inclusion/exclusion list of the shim DLL specified by
            dwCounter and then checks the global exclusion list also.
--*/
{
    BOOL     bExclude = TRUE;
    BOOL     bShimWantsToExclude = FALSE; // was there a shim that wanted to exclude?
    PHOOKAPI pHook = pTopHookAPI;

    //
    // The current process is to only exclude a chain if every shim in the chain wants to
    // exclude. If one shim needs to be included, the whole chain is included.
    //
    while (pHook && pHook->pHookEx) {

        DWORD dwCounter;

        dwCounter = pHook->pHookEx->dwShimID;

        switch (g_pShimInfo[dwCounter].eInExMode) {
        case INCLUDE_ALL:
        {
            //
            // We include everything except what's in the exclude list.
            //
            PINEXMOD pExcludeMod;

            pExcludeMod = g_pShimInfo[dwCounter].pFirstExclude;

            while (pExcludeMod != NULL) {
                if (_stricmp(pExcludeMod->pszModule, pszModule) == 0) {
                    if ((ULONG_PTR)pTopHookAPI->pszFunctionName < 0x0000FFFF) {
                        DPF(dlInfo,
                            "[SeiIsExcluded] Module \"%s\" excluded for shim %S, API \"%s!#%d\", because it is in the exclude list (MODE: IA).\n",
                            pszModule,
                            g_pShimInfo[dwCounter].wszName,
                            pTopHookAPI->pszModule,
                            pTopHookAPI->pszFunctionName);
                    } else {
                        DPF(dlInfo,
                            "[SeiIsExcluded] Module \"%s\" excluded for shim %S, API \"%s!%s\", because it is in the exclude list (MODE: IA).\n",
                            pszModule,
                            g_pShimInfo[dwCounter].wszName,
                            pTopHookAPI->pszModule,
                            pTopHookAPI->pszFunctionName);
                    }

                    //
                    // this wants to be excluded, so we go to the next
                    // shim, and see if it wants to be included
                    //
                    bShimWantsToExclude = TRUE;
                    goto nextShim;
                }
                pExcludeMod = pExcludeMod->pNext;
            }

            //
            // we should include this shim, and therefore, the whole chain
            //
            bExclude = FALSE;
            goto out;
            break;
        }

        case EXCLUDE_SYSTEM32:
        {
            //
            // In this case, we first check the include list,
            // then exclude it if it's in System32, then exclude it if
            // it's in the exclude list.
            //

            PINEXMOD pIncludeMod;
            PINEXMOD pExcludeMod;

            pIncludeMod = g_pShimInfo[dwCounter].pFirstInclude;
            pExcludeMod = g_pShimInfo[dwCounter].pFirstExclude;

            //
            // First, check the include list.
            //
            while (pIncludeMod != NULL) {
                if (_stricmp(pIncludeMod->pszModule, pszModule) == 0) {

                    //
                    // we should include this shim, and therefore, the whole chain
                    //
                    bExclude = FALSE;
                    goto out;
                }
                pIncludeMod = pIncludeMod->pNext;
            }

            //
            // it wasn't in the include list, so is it in System32?
            //
            if (bInSystem32) {
                if ((ULONG_PTR)pTopHookAPI->pszFunctionName < 0x0000FFFF) {
                    DPF(dlInfo,
                        "[SeiIsExcluded] module \"%s\" excluded for shim %S, API \"%s!#%d\", because it is in System32.\n",
                        pszModule,
                        g_pShimInfo[dwCounter].wszName,
                        pTopHookAPI->pszModule,
                        pTopHookAPI->pszFunctionName);
                } else {
                    DPF(dlInfo,
                        "[SeiIsExcluded] module \"%s\" excluded for shim %S, API \"%s!%s\", because it is in System32.\n",
                        pszModule,
                        g_pShimInfo[dwCounter].wszName,
                        pTopHookAPI->pszModule,
                        pTopHookAPI->pszFunctionName);
                }

                //
                // this wants to be excluded, so we go to the next
                // shim, and see if it wants to be included
                //
                bShimWantsToExclude = TRUE;
                goto nextShim;
            }

            //
            // it wasn't in System32, so is it in the exclude list?
            //
            while (pExcludeMod != NULL) {
                if (_stricmp(pExcludeMod->pszModule, pszModule) == 0) {
                    if ((ULONG_PTR)pTopHookAPI->pszFunctionName < 0x0000FFFF) {
                        DPF(dlInfo,
                            "[SeiIsExcluded] module \"%s\" excluded for shim %S, API \"%s!#%d\", because it is in the exclude list (MODE: ES).\n",
                            pszModule,
                            g_pShimInfo[dwCounter].wszName,
                            pTopHookAPI->pszModule,
                            pTopHookAPI->pszFunctionName);
                    } else {
                        DPF(dlInfo,
                            "[SeiIsExcluded] module \"%s\" excluded for shim %S, API \"%s!%s\", because it is in the exclude list (MODE: ES).\n",
                            pszModule,
                            g_pShimInfo[dwCounter].wszName,
                            pTopHookAPI->pszModule,
                            pTopHookAPI->pszFunctionName);
                    }

                    //
                    // this wants to be excluded, so we go to the next
                    // shim, and see if it wants to be included
                    //
                    bShimWantsToExclude = TRUE;
                    goto nextShim;
                }
                pExcludeMod = pExcludeMod->pNext;
            }

            //
            // we should include this shim, and therefore, the whole chain
            //
            bExclude = FALSE;
            goto out;
            break;
        }

        case EXCLUDE_ALL:
        {
            //
            // We exclude everything except what is in the include list.
            //

            PINEXMOD pIncludeMod;

            pIncludeMod = g_pShimInfo[dwCounter].pFirstInclude;

            while (pIncludeMod != NULL) {
                if (_stricmp(pIncludeMod->pszModule, pszModule) == 0) {
                    //
                    // we should include this shim, and therefore, the whole chain
                    //
                    bExclude = FALSE;
                    goto out;
                }
                pIncludeMod = pIncludeMod->pNext;
            }

            if ((ULONG_PTR)pTopHookAPI->pszFunctionName < 0x0000FFFF) {
                DPF(dlInfo,
                    "[SeiIsExcluded] module \"%s\" excluded for shim %S, API \"%s!#%d\", because it is not in the include list (MODE: EA).\n",
                    pszModule,
                    g_pShimInfo[dwCounter].wszName,
                    pTopHookAPI->pszModule,
                    pTopHookAPI->pszFunctionName);
            } else {
                DPF(dlInfo,
                    "[SeiIsExcluded] module \"%s\" excluded for shim %S, API \"%s!%s\", because it is not in the include list (MODE: EA).\n",
                    pszModule,
                    g_pShimInfo[dwCounter].wszName,
                    pTopHookAPI->pszModule,
                    pTopHookAPI->pszFunctionName);
            }

            //
            // this wants to be excluded, so we go to the next
            // shim, and see if it wants to be included
            //
            bShimWantsToExclude = TRUE;
            goto nextShim;
            break;
        }
        }

nextShim:

        pHook = pHook->pHookEx->pNext;
    }


out:
    if (!bExclude && bShimWantsToExclude) {
        if ((ULONG_PTR)pTopHookAPI->pszFunctionName < 0x0000FFFF) {
            DPF(dlError,
                "[SeiIsExcluded] Module \"%s\" mixed inclusion/exclusion for "
                "API \"%s!#%d\". Included.\n",
                pszModule,
                pTopHookAPI->pszModule,
                pTopHookAPI->pszFunctionName);
        } else {
            DPF(dlError,
                "[SeiIsExcluded] Module \"%s\" mixed inclusion/exclusion for "
                "API \"%s!%s\". Included.\n",
                pszModule,
                pTopHookAPI->pszModule,
                pTopHookAPI->pszFunctionName);
        }
    }

    return bExclude;
}


BOOL
SeiHookImports(
    IN  PBYTE           pDllBase,       // The base address of the DLL to be hooked
    IN  ULONG           ulSizeOfImage,  // The size of the DLL image
    IN  PUNICODE_STRING pstrDllName,    // The name of the DLL to be hooked
    IN  BOOL            bInSystem32     // TRUE if this is a system32 DLL
    )
/*++
    Return: TRUE if successful.

    Desc:   Walks the import table of the specified module and patches the APIs
            that need to be hooked.
--*/
{
    CHAR                        szBaseDllName[MAX_PATH];
    ANSI_STRING                 AnsiString = { 0, sizeof(szBaseDllName), szBaseDllName };
    NTSTATUS                    status;
    BOOL                        bAnyHooked = FALSE;
    PIMAGE_DOS_HEADER           pIDH       = (PIMAGE_DOS_HEADER)pDllBase;
    PIMAGE_NT_HEADERS           pINTH;
    PIMAGE_IMPORT_DESCRIPTOR    pIID;
    DWORD                       dwImportTableOffset;
    PHOOKAPI                    pTopHookAPI;
    DWORD                       dwOldProtect, dwOldProtect2;
    SIZE_T                      dwProtectSize;
    DWORD                       i, j;
    PVOID                       pfnOld;

    status = RtlUnicodeStringToAnsiString(&AnsiString, pstrDllName, FALSE);

    if (!NT_SUCCESS(status)) {
        DPF(dlError, "[SeiHookImports] Cannot convert \"%S\" to ANSI\n",
            pstrDllName->Buffer);
        return FALSE;
    }

    //
    // Get the import table.
    //
    pINTH = (PIMAGE_NT_HEADERS)(pDllBase + pIDH->e_lfanew);

    dwImportTableOffset = pINTH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;

    if (dwImportTableOffset == 0) {
        //
        // No import table found. This is probably ntdll.dll
        //
        return TRUE;
    }

    DPF(dlInfo, "[SeiHookImports] Hooking module 0x%p \"%s\"\n", pDllBase, szBaseDllName);

    pIID = (PIMAGE_IMPORT_DESCRIPTOR)(pDllBase + dwImportTableOffset);

    //
    // Loop through the import table and search for the APIs that we want to patch
    //
    while (TRUE) {

        LPSTR             pszImportEntryModule;
        PIMAGE_THUNK_DATA pITDA;

        //
        // Return if no first thunk (terminating condition).
        //
        if (pIID->FirstThunk == 0) {
            break;
        }

        pszImportEntryModule = (LPSTR)(pDllBase + pIID->Name);

        //
        // If we're not interested in this module jump to the next.
        //
        bAnyHooked = FALSE;

        for (i = 0; i < g_dwShimsCount; i++) {
            for (j = 0; j < g_pShimInfo[i].dwHookedAPIs; j++) {
                if (g_pHookArray[i][j].pszModule != NULL &&
                    _stricmp(g_pHookArray[i][j].pszModule, pszImportEntryModule) == 0) {
                    bAnyHooked = TRUE;
                    goto ScanDone;
                }
            }
        }

ScanDone:
        if (!bAnyHooked) {
            pIID++;
            continue;
        }

        //
        // We have APIs to hook for this module!
        //
        pITDA = (PIMAGE_THUNK_DATA)(pDllBase + (DWORD)pIID->FirstThunk);

        while (TRUE) {

            SIZE_T dwFuncAddr;

            pfnOld = (PVOID)pITDA->u1.Function;

            //
            // Done with all the imports from this module? (terminating condition)
            //
            if (pITDA->u1.Ordinal == 0) {
                break;
            }

            pTopHookAPI = SeiConstructChain(pfnOld, &i);

            if (pTopHookAPI == NULL || SeiIsExcluded(szBaseDllName, pTopHookAPI, bInSystem32)) {
                pITDA++;
                continue;
            }

            if ((ULONG_PTR)pTopHookAPI->pszFunctionName < 0x0000FFFF) {
                DPF(dlInfo,
                    "[SeiHookImports] Hooking API \"%s!#%d\" for DLL \"%s\"\n",
                    pTopHookAPI->pszModule,
                    pTopHookAPI->pszFunctionName,
                    szBaseDllName);
            } else {
                DPF(dlInfo,
                    "[SeiHookImports] Hooking API \"%s!%s\" for DLL \"%s\"\n",
                    pTopHookAPI->pszModule,
                    pTopHookAPI->pszFunctionName,
                    szBaseDllName);
            }

            //
            // Make the code page writable and overwrite new function pointer
            // in the import table.
            //
            dwProtectSize = sizeof(DWORD);

            dwFuncAddr = (SIZE_T)&pITDA->u1.Function;

            status = NtProtectVirtualMemory(NtCurrentProcess(),
                                            (PVOID)&dwFuncAddr,
                                            &dwProtectSize,
                                            PAGE_READWRITE,
                                            &dwOldProtect);

            if (NT_SUCCESS(status)) {
                pITDA->u1.Function = (SIZE_T)pTopHookAPI->pfnNew;

                dwProtectSize = sizeof(DWORD);

                status = NtProtectVirtualMemory(NtCurrentProcess(),
                                                (PVOID)&dwFuncAddr,
                                                &dwProtectSize,
                                                dwOldProtect,
                                                &dwOldProtect2);
                if (!NT_SUCCESS(status)) {
                    DPF(dlError, "[SeiHookImports] Failed to change back the protection\n");
                }
            } else {
                DPF(dlError,
                    "[SeiHookImports] Failed 0x%X to change protection to PAGE_READWRITE."
                    " Addr 0x%p\n",
                    status,
                    &pITDA->u1.Function);
            }
            pITDA++;

        }
        pIID++;
    }

    //
    // Add the hooked module to the list of hooked modules
    //
    g_hHookedModules[g_dwHookedModuleCount].pDllBase      = pDllBase;
    g_hHookedModules[g_dwHookedModuleCount].ulSizeOfImage = ulSizeOfImage;
    g_hHookedModules[g_dwHookedModuleCount].bInSystem32   = bInSystem32;

    strcpy(g_hHookedModules[g_dwHookedModuleCount++].szModuleName, szBaseDllName);

    return TRUE;
}


//
// NOTE: This used to be an exported function in the Win2k shim engine so
//       let's not change its name.
//

BOOL
PatchNewModules(
    BOOL bDynamic
    )
/*++
    Return: STATUS_SUCCESS if successful

    Desc:   Walks the loader list of loaded modules and attempts to patch all
            the modules that are not already patched. It also attempts to
            install the in memory patches.
--*/
{
    PPEB        Peb = NtCurrentPeb();
    PLIST_ENTRY LdrHead;
    PLIST_ENTRY LdrNext;
    DWORD       i;
    BOOL        bInSystem32;

    //
    // Resolve any APIs that became available from newly loaded modules.
    //
    SeiResolveAPIs();

    if (g_bShimInitialized) {
        DPF(dlInfo, "[PatchNewModules] Dynamic loaded modules\n");
    }

    //
    // Try to apply memory patches.
    //
    SeiAttemptPatches();

    //
    // Return if only patches were required.
    //
    if (g_dwShimsCount == 0) {
        return TRUE;
    }

    //
    // Loop through the loaded modules
    //
    LdrHead = &Peb->Ldr->InMemoryOrderModuleList;

    LdrNext = LdrHead->Flink;

    while (LdrNext != LdrHead) {

        PLDR_DATA_TABLE_ENTRY LdrEntry;

        LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);

        if ((SSIZE_T)LdrEntry->DllBase < 0) {
            DPF(dlWarning, "[PatchNewModules] Not hooking kernel-mode DLL \"%S\"\n",
                LdrEntry->BaseDllName.Buffer);
            goto Continue;
        }

        //
        // Don't hook our shim DLLs!
        //
        for (i = 0; i < g_dwShimsCount; i++) {
            if (g_pShimInfo[i].pDllBase == LdrEntry->DllBase) {
                goto Continue;
            }
        }

        //
        // Don't hook the shim engine!
        //
        if (LdrEntry->DllBase == g_pShimEngModHandle) {
            goto Continue;
        }

        //
        // Do nothing if it's already hooked.
        //
        if (!bDynamic) {
            for (i = 0; i < g_dwHookedModuleCount; i++) {
                if (LdrEntry->DllBase == g_hHookedModules[i].pDllBase) {
                    goto Continue;
                }
            }
        }

        //
        // Make sure we're not hooking more DLLs than we can.
        //
        if (g_dwHookedModuleCount == SHIM_MAX_HOOKED_MODULES - 1) {
            DPF(dlError, "\n[PatchNewModules] Too many modules hooked!!!\n\n");
            ASSERT(g_dwHookedModuleCount < SHIM_MAX_HOOKED_MODULES - 1);
            break;
        }

        //
        // Check if this DLL is in System32 (or WinSxS), and hence a possible candidate for blanket
        // exclusion.
        //
        if ((g_dwSystem32StrLen && _wcsnicmp(g_szSystem32,
                LdrEntry->FullDllName.Buffer, g_dwSystem32StrLen) == 0) ||
                (g_dwSxSStrLen && _wcsnicmp(g_szSxS,
                LdrEntry->FullDllName.Buffer, g_dwSxSStrLen) == 0)) {
            bInSystem32 = TRUE;
        } else {
            bInSystem32 = FALSE;
        }

        //
        // This is a candidate for hooking.
        //
        SeiHookImports(LdrEntry->DllBase,
                       LdrEntry->SizeOfImage,
                       &LdrEntry->BaseDllName,
                       bInSystem32);

Continue:
        LdrNext = LdrEntry->InMemoryOrderLinks.Flink;
    }

    return TRUE;
}

void
SeiBuildGlobalInclList(
    IN  HSDB hSDB               // the handle to the database channel
    )
/*++
    Return: void

    Desc:   This function builds the global inclusion list by reading it from the
            database.
--*/
{
    TAGREF         trDatabase, trLibrary, trInExList, trModule;
    WCHAR          wszModule[MAX_PATH];
    CHAR           szModule[MAX_PATH];
    ANSI_STRING    AnsiString = { 0, sizeof(szModule),  szModule  };
    UNICODE_STRING UnicodeString;
    PINEXMOD       pInExMod;
    SIZE_T         len;
    NTSTATUS       status;

    //
    // See if the list is not already built.
    //
    if (g_pGlobalInclusionList) {
        return;
    }

    trDatabase = SdbFindFirstTagRef(hSDB, TAGID_ROOT, TAG_DATABASE);

    if (trDatabase == TAGREF_NULL) {
        DPF(dlError, "[SeiBuildGlobalInclList] Corrupt database. TAG_DATABASE\n");
        ASSERT(trDatabase != TAGREF_NULL);
        return;
    }

    trLibrary = SdbFindFirstTagRef(hSDB, trDatabase, TAG_LIBRARY);

    if (trLibrary == TAGREF_NULL) {
        DPF(dlError, "[SeiBuildGlobalInclList] Corrupt database. TAG_LIBRARY\n");
        ASSERT(trLibrary != TAGREF_NULL);
        return;
    }

    trInExList = SdbFindFirstTagRef(hSDB, trLibrary, TAG_INEXCLUDE);

    if (trInExList == TAGREF_NULL) {
        DPF(dlWarning, "[SeiBuildGlobalInclList] no global inclusion list.\n");

        //
        // This is not a problem. It just means there is no
        // global inclusion list.
        //
        return;
    }

    if (trInExList != TAGREF_NULL) {
        DPF(dlInfo, "[SeiBuildGlobalInclList] Global inclusion list:\n");
    }

    while (trInExList != TAGREF_NULL) {

        trModule = SdbFindFirstTagRef(hSDB, trInExList, TAG_MODULE);

        if (trModule == TAGREF_NULL) {
            DPF(dlError,
                "[SeiBuildGlobalInclList] Corrupt database. Global exclusion list w/o module\n");
            ASSERT(trModule != TAGREF_NULL);
            return;
        }

        if (!SdbReadStringTagRef(hSDB, trModule, wszModule, MAX_PATH * sizeof(WCHAR))) {
            DPF(dlError,
                "[SeiBuildGlobalInclList] Corrupt database. Inclusion list w/ bad module\n");
            ASSERT(0);
            return;
        }

        //
        // Check for EXE name. The EXE should not be in the global inclusion list.
        //
        if (wszModule[0] == L'$') {
            //
            // The EXE name should not be specified in the global exclusion list.
            //
            DPF(dlError,
                "[SeiBuildGlobalInclList] EXE name used in the global exclusion list!\n");
            ASSERT(0);
            goto Continue;
        }

        RtlInitUnicodeString(&UnicodeString, wszModule);

        status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE);

        if (!NT_SUCCESS(status)) {
            DPF(dlError,
                "[SeiBuildGlobalInclList] 0x%X Cannot convert UNICODE \"%S\" to ANSI\n",
                status, wszModule);
            ASSERT(0);
            return;
        }

        pInExMod = (PINEXMOD)(*g_pfnRtlAllocateHeap)(g_pShimHeap,
                                                     HEAP_ZERO_MEMORY,
                                                     sizeof(INEXMOD));

        if (pInExMod == NULL) {
            DPF(dlError,
                "[SeiBuildGlobalInclList] Failed to allocate %d bytes\n",
                sizeof(INEXMOD));
            return;
        }

        len = strlen(szModule) + 1;

        pInExMod->pszModule = (char*)(*g_pfnRtlAllocateHeap)(g_pShimHeap, 0, len);

        if (pInExMod->pszModule == NULL) {
            DPF(dlError, "[SeiBuildGlobalInclList] Failed to allocate %d bytes\n", len);
            return;
        }

        RtlCopyMemory(pInExMod->pszModule, szModule, len);

        //
        // Link it in the list.
        //
        pInExMod->pNext = g_pGlobalInclusionList;
        g_pGlobalInclusionList = pInExMod;

        DPF(dlInfo, "\t\"%s\"\n", pInExMod->pszModule);

Continue:
        trInExList = SdbFindNextTagRef(hSDB, trLibrary, trInExList);
    }
}

void
SeiEmptyInclExclList(
    IN  DWORD dwCounter
    )
/*++
    Return: void

    Desc:   This function empties the inclusion and exclusion lists for the specified
            shim.
--*/
{
    PINEXMOD pInExMod;
    PINEXMOD pInExFree;

    //
    // First the include list.
    //
    pInExMod = g_pShimInfo[dwCounter].pFirstInclude;

    while (pInExMod != NULL) {
        pInExFree = pInExMod;
        pInExMod  = pInExMod->pNext;

        (*g_pfnRtlFreeHeap)(g_pShimHeap, 0, pInExFree->pszModule);
        (*g_pfnRtlFreeHeap)(g_pShimHeap, 0, pInExFree);
    }

    g_pShimInfo[dwCounter].pFirstInclude = NULL;

    //
    // Now the exclude list.
    //
    pInExMod = g_pShimInfo[dwCounter].pFirstExclude;

    while (pInExMod != NULL) {
        pInExFree = pInExMod;
        pInExMod  = pInExMod->pNext;

        (*g_pfnRtlFreeHeap)(g_pShimHeap, 0, pInExFree->pszModule);
        (*g_pfnRtlFreeHeap)(g_pShimHeap, 0, pInExFree);
    }

    g_pShimInfo[dwCounter].pFirstExclude = NULL;
}


#define MAX_LOCAL_INCLUDES 64     // max of 64 Incl/Excl statements

BOOL
SeiBuildInclExclListForShim(
    IN  HSDB            hSDB,           // handle to the database channel
    IN  TAGREF          trShim,         // TAGREF to the shim entry
    IN  DWORD           dwCounter,      // the index for the shim
    IN  LPCWSTR         pwszExePath     // full path to the EXE
    )
/*++
    Return: STATUS_SUCCESS on success, STATUS_UNSUCCESSFUL on failure.

    Desc:   This function builds the inclusion and exclusion lists for the
            specified shim.
--*/
{
    TAGREF         trInExList, trModule, trInclude;
    WCHAR          wszModule[MAX_PATH];
    CHAR           szModule[MAX_PATH];
    ANSI_STRING    AnsiString = { 0, sizeof(szModule), szModule };
    UNICODE_STRING UnicodeString;
    PINEXMOD       pInExMod;
    SIZE_T         len;
    int            nInEx;
    BOOL           bInclude;
    DWORD          trArrInEx[MAX_LOCAL_INCLUDES];
    NTSTATUS       status;

    trInExList = SdbFindFirstTagRef(hSDB, trShim, TAG_INEXCLUDE);

    nInEx = 0;

    //
    // Count the number of inclusion/exclusion statements. We need to do
    // this first because the statements are written into the sdb file
    // from bottom to top.
    //
    while (trInExList != TAGREF_NULL && nInEx < MAX_LOCAL_INCLUDES) {

        trArrInEx[nInEx++] = trInExList;

        trInExList = SdbFindNextTagRef(hSDB, trShim, trInExList);

        ASSERT(nInEx <= MAX_LOCAL_INCLUDES);
    }

    if (nInEx == 0) {
        return TRUE;
    }

    nInEx--;

    while (nInEx >= 0) {

        trInExList = trArrInEx[nInEx];

        trInclude = SdbFindFirstTagRef(hSDB, trInExList, TAG_INCLUDE);

        bInclude = (trInclude != TAGREF_NULL);

        trModule = SdbFindFirstTagRef(hSDB, trInExList, TAG_MODULE);

        if (trModule == TAGREF_NULL) {
            DPF(dlError,
                "[SeiBuildInclExclListForShim] Corrupt database. Incl/Excl list w/o module\n");
            ASSERT(trModule != TAGREF_NULL);
            return FALSE;
        }

        if (!SdbReadStringTagRef(hSDB, trModule, wszModule, MAX_PATH * sizeof(WCHAR))) {
            DPF(dlError,
                "[SeiBuildInclExclListForShim] Corrupt database. Incl/Excl list w/ bad module\n");
            ASSERT(0);
            return FALSE;
        }

        //
        // Special case for '*'. '*' means all modules.
        //
        // NOTE: this option is ignored for dynamic shimming.
        //
        if (wszModule[0] == L'*') {

            if (bInclude) {
                //
                // This is INCLUDE MODULE="*"
                // Mark that we are in INCLUDE_ALL mode.
                //
                g_pShimInfo[dwCounter].eInExMode = INCLUDE_ALL;
            } else {
                //
                // This is EXCLUDE MODULE="*"
                // Mark that we are in EXCLUDE_ALL mode.
                //
                g_pShimInfo[dwCounter].eInExMode = EXCLUDE_ALL;
            }

            SeiEmptyInclExclList(dwCounter);

        } else {

            if (wszModule[0] == L'$') {
                //
                // Special case for EXE name. Get the name of the executable.
                //
                LPCWSTR pwszWalk = pwszExePath + wcslen(pwszExePath);

                while (pwszWalk >= pwszExePath) {
                    if (*pwszWalk == '\\') {
                        break;
                    }
                    pwszWalk--;
                }

                wcscpy(wszModule, pwszWalk + 1);

                DPF(dlInfo,
                    "[SeiBuildInclExclListForShim] EXE name resolved to \"%S\".\n",
                    wszModule);
            }

            RtlInitUnicodeString(&UnicodeString, wszModule);

            status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE);

            if (!NT_SUCCESS(status)) {
                DPF(dlError,
                    "[SeiBuildInclExclListForShim] 0x%X Cannot convert UNICODE \"%S\" to ANSI\n",
                    status, wszModule);
                ASSERT(0);

                return FALSE;
            }

            //
            // Add the module to the correct list.
            //
            pInExMod = (PINEXMOD)(*g_pfnRtlAllocateHeap)(g_pShimHeap,
                                                         HEAP_ZERO_MEMORY,
                                                         sizeof(INEXMOD));
            if (pInExMod == NULL) {
                DPF(dlError,
                    "[SeiBuildInclExclListForShim] Failed to allocate %d bytes\n",
                    sizeof(INEXMOD));
                return FALSE;
            }

            len = strlen(szModule) + 1;

            pInExMod->pszModule = (char*)(*g_pfnRtlAllocateHeap)(g_pShimHeap, 0, len);

            if (pInExMod->pszModule == NULL) {
                DPF(dlError,
                    "[SeiBuildInclExclListForShim] Failed to allocate %d bytes\n", len);
                return FALSE;
            }

            RtlCopyMemory(pInExMod->pszModule, szModule, len);

            //
            // Link it in the list.
            //
            if (bInclude) {
                pInExMod->pNext = g_pShimInfo[dwCounter].pFirstInclude;
                g_pShimInfo[dwCounter].pFirstInclude = pInExMod;
            } else {
                pInExMod->pNext = g_pShimInfo[dwCounter].pFirstExclude;
                g_pShimInfo[dwCounter].pFirstExclude = pInExMod;
            }

            //
            // See if this module is in the other list, and take it out.
            //
            {
                PINEXMOD  pInExFree;
                PINEXMOD* ppInExModX;

                if (bInclude) {
                    ppInExModX = &g_pShimInfo[dwCounter].pFirstExclude;
                } else {
                    ppInExModX = &g_pShimInfo[dwCounter].pFirstInclude;
                }

                while (*ppInExModX != NULL) {

                    if (_stricmp((*ppInExModX)->pszModule, szModule) == 0) {

                        pInExFree = *ppInExModX;

                        *ppInExModX = pInExFree->pNext;

                        (*g_pfnRtlFreeHeap)(g_pShimHeap, 0, pInExFree->pszModule);
                        (*g_pfnRtlFreeHeap)(g_pShimHeap, 0, pInExFree);
                        break;
                    }

                    ppInExModX = &(*ppInExModX)->pNext;
                }
            }
        }

        nInEx--;
    }

    return TRUE;
}

BOOL
SeiCopyGlobalInclList(
    IN  DWORD dwCounter
    )
/*++
    Return: STATUS_SUCCESS on success, STATUS_UNSUCCESSFUL on failure.

    Desc:   This function copies the global inclusion list.
--*/
{
    PINEXMOD pInExModX;
    SIZE_T   len;
    PINEXMOD pInExMod = g_pGlobalInclusionList;

    //
    // Don't do it if we already added it.
    //
    if (g_pShimInfo[dwCounter].pFirstInclude != NULL) {
        return TRUE;
    }

    while (pInExMod != NULL) {
        pInExModX = (PINEXMOD)(*g_pfnRtlAllocateHeap)(g_pShimHeap,
                                                      HEAP_ZERO_MEMORY,
                                                      sizeof(INEXMOD));
        if (pInExModX == NULL) {
            DPF(dlError,
                "[SeiCopyGlobalInclList] (1) Failed to allocate %d bytes\n",
                sizeof(INEXMOD));
            return FALSE;
        }

        len = strlen(pInExMod->pszModule) + 1;

        pInExModX->pszModule = (char*)(*g_pfnRtlAllocateHeap)(g_pShimHeap, 0, len);

        if (pInExModX->pszModule == NULL) {
            DPF(dlError,
                "[SeiCopyGlobalInclList] (2) Failed to allocate %d bytes\n", len);
            return FALSE;
        }

        RtlCopyMemory(pInExModX->pszModule, pInExMod->pszModule, len);

        //
        // Link it in the list.
        //
        pInExModX->pNext = g_pShimInfo[dwCounter].pFirstInclude;
        g_pShimInfo[dwCounter].pFirstInclude = pInExModX;

        pInExMod = pInExMod->pNext;
    }

    return TRUE;
}


BOOL
SeiBuildInclListWithOneModule(
    IN  DWORD  dwCounter,
    IN  LPCSTR lpszModuleToShim
    )
{
    PINEXMOD pInExMod;
    int      len;

    g_pShimInfo[dwCounter].eInExMode = EXCLUDE_ALL;

    //
    // Add the module to the correct list.
    //
    pInExMod = (PINEXMOD)(*g_pfnRtlAllocateHeap)(g_pShimHeap,
                                                 HEAP_ZERO_MEMORY,
                                                 sizeof(INEXMOD));
    if (pInExMod == NULL) {
        DPF(dlError,
            "[SeiBuildInclListWithOneModule] Failed to allocate %d bytes\n",
            sizeof(INEXMOD));
        return FALSE;
    }

    len = strlen(lpszModuleToShim) + 1;

    pInExMod->pszModule = (char*)(*g_pfnRtlAllocateHeap)(g_pShimHeap, 0, len);

    if (pInExMod->pszModule == NULL) {
        DPF(dlError,
            "[SeiBuildInclListWithOneModule] Failed to allocate %d bytes\n", len);
        return FALSE;
    }

    RtlCopyMemory(pInExMod->pszModule, lpszModuleToShim, len);

    //
    // Add it to the list.
    //
    pInExMod->pNext = g_pShimInfo[dwCounter].pFirstInclude;
    g_pShimInfo[dwCounter].pFirstInclude = pInExMod;

    return TRUE;
}

BOOL
SeiBuildInclExclList(
    IN  HSDB            hSDB,       // handle to the database channel
    IN  TAGREF          trShimRef,  // The TAGREF to the shim DLL for which to read the
                                    // inclusion or exclusion list from the database.
    IN  DWORD           dwCounter,  // Index in the g_pShimInfo array for this shim DLL.
    IN  LPCWSTR         pwszExePath // The full path name of the main EXE.
    )
/*++
    Return: STATUS_SUCCESS if successful.

    Desc:   This function builds the inclusion or exclusion list for the specified
            shim DLL by reading it from the database.
--*/
{
    TAGREF trShim;

    //
    // Set the default mode to EXCLUDE_SYSTEM32
    //
    g_pShimInfo[dwCounter].eInExMode = EXCLUDE_SYSTEM32;

    trShim = SdbGetShimFromShimRef(hSDB, trShimRef);

    if (trShim == TAGREF_NULL) {
        DPF(dlError,
            "[SeiBuildInclExclList] Corrupt database. Couldn't get the DLL from "
            "the LIBRARY section\n");
        return FALSE;
    }

    //
    // Make a copy of the global exclusion list first.
    //
    if (!SeiCopyGlobalInclList(dwCounter)) {
        DPF(dlError,
            "[SeiBuildInclExclList] SeiCopyGlobalInclList failed\n");
        return FALSE;
    }

    //
    // Get DLL specific incl/excl list first.
    //
    if (!SeiBuildInclExclListForShim(hSDB, trShim, dwCounter, pwszExePath)) {
        DPF(dlError,
            "[SeiBuildInclExclList] (1) Corrupt database. Couldn't build incl/excl list\n");
        return FALSE;
    }

    //
    // Now get the incl/excl specified for this shim within its parent EXE tag.
    //
    if (!SeiBuildInclExclListForShim(hSDB, trShimRef, dwCounter, pwszExePath)) {
        DPF(dlError,
            "[SeiBuildInclExclList] (2) Corrupt database. Couldn't build incl/excl list\n");
        return FALSE;
    }

#if DBG
    //
    // Print the incl/excl list for this shim.
    //
    if (g_pShimInfo[dwCounter].pFirstInclude != NULL) {
        PINEXMOD pInExMod;

        DPF(dlInfo, "[SeiBuildInclExclList] Inclusion list for \"%S\"\n",
            g_pShimInfo[dwCounter].pLdrEntry->BaseDllName.Buffer);

        pInExMod = g_pShimInfo[dwCounter].pFirstInclude;

        while (pInExMod != NULL) {
            DPF(dlInfo, "\t\"%s\"\n", pInExMod->pszModule);

            pInExMod = pInExMod->pNext;
        }
    }

    if (g_pShimInfo[dwCounter].pFirstExclude != NULL) {
        PINEXMOD pInExMod;

        DPF(dlInfo, "[SeiBuildInclExclList] Exclusion list for \"%S\"\n",
            g_pShimInfo[dwCounter].pLdrEntry->BaseDllName.Buffer);

        pInExMod = g_pShimInfo[dwCounter].pFirstExclude;

        while (pInExMod != NULL) {
            DPF(dlInfo, "\t\"%s\"\n", pInExMod->pszModule);

            pInExMod = pInExMod->pNext;
        }
    }
#endif // DBG

    return TRUE;
}

PLDR_DATA_TABLE_ENTRY
SeiGetLoaderEntry(
    IN  PPEB  Peb,              // The PEB
    IN  PVOID pDllBase          // The address of the shim DLL to be removed from
                                // the loader's lists.
    )
/*++
    Return: Pointer to the loader entry for the shim DLL being removed.

    Desc:   This function removes the shim DLLs from the loader's lists.
--*/
{
    PLIST_ENTRY           LdrHead;
    PLIST_ENTRY           LdrNext;
    PLDR_DATA_TABLE_ENTRY LdrEntry;

    LdrHead = &Peb->Ldr->InMemoryOrderModuleList;

    LdrNext = LdrHead->Flink;

    while (LdrNext != LdrHead) {

        LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);

        if (LdrEntry->DllBase == pDllBase) {
            break;
        }

        LdrNext = LdrEntry->InMemoryOrderLinks.Flink;
    }

    if (LdrNext != LdrHead) {
        return LdrEntry;
    }

    DPF(dlError, "[SeiGetLoaderEntry] Couldn't find shim DLL in the loader list!\n");
    ASSERT(0);

    return NULL;
}

void
SeiLoadPatches(
    IN  HSDB   hSDB,            // handle to the database channel
    IN  TAGREF trExe            // TAGREF of the EXE for which to get the memory
                                // patches from the database
    )
/*++
    Return: void

    Desc:   This function reads the memory patches from the database and
            stores them in the g_pMemoryPatches array.
--*/
{
    TAGREF trPatchRef;
    DWORD  dwSize;

    //
    // Read the patches for this EXE.
    //
    trPatchRef = SdbFindFirstTagRef(hSDB, trExe, TAG_PATCH_REF);

    while (trPatchRef != TAGREF_NULL) {
        //
        // Get the size of this patch.
        //
        dwSize = 0;

        SdbReadPatchBits(hSDB, trPatchRef, NULL, &dwSize);

        if (dwSize == 0) {
            DPF(dlError, "[SeiLoadPatches] returned 0 for patch size.\n");
            ASSERT(dwSize != 0);
            return;
        }

        if (g_dwMemoryPatchCount == SHIM_MAX_PATCH_COUNT) {
            DPF(dlError, "[SeiLoadPatches] Too many patches.\n");
            return;
        }

        //
        // Allocate memory for the patch bits.
        //
        g_pMemoryPatches[g_dwMemoryPatchCount] = (PBYTE)(*g_pfnRtlAllocateHeap)(g_pShimHeap,
                                                                                HEAP_ZERO_MEMORY,
                                                                                dwSize);

        if (g_pMemoryPatches[g_dwMemoryPatchCount] == NULL) {
            DPF(dlError, "[SeiLoadPatches] Failed to allocate %d bytes for patch.\n",
                dwSize);
            return;
        }

        //
        // Read the patch bits from the database.
        //
        if (!SdbReadPatchBits(hSDB,
                              trPatchRef,
                              g_pMemoryPatches[g_dwMemoryPatchCount],
                              &dwSize)) {
            DPF(dlError, "[SeiLoadPatches] Failure getting patch bits.\n");
            ASSERT(0);
            return;
        }

        g_dwMemoryPatchCount++;

        //
        // Get the next patch.
        //
        trPatchRef = SdbFindNextTagRef(hSDB, trExe, trPatchRef);
    }
}

BOOL
SeiGetModuleHandle(
    IN  LPSTR  pszModule,
    OUT PVOID* pModuleHandle
    )
/*++
    Return: void

    Desc:   This function loops through the loaded modules and gets the
            handle of the specified named module.
--*/
{
    ANSI_STRING       AnsiString;
    WCHAR             wszDllName[128];
    UNICODE_STRING    UnicodeString  = { 0, sizeof(wszDllName), wszDllName };
    NTSTATUS          status;

    RtlInitAnsiString(&AnsiString, pszModule);

    if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&UnicodeString,
                                                 &AnsiString,
                                                 FALSE))){

        DPF(dlError,
            "[SeiGetModuleHandle] Failed to convert \"%s\" to UNICODE.\n",
            pszModule);
        return FALSE;
    }

    status = LdrGetDllHandle(NULL,
                             NULL,
                             &UnicodeString,
                             pModuleHandle);

    if (!NT_SUCCESS(status)) {
        DPF(dlError,
            "[SeiGetModuleHandle] Failed to get the handle for \"%s\".\n",
            pszModule);
        return FALSE;
    }

    return TRUE;
}

void
SeiRemoveDll(
    IN  LPSTR pszBaseDllName    // the named of the unloaded module
    )
/*++
    Return: void

    Desc:   This function loops through the loaded shims info and resets
            the resolved APIs that belong to the specified module that had
            just been unloaded.
--*/
{
    DWORD i, j;

    for (i = 0; i < g_dwShimsCount; i++) {
        for (j = 0; j < g_pShimInfo[i].dwHookedAPIs; j++) {
            if (g_pHookArray[i][j].pszModule != NULL &&
                _strcmpi(g_pHookArray[i][j].pszModule, pszBaseDllName) == 0) {

                if ((ULONG_PTR)g_pHookArray[i][j].pszFunctionName < 0x0000FFFF) {
                    DPF(dlWarning,
                        "[SeiRemoveDll] \"%s!#%d\" not resolved again\n",
                        g_pHookArray[i][j].pszModule,
                        g_pHookArray[i][j].pszFunctionName);
                } else {
                    DPF(dlWarning,
                        "[SeiRemoveDll] \"%s!%s\" not resolved again\n",
                        g_pHookArray[i][j].pszModule,
                        g_pHookArray[i][j].pszFunctionName);
                }

                g_pHookArray[i][j].pfnOld = NULL;
                g_pShimInfo[i].dwFlags &= ~SIF_RESOLVED;
            }
        }
    }
}

BOOL
SeiGetModuleByAddress(
    PVOID   pAddress,
    CHAR*   pszModuleName,
    BOOL*   pbInSystem32
    )
{
    DWORD i;
    
    for (i = 0; i < g_dwHookedModuleCount; i++) {
        if ((ULONG_PTR)pAddress >= (ULONG_PTR)g_hHookedModules[i].pDllBase &&
            (ULONG_PTR)pAddress < (ULONG_PTR)g_hHookedModules[i].pDllBase + (ULONG_PTR)g_hHookedModules[i].ulSizeOfImage) {

            //
            // We found the DLL in the hooked list.
            //
            strcpy(pszModuleName, g_hHookedModules[i].szModuleName);
            
            *pbInSystem32 = g_hHookedModules[i].bInSystem32;

            return TRUE;
        }
    }
    
    return FALSE;
}

PVOID
StubGetProcAddress(
    IN  HMODULE hMod,
    IN  LPSTR   pszProc
    )
/*++
    Return: The address of the function specified.

    Desc:   Intercepts calls to GetProcAddress to look for hooked functions. If
            a function was hooked, return the top-most stub function.
--*/
{
    DWORD             i, j;
    DWORD             dwDllIndex;
    PHOOKAPI          pTopHookAPI = NULL;
    PVOID             pfn;
    PFNGETPROCADDRESS pfnOld;
    PVOID             retAddress = NULL;
    ULONG             ulHash;
    CHAR              szBaseDllName[MAX_PATH];
    BOOL              bInSystem32;

    pfnOld = g_IntHookAPI[IHA_GetProcAddress].pfnOld;

    pfn = (*pfnOld)(hMod, pszProc);

    if (pfn == NULL) {
        return NULL;
    }

    for (i = 0; i < g_dwShimsCount; i++) {
        for (j = 0; j < g_pShimInfo[i].dwHookedAPIs; j++) {
            if (g_pHookArray[i][j].pfnOld == pfn) {

                pTopHookAPI = SeiConstructChain(pfn, &dwDllIndex);

                if (pTopHookAPI == NULL) {
                    DPF(dlError,
                        "[StubGetProcAddress] failed to construct the chain for pfn 0x%p\n",
                        pfn);
                    return pfn;
                }

                //
                // Probably we care about inclusion/exclusion lists here as well.
                //
                
                RtlCaptureStackBackTrace(1, 1, &retAddress, &ulHash);
                
                DPF(dlPrint,
                    "[StubGetProcAddress] Stack capture caller 0x%p\n",
                    retAddress);

                if (retAddress && SeiGetModuleByAddress(retAddress, szBaseDllName, &bInSystem32)) {
                    
                    if (SeiIsExcluded(szBaseDllName, pTopHookAPI, bInSystem32)) {
                        return pfn;
                    }
                }
                
                if ((ULONG_PTR)pTopHookAPI->pszFunctionName < 0x0000FFFF) {
                    DPF(dlInfo,
                        "[StubGetProcAddress] called for \"%s!#%d\" 0x%p changed to 0x%p\n",
                        pTopHookAPI->pszModule,
                        pTopHookAPI->pszFunctionName,
                        pfn,
                        pTopHookAPI->pfnNew);
                } else {
                    DPF(dlInfo,
                        "[StubGetProcAddress] called for \"%s!%s\" 0x%p changed to 0x%p\n",
                        pTopHookAPI->pszModule,
                        pTopHookAPI->pszFunctionName,
                        pfn,
                        pTopHookAPI->pfnNew);
                }

                return pTopHookAPI->pfnNew;
            }
        }
    }

    return pfn;
}

#ifdef SE_WIN2K

//
// The Win2k engine needs to hook a few other APIs as well.
//

HMODULE
StubLoadLibraryA(
    IN  LPCSTR pszModule
    )
{
    HMODULE         hMod;
    PFNLOADLIBRARYA pfnOld;
    DWORD           i;

    pfnOld = g_IntHookAPI[IHA_LoadLibraryA].pfnOld;

    hMod = (*pfnOld)(pszModule);

    if (hMod == NULL) {
        return NULL;
    }

    //
    // Was this DLL already loaded ?
    //
    for (i = 0; i < g_dwHookedModuleCount; i++) {
        if (hMod == g_hHookedModules[i].pDllBase) {
            DPF(dlInfo,
                "[StubLoadLibraryA] DLL \"%s\" was already loaded.\n",
                pszModule);
            return hMod;
        }
    }

    PatchNewModules(FALSE);

    return hMod;
}

HMODULE
StubLoadLibraryW(
    IN  LPCWSTR pszModule
    )
{
    HMODULE         hMod;
    PFNLOADLIBRARYW pfnOld;
    DWORD           i;

    pfnOld = g_IntHookAPI[IHA_LoadLibraryW].pfnOld;

    hMod = (*pfnOld)(pszModule);

    if (hMod == NULL) {
        return NULL;
    }

    //
    // Was this DLL already loaded ?
    //
    for (i = 0; i < g_dwHookedModuleCount; i++) {
        if (hMod == g_hHookedModules[i].pDllBase) {
            DPF(dlInfo,
                "[StubLoadLibraryW] DLL \"%S\" was already loaded.\n",
                pszModule);
            return hMod;
        }
    }

    PatchNewModules(FALSE);

    return hMod;
}

HMODULE
StubLoadLibraryExA(
    IN  LPCSTR pszModule,
    IN  HANDLE hFile,
    IN  DWORD  dwFlags
    )
{
    HMODULE           hMod;
    PFNLOADLIBRARYEXA pfnOld;
    DWORD             i;

    pfnOld = g_IntHookAPI[IHA_LoadLibraryExA].pfnOld;

    hMod = (*pfnOld)(pszModule, hFile, dwFlags);

    if (hMod == NULL) {
        return NULL;
    }

    //
    // Was this DLL already loaded ?
    //
    for (i = 0; i < g_dwHookedModuleCount; i++) {
        if (hMod == g_hHookedModules[i].pDllBase) {
            DPF(dlInfo,
                "[StubLoadLibraryExA] DLL \"%s\" was already loaded.\n",
                pszModule);
            return hMod;
        }
    }

    PatchNewModules(FALSE);

    return hMod;
}

HMODULE
StubLoadLibraryExW(
    IN  LPCWSTR pszModule,
    IN  HANDLE  hFile,
    IN  DWORD   dwFlags
    )
{
    HMODULE           hMod;
    PFNLOADLIBRARYEXW pfnOld;
    DWORD             i;

    pfnOld = g_IntHookAPI[IHA_LoadLibraryExW].pfnOld;

    hMod = (*pfnOld)(pszModule, hFile, dwFlags);

    if (hMod == NULL) {
        return NULL;
    }

    //
    // Was this DLL already loaded ?
    //
    for (i = 0; i < g_dwHookedModuleCount; i++) {
        if (hMod == g_hHookedModules[i].pDllBase) {
            DPF(dlInfo,
                "[StubLoadLibraryExW] DLL \"%S\" was already loaded.\n",
                pszModule);
            return hMod;
        }
    }

    PatchNewModules(FALSE);

    return hMod;
}

BOOL
SeiIsDllLoaded(
    IN  HMODULE                hMod,
    IN  PLDR_DATA_TABLE_ENTRY* pLdrEntry
    )
{
    PPEB        Peb = NtCurrentPeb();
    PLIST_ENTRY LdrHead;
    PLIST_ENTRY LdrNext;
    DWORD       i;

    //
    // Loop through the loaded modules.
    //
    LdrHead = &Peb->Ldr->InMemoryOrderModuleList;

    LdrNext = LdrHead->Flink;

    while (LdrNext != LdrHead) {

        PLDR_DATA_TABLE_ENTRY LdrEntry;

        LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);

        if (LdrEntry->DllBase == hMod) {
            *pLdrEntry = LdrEntry;
            return TRUE;
        }

        LdrNext = LdrEntry->InMemoryOrderLinks.Flink;
    }

    return FALSE;
}

BOOL
StubFreeLibrary(
    IN  HMODULE hLibModule
    )
{
    DWORD                 i, j;
    PFNFREELIBRARY        pfnOld;
    BOOL                  bRet;
    PLDR_DATA_TABLE_ENTRY LdrEntry;
    char                  szBaseDllName[128];

    pfnOld = g_IntHookAPI[IHA_FreeLibrary].pfnOld;

    bRet = (*pfnOld)(hLibModule);

    //
    // See if this DLL is one that we hooked.
    //
    for (i = 0; i < g_dwHookedModuleCount; i++) {
        if (g_hHookedModules[i].pDllBase == hLibModule) {
            break;
        }
    }

    if (i >= g_dwHookedModuleCount) {
        return bRet;
    }

    //
    // Is the DLL still loaded ?
    //
    if (SeiIsDllLoaded(hLibModule, &LdrEntry)) {
        DPF(dlInfo,
            "[StubFreeLibrary] Dll \"%S\" still loaded.\n",
            LdrEntry->BaseDllName.Buffer);
        return bRet;
    }

    strcpy(szBaseDllName, g_hHookedModules[i].szModuleName);

    DPF(dlInfo,
        "[StubFreeLibrary] Removing hooked DLL 0x%p \"%s\"\n",
        hLibModule,
        szBaseDllName);

    //
    // Take it out of the list of hooked modules.
    //
    for (j = i; j < g_dwHookedModuleCount - 1; j++) {
        RtlCopyMemory(g_hHookedModules + j, g_hHookedModules + j + 1, sizeof(HOOKEDMODULE));
    }

    g_hHookedModules[j].pDllBase = NULL;
    strcpy(g_hHookedModules[j].szModuleName, "removed!");

    g_dwHookedModuleCount--;

    //
    // Remove the pfnOld from the HOOKAPIs that were
    // resolved to this DLL
    //
    SeiRemoveDll(szBaseDllName);

    return bRet;
}

#endif // SE_WIN2K


BOOL
SeiInitFileLog(
    IN  LPCWSTR pwszAppName      // The full path of the starting EXE
    )
/*++
    Return: TRUE if the log was initialized.

    Desc:   This function checks an environment variable to determine if logging
            is enabled. If so, it will append a header that tells a new app is
            started.
--*/
{
    NTSTATUS            status;
    UNICODE_STRING      EnvName;
    UNICODE_STRING      EnvValue;
    UNICODE_STRING      FilePath;
    UNICODE_STRING      NtSystemRoot;
    WCHAR               wszEnvValue[128];
    WCHAR               wszLogFile[MAX_PATH];
    HANDLE              hfile;
    OBJECT_ATTRIBUTES   ObjA;
    LARGE_INTEGER       liOffset;
    RTL_RELATIVE_NAME   RelativeName;
    ULONG               uBytes;
    char                szHeader[512];
    char                szFormatHeader[] = "-------------------------------------------\r\n"
                                           " Log  \"%S\"\r\n"
                                           "-------------------------------------------\r\n";
    IO_STATUS_BLOCK     ioStatusBlock;

    RtlInitUnicodeString(&EnvName, L"SHIM_FILE_LOG");

    EnvValue.Buffer = wszEnvValue;
    EnvValue.Length = 0;
    EnvValue.MaximumLength = sizeof(wszEnvValue);

    status = RtlQueryEnvironmentVariable_U(NULL, &EnvName, &EnvValue);

    if (!NT_SUCCESS(status)) {
        DPF(dlInfo, "[SeiInitFileLog] Logging not enabled\n");
        return FALSE;
    }

    FilePath.Buffer = wszLogFile;
    FilePath.Length = 0;
    FilePath.MaximumLength = sizeof(wszLogFile);

    RtlInitUnicodeString(&NtSystemRoot, USER_SHARED_DATA->NtSystemRoot);
    RtlAppendUnicodeStringToString(&FilePath, &NtSystemRoot);
    RtlAppendUnicodeToString(&FilePath, L"\\AppPatch\\");
    RtlAppendUnicodeStringToString(&FilePath, &EnvValue);

    if (!RtlDosPathNameToNtPathName_U(FilePath.Buffer,
                                      &FilePath,
                                      NULL,
                                      &RelativeName)) {
        DPF(dlError,
            "[SeiInitFileLog] Failed to convert path name \"%S\"\n",
            wszLogFile);
        return FALSE;
    }

    InitializeObjectAttributes(&ObjA,
                               &FilePath,
                               OBJ_CASE_INSENSITIVE,
                               NULL,
                               NULL);

    //
    // Open/Create the log file.
    //
    status = NtCreateFile(&hfile,
                          FILE_APPEND_DATA | SYNCHRONIZE,
                          &ObjA,
                          &ioStatusBlock,
                          NULL,
                          FILE_ATTRIBUTE_NORMAL,
                          0,
                          FILE_OPEN_IF,
                          FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
                          NULL,
                          0);

    RtlFreeUnicodeString(&FilePath);

    if (!NT_SUCCESS(status)) {
        DPF(dlError,
            "[SeiInitFileLog] 0x%X Cannot open/create log file \"%S\"\n",
            status, wszLogFile);
        return FALSE;
    }

    //
    // Now write a new line in the log file
    //
    ioStatusBlock.Status = 0;
    ioStatusBlock.Information = 0;

    liOffset.LowPart  = 0;
    liOffset.HighPart = 0;

    uBytes = (ULONG)sprintf(szHeader, szFormatHeader, pwszAppName);

    status = NtWriteFile(hfile,
                         NULL,
                         NULL,
                         NULL,
                         &ioStatusBlock,
                         (PVOID)szHeader,
                         uBytes,
                         &liOffset,
                         NULL);

    NtClose(hfile);

    if (!NT_SUCCESS(status)) {
        DPF(dlError,
            "[SeiInitFileLog] 0x%X Cannot write into the log file \"%S\"\n",
            status, wszLogFile);
        return FALSE;
    }

    return TRUE;
}

LPWSTR
SeiGetLayerName(
    IN  HSDB   hSDB,
    IN  TAGREF trLayer
    )
/*++
    Return: BUGBUG

    Desc:   BUGBUG
--*/
{
    PDB    pdb;
    TAGID  tiLayer, tiName;
    LPWSTR pwszName;

    if (!SdbTagRefToTagID(hSDB, trLayer, &pdb, &tiLayer)) {
        DPF(dlError, "[SeiGetLayerName] Failed to get tag id from tag ref\n");
        return NULL;
    }

    tiName = SdbFindFirstTag(pdb, tiLayer, TAG_NAME);

    if (tiName == TAGID_NULL) {
        DPF(dlError,
            "[SeiGetLayerName] Failed to get the name tag id\n");
        return NULL;
    }

    pwszName = SdbGetStringTagPtr(pdb, tiName);

    if (pwszName == NULL) {
        DPF(dlError,
            "[SeiGetLayerName] Cannot read the name of the layer tag\n");
    }

    return pwszName;
}

void
SeiClearLayerEnvVar(
    void
    )
/*++
    Return: BUGBUG

    Desc:   BUGBUG
--*/
{
    UNICODE_STRING EnvName;
    NTSTATUS       status;

    RtlInitUnicodeString(&EnvName, L"__COMPAT_LAYER");

    status = RtlSetEnvironmentVariable(NULL, &EnvName, NULL);

    if (NT_SUCCESS(status)) {
        DPF(dlInfo, "[SeiClearLayerEnvVar] Cleared env var __COMPAT_LAYER.\n");
    } else {
        DPF(dlError, "[SeiClearLayerEnvVar] Failed to clear __COMPAT_LAYER. 0x%X\n", status);
    }
}

BOOL
SeiIsLayerEnvVarSet(
    void
    )
/*++
    Return: BUGBUG

    Desc:   BUGBUG
--*/
{

    NTSTATUS       status;
    UNICODE_STRING EnvName;
    UNICODE_STRING EnvValue;
    WCHAR          wszEnvValue[128];

    RtlInitUnicodeString(&EnvName, L"__COMPAT_LAYER");

    EnvValue.Buffer = wszEnvValue;
    EnvValue.Length = 0;
    EnvValue.MaximumLength = sizeof(wszEnvValue);

    status = RtlQueryEnvironmentVariable_U(NULL, &EnvName, &EnvValue);

    return NT_SUCCESS(status);
}

BOOL
SeiCheckLayerEnvVarFlags(
    BOOL* pbApplyExes,
    BOOL* pbApplyToSystemExes
    )
/*++
    Return: BUGBUG

    Desc:   BUGBUG
--*/
{
    NTSTATUS       status;
    UNICODE_STRING EnvName;
    UNICODE_STRING EnvValue;
    WCHAR          wszEnvValue[128] = L"";
    LPWSTR         pwszEnvTemp;

    if (pbApplyExes) {
        *pbApplyExes = TRUE;
    }
    if (pbApplyToSystemExes) {
        *pbApplyToSystemExes = FALSE;
    }

    RtlInitUnicodeString(&EnvName, L"__COMPAT_LAYER");

    EnvValue.Buffer = wszEnvValue;
    EnvValue.Length = 0;
    EnvValue.MaximumLength = sizeof(wszEnvValue);

    status = RtlQueryEnvironmentVariable_U(NULL, &EnvName, &EnvValue);

    //
    // Skip over and handle special flag characters
    //    '!' means don't use any EXE entries from the DB
    //    '#' means go ahead and apply layers to system EXEs
    //
    if (NT_SUCCESS(status)) {
        pwszEnvTemp = EnvValue.Buffer;

        while (*pwszEnvTemp) {
            if (*pwszEnvTemp == L'!') {

                if (pbApplyExes) {
                    *pbApplyExes = FALSE;
                }
            } else if (*pwszEnvTemp == L'#') {

                if (pbApplyToSystemExes) {
                    *pbApplyToSystemExes = TRUE;
                }

            } else {
                break;
            }
            pwszEnvTemp++;
        }
    }

    return NT_SUCCESS(status);
}

void
SeiSetLayerEnvVar(
    WCHAR* pwszName
    )
/*++
    Return: BUGBUG

    Desc:   BUGBUG
--*/
{
    NTSTATUS       status;
    UNICODE_STRING EnvName;
    UNICODE_STRING EnvValue;
    WCHAR          wszEnvValue[128];

    RtlInitUnicodeString(&EnvName, L"__COMPAT_LAYER");

    EnvValue.Buffer = wszEnvValue;
    EnvValue.Length = 0;
    EnvValue.MaximumLength = sizeof(wszEnvValue);

    status = RtlQueryEnvironmentVariable_U(NULL, &EnvName, &EnvValue);

    if (NT_SUCCESS(status) && (EnvValue.Buffer[0] == L'!' || EnvValue.Buffer[1] == L'!')) {

        //
        // There should be no way to add extra layers to the list,
        // So we should leave it alone.
        //
        return;
    }

    //
    // We need to set the environment variable.
    //
    if (pwszName != NULL) {

        RtlInitUnicodeString(&EnvValue, pwszName);

        status = RtlSetEnvironmentVariable(NULL, &EnvName, &EnvValue);
        if (NT_SUCCESS(status)) {
            DPF(dlInfo, "[SeiSetLayerEnvVar] Env var set __COMPAT_LAYER=\"%S\"\n", pwszName);
        } else {
            DPF(dlError, "[SeiSetLayerEnvVar] Failed to set __COMPAT_LAYER. 0x%X\n", status);
        }
    }
}


BOOL
SeiAddInternalHooks(
    DWORD dwCounter
    )
/*++
    Return: FALSE if the internal hooks have been already added, TRUE otherwise

    Desc:   BUGBUG
--*/
{
    if (g_bInternalHooksUsed) {
        return FALSE;
    }

    g_bInternalHooksUsed = TRUE;

    ZeroMemory(g_IntHookAPI, sizeof(HOOKAPI) * IHA_COUNT);
    ZeroMemory(g_IntHookEx, sizeof(HOOKAPIEX) * IHA_COUNT);

    g_IntHookAPI[IHA_GetProcAddress].pszModule       = "kernel32.dll";
    g_IntHookAPI[IHA_GetProcAddress].pszFunctionName = "GetProcAddress";
    g_IntHookAPI[IHA_GetProcAddress].pfnNew          = (PVOID)StubGetProcAddress;
    g_IntHookAPI[IHA_GetProcAddress].pHookEx         = &g_IntHookEx[IHA_GetProcAddress];
    g_IntHookAPI[IHA_GetProcAddress].pHookEx->dwShimID = dwCounter;

#ifdef SE_WIN2K

    g_IntHookAPI[IHA_LoadLibraryA].pszModule         = "kernel32.dll";
    g_IntHookAPI[IHA_LoadLibraryA].pszFunctionName   = "LoadLibraryA";
    g_IntHookAPI[IHA_LoadLibraryA].pfnNew            = (PVOID)StubLoadLibraryA;
    g_IntHookAPI[IHA_LoadLibraryA].pHookEx           = &g_IntHookEx[IHA_LoadLibraryA];
    g_IntHookAPI[IHA_LoadLibraryA].pHookEx->dwShimID = dwCounter;

    g_IntHookAPI[IHA_LoadLibraryW].pszModule         = "kernel32.dll";
    g_IntHookAPI[IHA_LoadLibraryW].pszFunctionName   = "LoadLibraryW";
    g_IntHookAPI[IHA_LoadLibraryW].pfnNew            = (PVOID)StubLoadLibraryW;
    g_IntHookAPI[IHA_LoadLibraryW].pHookEx           = &g_IntHookEx[IHA_LoadLibraryW];
    g_IntHookAPI[IHA_LoadLibraryW].pHookEx->dwShimID = dwCounter;

    g_IntHookAPI[IHA_LoadLibraryExA].pszModule       = "kernel32.dll";
    g_IntHookAPI[IHA_LoadLibraryExA].pszFunctionName = "LoadLibraryExA";
    g_IntHookAPI[IHA_LoadLibraryExA].pfnNew          = (PVOID)StubLoadLibraryExA;
    g_IntHookAPI[IHA_LoadLibraryExA].pHookEx         = &g_IntHookEx[IHA_LoadLibraryExA];
    g_IntHookAPI[IHA_LoadLibraryExA].pHookEx->dwShimID = dwCounter;

    g_IntHookAPI[IHA_LoadLibraryExW].pszModule       = "kernel32.dll";
    g_IntHookAPI[IHA_LoadLibraryExW].pszFunctionName = "LoadLibraryExW";
    g_IntHookAPI[IHA_LoadLibraryExW].pfnNew          = (PVOID)StubLoadLibraryExW;
    g_IntHookAPI[IHA_LoadLibraryExW].pHookEx         = &g_IntHookEx[IHA_LoadLibraryExW];
    g_IntHookAPI[IHA_LoadLibraryExW].pHookEx->dwShimID = dwCounter;

    g_IntHookAPI[IHA_FreeLibrary].pszModule          = "kernel32.dll";
    g_IntHookAPI[IHA_FreeLibrary].pszFunctionName    = "FreeLibrary";
    g_IntHookAPI[IHA_FreeLibrary].pfnNew             = (PVOID)StubFreeLibrary;
    g_IntHookAPI[IHA_FreeLibrary].pHookEx            = &g_IntHookEx[IHA_FreeLibrary];
    g_IntHookAPI[IHA_FreeLibrary].pHookEx->dwShimID  = dwCounter;

#endif // SE_WIN2K

    //
    // Add the info for our internal hook
    //
    g_pShimInfo[dwCounter].dwHookedAPIs = IHA_COUNT;
    g_pShimInfo[dwCounter].pDllBase     = g_pShimEngModHandle;
    g_pShimInfo[dwCounter].pLdrEntry    = g_pShimEngLdrEntry;
    g_pShimInfo[dwCounter].eInExMode    = INCLUDE_ALL;
    
    wcscpy(g_pShimInfo[dwCounter].wszName, L"SHIMENG.DLL");

    g_pHookArray[dwCounter] = g_IntHookAPI;

    return TRUE;
}

void
NotifyShims(
    int      nReason,
    UINT_PTR extraInfo
    )
{
    DWORD           i, j;
    NTSTATUS        status;
    ANSI_STRING     ProcedureNameString;
    PFNNOTIFYSHIMS  pfnNotifyShims = NULL;

    for (i = 0; i < g_dwShimsCount; i++) {

        for (j = 0; j < i; j++) {
            if (g_pShimInfo[i].pDllBase == g_pShimInfo[j].pDllBase) {
                break;
            }
        }

        if (i == j && g_pShimInfo[i].pLdrEntry != g_pShimEngLdrEntry) {
            //
            // Get the NotifyShims entry point
            //
            RtlInitString(&ProcedureNameString, "NotifyShims");

            status = LdrGetProcedureAddress(g_pShimInfo[i].pDllBase,
                                            &ProcedureNameString,
                                            0,
                                            (PVOID*)&pfnNotifyShims);

            if (!NT_SUCCESS(status) || pfnNotifyShims == NULL) {
                DPF(dlError,
                    "[NotifyShims] Failed to get 'NotifyShims' address, DLL \"%S\"\n",
                    g_pShimInfo[i].wszName);
            } else {
                //
                // Call the notification function.
                //
                (*pfnNotifyShims)(nReason, extraInfo);
            }
        }
    }

    return;
}


void
NotifyShimDlls(
    void
    )
/*++
    Return: void

    Desc:   Notify the shim DLLs that all the static linked modules have run
            their init routines.
--*/
{
    NotifyShims(SN_STATIC_DLLS_INITIALIZED, 0);

#ifdef SE_WIN2K
    //
    // On Win2k we need to restore the code at the entry point.
    //
    RestoreOriginalCode();
#endif // SE_WIN2K

    return;
}



BOOL
SeiGetExeName(
    PPEB   Peb,
    LPWSTR pwszExeName
    )
/*++
    Return: BUGBUG

    Desc:   BUGBUG
--*/
{
    PLDR_DATA_TABLE_ENTRY Entry;
    PLIST_ENTRY           Head;

    Head = &Peb->Ldr->InLoadOrderModuleList;
    Head = Head->Flink;

    Entry = CONTAINING_RECORD(Head, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);

    wcscpy(pwszExeName, Entry->FullDllName.Buffer);

    //
    // Save the exe name in our global
    //
    wcscpy(g_szExeName, Entry->BaseDllName.Buffer);

#ifdef SE_WIN2K
    InjectNotificationCode(Entry->EntryPoint);
#endif // SE_WIN2K

    return TRUE;
}

#ifndef SE_WIN2K

int
SE_IsShimDll(
    IN  PVOID pDllBase          // The address of a loaded DLL
    )
/*++
    Return: TRUE if the DLL is one of our shim DLLs

    Desc:   This function checks to see if a DLL is one of the shim DLLs
            loaded in this process.
--*/
{
    DWORD i;

    for (i = 0; i < g_dwShimsCount; i++) {
        if (g_pShimInfo[i].pDllBase == pDllBase) {
            return 1;
        }
    }
    
    //
    // Special hack for the apphelp case
    //
    if (pDllBase == g_hApphelpDllHelper) {
        return 1;
    }
    
    return 0;
}


void
SeiSetEntryProcessed(
    IN  PPEB Peb
    )
/*++
    Return: void

    Desc:   This function hacks the loader list of loaded DLLs and marks them
            to tell the loader that they executed their init routines even if
            that is not the case. This needs to be done so that our shim mechanism
            is effective before the staticly loaded module get to execute their
            init routines.
--*/
{
    PLIST_ENTRY           LdrHead;
    PLIST_ENTRY           LdrNext;
    PLDR_DATA_TABLE_ENTRY LdrEntry;

    if (g_bComPlusImage) {
        //
        // COM+ images mess with the loader in ntdll. Don't step on ntdll's
        // toes by messing with LDRP_ENTRY_PROCESSED.
        //
        return;
    }

    //
    // Loop through the loaded modules and set LDRP_ENTRY_PROCESSED as
    // needed. Don't do this for ntdll.dll and kernel32.dll.
    // This needs to be done so when we load the shim DLLs the routines for
    // the statically linked libraries don't get called.
    //
    LdrHead = &Peb->Ldr->InInitializationOrderModuleList;

    LdrNext = LdrHead->Flink;

    while (LdrNext != LdrHead) {

        LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks);

        if (RtlCompareUnicodeString(&Kernel32String, &LdrEntry->BaseDllName, TRUE) != 0 &&
            RtlCompareUnicodeString(&VerifierdllString, &LdrEntry->BaseDllName, TRUE) != 0 &&
            RtlCompareUnicodeString(&NtdllString, &LdrEntry->BaseDllName, TRUE) != 0 &&
            !SE_IsShimDll(LdrEntry->DllBase) &&
            _wcsicmp(LdrEntry->BaseDllName.Buffer, g_wszShimDllInLoading) != 0) {

            LdrEntry->Flags |= LDRP_ENTRY_PROCESSED;

            DPF(dlWarning,
                "[SeiSetEntryProcessed] Touching        0x%X \"%S\"\n",
                LdrEntry->DllBase,
                LdrEntry->BaseDllName.Buffer);
        } else {
            DPF(dlWarning,
                "[SeiSetEntryProcessed] Don't mess with 0x%X \"%S\"\n",
                LdrEntry->DllBase,
                LdrEntry->BaseDllName.Buffer);
        }

        LdrNext = LdrEntry->InInitializationOrderLinks.Flink;
    }

#if DBG

    DPF(dlInfo, "[SeiSetEntryProcessed] In memory:\n");

    LdrHead = &Peb->Ldr->InMemoryOrderModuleList;

    LdrNext = LdrHead->Flink;

    while (LdrNext != LdrHead) {

        LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);

        DPF(dlInfo,
            "\t0x%X \"%S\"\n",
            LdrEntry->DllBase,
            LdrEntry->BaseDllName.Buffer);

        LdrNext = LdrEntry->InMemoryOrderLinks.Flink;
    }


    DPF(dlInfo, "\n[SeiSetEntryProcessed] In load:\n");

    LdrHead = &Peb->Ldr->InLoadOrderModuleList;

    LdrNext = LdrHead->Flink;

    while (LdrNext != LdrHead) {

        LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);

        DPF(dlInfo,
            "\t0x%X \"%S\"\n",
            LdrEntry->DllBase,
            LdrEntry->BaseDllName.Buffer);

        LdrNext = LdrEntry->InLoadOrderLinks.Flink;
    }
#endif // DBG

}

void
SeiResetEntryProcessed(
    PPEB Peb
    )
/*++
    Return: void

    Desc:   This function restores the flag in the loader's list
            of loaded DLLs that tells they need to run their init
            routines (see LdrpSetEntryProcessed)
--*/
{
    PLIST_ENTRY    LdrHead;
    PLIST_ENTRY    LdrNext;

    if (g_bComPlusImage) {
        //
        // COM+ images mess with the loader in ntdll. Don't step on ntdll's
        // toes by messing with LDRP_ENTRY_PROCESSED.
        //
        return;
    }
    
    //
    // Loop through the loaded modules and remove LDRP_ENTRY_PROCESSED as
    // needed. Don't do this for ntdll.dll, kernel32.dll and all the shim DLLs
    //
    LdrHead = &Peb->Ldr->InInitializationOrderModuleList;

    LdrNext = LdrHead->Flink;

    while (LdrNext != LdrHead) {

        PLDR_DATA_TABLE_ENTRY LdrEntry;

        LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks);

        if (RtlCompareUnicodeString(&Kernel32String, &LdrEntry->BaseDllName, TRUE) != 0 &&
            RtlCompareUnicodeString(&VerifierdllString, &LdrEntry->BaseDllName, TRUE) != 0 &&
            RtlCompareUnicodeString(&NtdllString, &LdrEntry->BaseDllName, TRUE) != 0 &&
            LdrEntry->DllBase != g_pShimEngModHandle &&
            !SE_IsShimDll(LdrEntry->DllBase)) {

            LdrEntry->Flags &= ~LDRP_ENTRY_PROCESSED;

            DPF(dlWarning,
                "[SeiResetEntryProcessed] Reseting        \"%S\"\n",
                LdrEntry->BaseDllName.Buffer);
        } else {
            DPF(dlWarning,
                "[SeiResetEntryProcessed] Don't mess with \"%S\"\n",
                LdrEntry->BaseDllName.Buffer);
        }

        LdrNext = LdrEntry->InInitializationOrderLinks.Flink;
    }
}

void
SE_GetProcAddress(
    PVOID* pProcedureAddress    // Pointer containing the return from the original
                                // LdrGetProcedureAddress. This  functions fils this
                                // pointer with the eventual replacement from the
                                // HOOKAPI arrays
    )
/*++
    Return: void

    Desc:   Scans HOOKAPI arrays for pfnOld and return pfnNew if this is
            a shimmed API
--*/
{
    return;
    UNREFERENCED_PARAMETER(pProcedureAddress);
}

void
SE_DllLoaded(
    PLDR_DATA_TABLE_ENTRY LdrEntry
    )
{
    if (g_bShimInitialized) {

#if DBG
        DWORD i;

        //
        // Was this DLL already loaded ?
        //
        for (i = 0; i < g_dwHookedModuleCount; i++) {
            if (LdrEntry->DllBase == g_hHookedModules[i].pDllBase) {
                DPF(dlError,
                    "[SE_DllLoaded] DLL \"%s\" was already loaded.\n",
                    g_hHookedModules[i].szModuleName);
            }
        }
#endif // DBG

        DPF(dlInfo,
            "[SE_DllLoaded] AFTER INIT. loading DLL \"%S\".\n",
            LdrEntry->BaseDllName.Buffer);

        PatchNewModules(FALSE);

    } else if (g_bShimDuringInit) {
        DPF(dlInfo,
            "[SE_DllLoaded] INIT. loading DLL \"%S\".\n",
            LdrEntry->BaseDllName.Buffer);

        SeiSetEntryProcessed(NtCurrentPeb());
    }

    return;

    UNREFERENCED_PARAMETER(LdrEntry);
}


void
SE_DllUnloaded(
    PLDR_DATA_TABLE_ENTRY Entry // The pointer to the loader entry for the DLL that is
                                // being unloaded.
    )
/*++
    Return: void

    Desc:   This notification comes from LdrUnloadDll. This function looks up
            to see if we have HOOKAPI that have their original functions in
            this DLL. If that is the case then HOOKAPI.pfnOld is set back to
            NULL.
--*/
{
    DWORD       i, j;
    CHAR        szBaseDllName[128];
    ANSI_STRING AnsiString = { 0, sizeof(szBaseDllName), szBaseDllName };

    if (g_dwShimsCount == 0) {
        return;
    }

    //
    // See if this DLL is one that we hooked.
    //
    for (i = 0; i < g_dwHookedModuleCount; i++) {
        if (g_hHookedModules[i].pDllBase == Entry->DllBase) {
            break;
        }
    }

    if (i >= g_dwHookedModuleCount) {
        return;
    }

    DPF(dlWarning,
        "[SEi_DllUnloaded] Removing hooked DLL 0x%p \"%S\"\n",
        Entry->DllBase,
        Entry->BaseDllName.Buffer);

    //
    // Take it out of the list of hooked modules.
    //
    for (j = i; j < g_dwHookedModuleCount - 1; j++) {
        RtlCopyMemory(g_hHookedModules + j, g_hHookedModules + j + 1, sizeof(HOOKEDMODULE));
    }

    g_hHookedModules[j].pDllBase = NULL;
    strcpy(g_hHookedModules[j].szModuleName, "removed!");

    g_dwHookedModuleCount--;

    //
    // Remove the pfnOld from the HOOKAPIs that were
    // resolved to this DLL.
    //
    if (!NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiString, &Entry->BaseDllName, FALSE))) {
        DPF(dlError,
            "[SEi_DllUnloaded] Cannot convert \"%S\" to ANSI\n",
            Entry->BaseDllName.Buffer);
        return;
    }

    SeiRemoveDll(szBaseDllName);
}

BOOLEAN
SE_InstallAfterInit(
    IN  PUNICODE_STRING UnicodeImageName,   // The name of the starting EXE
    IN  PVOID           pShimExeData        // The pointer provided by apphelp.dll
    )
/*++
    Return: FALSE if the shim engine should be unloaded, TRUE otherwise

    Desc:   Calls the notification function for static linked modules.
--*/
{
    NotifyShimDlls();

    if (g_dwShimsCount == 0 && g_dwMemoryPatchCount == 0) {
        return FALSE;
    }

    return TRUE;

    UNREFERENCED_PARAMETER(UnicodeImageName);
    UNREFERENCED_PARAMETER(pShimExeData);
}

#endif // SE_WIN2K

LPWSTR
SeiGetShortName(
    IN  LPCWSTR pwszDLLPath
    )
/*++
    Return: The pointer to the short name from the full path

    Desc:   Gets the pointer to the short name from the full path.
--*/
{
    LPWSTR pwsz;

    pwsz = (LPWSTR)pwszDLLPath + wcslen(pwszDLLPath);

    while (pwsz >= pwszDLLPath) {
        if (*pwsz == L'\\') {
            break;
        }
        pwsz--;
    }

    return pwsz + 1;
}


BOOL
SeiInitGlobals(
    IN  LPCWSTR lpszFullPath
    )
{
    PPEB Peb = NtCurrentPeb();
    BOOL bResult;
    BOOL bWow64 = FALSE;

    if (g_bInitGlobals) {
        return TRUE;
    }

    //
    // Nab the system32 and windows directory path and length.
    //
    wcscpy(g_szSystem32, USER_SHARED_DATA->NtSystemRoot);
    
    IsWow64Process(GetCurrentProcess(), &bWow64);
    
    if (bWow64) {
        wcscat(g_szSystem32, L"\\SysWow64\\");
    } else {
        wcscat(g_szSystem32, L"\\System32\\");
    }

    g_dwSystem32StrLen = wcslen(g_szSystem32);

    wcscpy(g_szSxS, USER_SHARED_DATA->NtSystemRoot);
    wcscat(g_szSxS, L"\\WinSxS\\");

    g_dwSxSStrLen = wcslen(g_szSxS);

    wcscpy(g_szWindir, USER_SHARED_DATA->NtSystemRoot);
    wcscat(g_szWindir, L"\\");
    g_dwWindirStrLen = wcslen(g_szWindir);

    wcscpy(g_szCmdExePath, g_szSystem32);
    wcscat(g_szCmdExePath, L"cmd.exe");

    //
    // Initialize our global function pointers.
    //
    // This is done because these functions may be hooked by a shim and
    // we don't want to trip over a shim hook internally. If one of these
    // functions is hooked, these global pointers will be overwritten
    // with thunk addresses.
    //
    g_pfnRtlAllocateHeap = RtlAllocateHeap;
    g_pfnRtlFreeHeap     = RtlFreeHeap;

    //
    // Set up our own shim heap.
    //
    g_pShimHeap = RtlCreateHeap(HEAP_GROWABLE,
                                0,          // location isn't important
                                64 * 1024,  // 64k is the initial heap size
                                8 * 1024,   // bring in an 1/8 of the reserved pages
                                0,
                                0);
    if (g_pShimHeap == NULL) {
        //
        // We didn't get our heap.
        //
        DPF(dlError, "[SeiInitGlobals] Can't create shim heap.\n");
        return FALSE;
    }

    //
    // Get the DLL handle for this shim engine
    //
#ifdef SE_WIN2K
    bResult = SeiGetModuleHandle("Shim.dll", &g_pShimEngModHandle);
#else
    bResult = SeiGetModuleHandle("ShimEng.dll", &g_pShimEngModHandle);
#endif // SE_WIN2K

    if (!bResult) {
        DPF(dlError, "[SeiInitGlobals] Failed to get the shim engine's handle\n");
        return FALSE;
    }

    g_pShimEngLdrEntry = SeiGetLoaderEntry(Peb, g_pShimEngModHandle);

    //
    // Setup the log file.
    //
    SeiInitFileLog(lpszFullPath);

    g_bInitGlobals = TRUE;

    return TRUE;
}


void
SeiLayersCheck(
    IN  LPCWSTR         lpszFullPath,
    OUT LPBOOL          lpbApplyExes,
    OUT LPBOOL          lpbApplyToSystemExes,
    OUT SDBQUERYRESULT* psdbQuery
    )
{
    BOOL bLayerEnvSet = FALSE;
    BOOL bCmdExe      = FALSE;

    //
    // Get the flags from the environment variable, if any.
    //
    bLayerEnvSet = SeiCheckLayerEnvVarFlags(lpbApplyExes, lpbApplyToSystemExes);

    //
    // make sure we are not cmd.exe
    //
    bCmdExe = (_wcsicmp(lpszFullPath, g_szCmdExePath) == 0);

    //
    // Unless the environment variable has the flag that allows layer shimming of
    // system exes, check for the EXE being in System32 or Windir.
    // If it is, disable any layer that is coming from the environment variable,
    // and clear the environment variable so the layer won't be propagated.
    //
    if (bLayerEnvSet && !*lpbApplyToSystemExes) {
        if (g_dwSystem32StrLen &&
            _wcsnicmp(g_szSystem32, lpszFullPath, g_dwSystem32StrLen) == 0) {

            //
            // In this case, we'll exclude anything in System32 or any
            // subdirectories.
            //
            DPF(dlWarning,
                "[SeiLayersCheck] Won't apply layer to \"%S\" because it is in System32.\n",
                lpszFullPath);

            psdbQuery->atrLayers[0] = TAGREF_NULL;
            if (!bCmdExe) {
                SeiClearLayerEnvVar();
            }

        } else if (!*lpbApplyToSystemExes &&
                   g_dwWindirStrLen &&
                   _wcsnicmp(g_szWindir, lpszFullPath, g_dwWindirStrLen) == 0) {

            DWORD i;
            BOOL  bInWindir = TRUE;

            //
            // The app is somewhere in the windows tree, but we only want to exclude
            // the windows directory, not the subdirectories.
            //
            for (i = g_dwWindirStrLen; lpszFullPath[i] != 0; ++i) {
                if (lpszFullPath[i] == L'\\') {
                    //
                    // It's in a subdirectory.
                    //
                    bInWindir = FALSE;
                    break;
                }
            }

            if (bInWindir) {
                DPF(dlWarning,
                    "[SeiLayersCheck] Won't apply layer(s) to \"%S\" because"
                    " it is in Windows dir.\n",
                    lpszFullPath);
                psdbQuery->atrLayers[0] = TAGREF_NULL;
                if (!bCmdExe) {
                    SeiClearLayerEnvVar();
                }
            }
        }
    }
}

void
SeiPropagateLayers(
    IN  HSDB            hSDB,
    IN  SDBQUERYRESULT* psdbQuery,
    IN  BOOL            bApplyExes,
    IN  BOOL            bApplyToSystemExes,
    OUT LPDWORD         lpdwShimsCount
    )
{
    int    i;
    WCHAR  szFullEnvVar[MAX_PATH];
    TAGREF trShimRef;

    //
    // Count the DLLs that trLayer uses, and put together the environment variable
    //
    szFullEnvVar[0] = 0;

    //
    // Make sure to propagate the flags.
    //
    if (!bApplyExes) {
        wcscat(szFullEnvVar, L"!");
    }

    if (bApplyToSystemExes) {
        wcscat(szFullEnvVar, L"#");
    }

    for (i = 0; i < SDB_MAX_LAYERS && psdbQuery->atrLayers[i] != TAGREF_NULL; ++i) {
        WCHAR* pszEnvVar;

        //
        // Get the environment var and tack it onto the full string
        //
        pszEnvVar = SeiGetLayerName(hSDB, psdbQuery->atrLayers[i]);

        if (pszEnvVar) {
            wcscat(szFullEnvVar, pszEnvVar);
            wcscat(szFullEnvVar, L" ");
        }

        //
        // Keep counting the dlls.
        //
        trShimRef = SdbFindFirstTagRef(hSDB, psdbQuery->atrLayers[i], TAG_SHIM_REF);

        while (trShimRef != TAGREF_NULL) {
            (*lpdwShimsCount)++;
            trShimRef = SdbFindNextTagRef(hSDB, psdbQuery->atrLayers[i], trShimRef);
        }
    }

    //
    // Set the layer environment variable if not set
    //
    if (szFullEnvVar[0] && psdbQuery->atrLayers[0]) {
        SeiSetLayerEnvVar(szFullEnvVar);
    }
}


BOOL
SeiGetShimCommandLine(
    HSDB    hSDB,
    TAGREF  trShimRef,
    LPSTR   lpszCmdLine
    )
{
    TAGREF          trCmdLine;
    WCHAR           wszCmdLine[SHIM_COMMAND_LINE_MAX_BUFFER];


    //
    // Check for command line
    //
    lpszCmdLine[0] = 0;

    trCmdLine = SdbFindFirstTagRef(hSDB, trShimRef, TAG_COMMAND_LINE);

    if (trCmdLine == TAGREF_NULL) {
        return FALSE;
    }

    wszCmdLine[0] = 0;

    if (SdbReadStringTagRef(hSDB, trCmdLine, wszCmdLine, SHIM_COMMAND_LINE_MAX_BUFFER)) {

        UNICODE_STRING  UnicodeString;
        ANSI_STRING     AnsiString = { 0, SHIM_COMMAND_LINE_MAX_BUFFER, lpszCmdLine };
        NTSTATUS        status;

        RtlInitUnicodeString(&UnicodeString, wszCmdLine);

        status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE);

        //
        // If conversion is unsuccessful, reset to zero-length string.
        //
        if (!NT_SUCCESS(status)) {
            lpszCmdLine[0] = 0;
            return FALSE;
        }
    }

    return TRUE;
}

#ifndef SE_WIN2K

BOOL
SeiSetApphackFlags(
    HSDB            hSDB,
    SDBQUERYRESULT* psdbQuery
    )
{
    ULARGE_INTEGER  uliKernel;
    ULARGE_INTEGER  uliUser;
    BOOL            bUsingApphackFlags = FALSE;
    PPEB            Peb = NtCurrentPeb();

    SdbQueryFlagMask(hSDB, psdbQuery, TAG_FLAG_MASK_KERNEL, &uliKernel.QuadPart, NULL);
    SdbQueryFlagMask(hSDB, psdbQuery, TAG_FLAG_MASK_USER, &uliUser.QuadPart, NULL);

    Peb->AppCompatFlags.QuadPart     = uliKernel.QuadPart;
    Peb->AppCompatFlagsUser.QuadPart = uliUser.QuadPart;

    if (uliKernel.QuadPart != 0) {
        DPF(dlPrint, "[SeiSetApphackFlags] Using kernel apphack flags 0x%x.\n", uliKernel.LowPart);
        
        swprintf(g_szPipeData, L"%s - Using kernel apphack flags 0x%x",
                 g_szExeName,
                 uliKernel.LowPart);
        
        SeiSendDataToPipe();
        
        bUsingApphackFlags = TRUE;
    }

    if (uliUser.QuadPart != 0) {
        DPF(dlPrint, "[SeiSetApphackFlags] Using user apphack flags 0x%x.\n", uliUser.LowPart);
        
        swprintf(g_szPipeData, L"%s - Using user apphack flags 0x%x",
                 g_szExeName,
                 uliUser.LowPart);
        
        SeiSendDataToPipe();
        
        bUsingApphackFlags = TRUE;
    }

    return bUsingApphackFlags;
}

#endif

typedef struct tagTRSHIM {
    TAGREF trShimRef;
    BOOL   bPlaceholder;
} TRSHIM, *PTRSHIM;

typedef struct tagTRSHIMARRAY {
    int     nShimRefCount;
    int     nShimRefMax;
    TRSHIM* parrShimRef;
} TRSHIMARRAY, *PTRSHIMARRAY;

#define TR_DELTA    4

BOOL
SeiAddShim(
    IN PTRSHIMARRAY pShimArray,
    IN TAGREF       trShimRef,
    IN BOOL         bPlaceholder
    )
{
    if (pShimArray->nShimRefCount >= pShimArray->nShimRefMax) {
        PTRSHIM parrShimRef;
        DWORD   dwSize;

        dwSize = (pShimArray->nShimRefMax + TR_DELTA) * sizeof(TRSHIM);

        parrShimRef = (PTRSHIM)(*g_pfnRtlAllocateHeap)(g_pShimHeap,
                                                       HEAP_ZERO_MEMORY,
                                                       dwSize);

        if (parrShimRef == NULL) {
            DPF(dlError, "[SeiAddShim] Failed to allocate %d bytes.\n", dwSize);
            return FALSE;
        }

        memcpy(parrShimRef, pShimArray->parrShimRef, pShimArray->nShimRefMax * sizeof(TRSHIM));

        pShimArray->nShimRefMax += TR_DELTA;

        (*g_pfnRtlFreeHeap)(g_pShimHeap, 0, pShimArray->parrShimRef);

        pShimArray->parrShimRef = parrShimRef;
    }

    pShimArray->parrShimRef[pShimArray->nShimRefCount].trShimRef    = trShimRef;
    pShimArray->parrShimRef[pShimArray->nShimRefCount].bPlaceholder = bPlaceholder;

    (pShimArray->nShimRefCount)++;

    return TRUE;
}

PTRSHIMARRAY
SeiBuildShimRefArray(
    IN  HSDB            hSDB,
    IN  SDBQUERYRESULT* psdbQuery,
    OUT LPDWORD         lpdwShimCount,
    IN  BOOL            bApplyExes,
    IN  BOOL            bApplyToSystemExes,
    IN  BOOL            bIsSetup
    )
{
    DWORD        dw;
    TAGREF       trExe;
    TAGREF       trLayer;
    TAGREF       trShimRef;
    DWORD        dwShimsCount = 0;
    WCHAR        szFullEnvVar[MAX_PATH];
    PTRSHIMARRAY pShimArray;

    *lpdwShimCount = 0;

    pShimArray = (PTRSHIMARRAY)(*g_pfnRtlAllocateHeap)(g_pShimHeap,
                                                       HEAP_ZERO_MEMORY,
                                                       sizeof(TRSHIMARRAY));
    if (pShimArray == NULL) {
        DPF(dlError, "[SeiBuildShimRefArray] Failed to allocate %d bytes.\n", sizeof(TRSHIMARRAY));
        return NULL;
    }

    for (dw = 0; dw < SDB_MAX_EXES; dw++) {

        trExe = psdbQuery->atrExes[dw];

        if (trExe == TAGREF_NULL) {
            break;
        }

        //
        // Count the SHIMs that this EXE uses.
        //
        trShimRef = SdbFindFirstTagRef(hSDB, trExe, TAG_SHIM_REF);

        while (trShimRef != TAGREF_NULL) {

            SeiAddShim(pShimArray, trShimRef, FALSE);
            dwShimsCount++;

            trShimRef = SdbFindNextTagRef(hSDB, trExe, trShimRef);
        }
    }

    //
    // Count the DLLs that trLayer uses, and put together the environment variable
    //
    szFullEnvVar[0] = 0;

    //
    // Make sure to propagate the flags.
    //
    if (!bApplyExes) {
        wcscat(szFullEnvVar, L"!");
    }

    if (bApplyToSystemExes) {
        wcscat(szFullEnvVar, L"#");
    }

    for (dw = 0; dw < SDB_MAX_LAYERS && psdbQuery->atrLayers[dw] != TAGREF_NULL; dw++) {
        WCHAR* pszEnvVar;

        trLayer = psdbQuery->atrLayers[dw];

        //
        // Get the environment var and tack it onto the full string
        //
        pszEnvVar = SeiGetLayerName(hSDB, trLayer);

        if (bIsSetup && !wcscmp(pszEnvVar, L"LUA")) {

            //
            // If the user is trying to apply the LUA layer to a setup program,
            // we ignore it.
            //
            continue;
        }

        if (pszEnvVar) {
            wcscat(szFullEnvVar, pszEnvVar);
            wcscat(szFullEnvVar, L" ");
        }

        //
        // Keep counting the shims.
        //
        trShimRef = SdbFindFirstTagRef(hSDB, trLayer, TAG_SHIM_REF);

        while (trShimRef != TAGREF_NULL) {

            SeiAddShim(pShimArray, trShimRef, FALSE);
            dwShimsCount++;

            trShimRef = SdbFindNextTagRef(hSDB, trLayer, trShimRef);
        }
    }

    //
    // Set the layer environment variable if not set
    //
    if (szFullEnvVar[0] && psdbQuery->atrLayers[0]) {
        SeiSetLayerEnvVar(szFullEnvVar);
    }

    *lpdwShimCount = dwShimsCount;

    return pShimArray;
}

BOOL
SeiIsSetup(
    IN  LPCWSTR pwszFullPath
    )
{
    WCHAR   wszModuleName[MAX_PATH];
    LPWSTR  pwszModuleName = NULL;

    wcsncpy(wszModuleName, pwszFullPath, MAX_PATH);
    wszModuleName[MAX_PATH - 1] = 0;

    pwszModuleName = wcsrchr(wszModuleName, L'\\') + 1;
    _wcslwr(pwszModuleName);

    if (wcsstr(pwszModuleName, L"setup") || wcsstr(pwszModuleName, L"install")) {

        return TRUE;

    } else {

        LPWSTR pwsz;

        if (pwsz = wcsstr(pwszModuleName, L"_ins")) {

            if (wcsstr(pwsz + 4, L"_mp")) {

                return TRUE;
            }
        }
    }

    return FALSE;
}

BOOL
SeiInit(
    IN  LPCWSTR         pwszFullPath,
    IN  HSDB            hSDB,
    OUT SDBQUERYRESULT* psdbQuery,
    IN  LPCSTR          lpszModuleToShim,
    IN  BOOL            bDynamic
    )
/*++
    Return: TRUE on success, FALSE otherwise.

    Desc:   Injects all the shims and patches specified for this EXE
            in the database.
--*/
{
    PPEB         Peb = NtCurrentPeb();
    BOOL         bResult = FALSE;
    TAGREF       trShimRef;
    NTSTATUS     status;
    DWORD        dwCounter = 0;
    WCHAR        wszDLLPath[MAX_PATH];
    WCHAR        wszShimName[MAX_PATH];
    CHAR         szCmdLine[SHIM_COMMAND_LINE_MAX_BUFFER];
    DWORD        dwTotalHooks = 0;
    BOOL         bApplyExes = TRUE;
    BOOL         bApplyToSystemExes = FALSE;
    BOOL         bUsingApphackFlags = FALSE;
    DWORD        dwAPIsHooked = 0;
    DWORD        dw;
    DWORD        dwShimsCount = 0;
    PHOOKAPI*    pHookArray = NULL;
    PSHIMINFO    pShimInfo;
    int          nShimRef;
    PTRSHIMARRAY pShimArray = NULL;
    BOOL         bIsSetup;

    g_bShimDuringInit = TRUE;

#ifndef SE_WIN2K
    if (!bDynamic) {
        //
        // Mark almost all loaded DLLs as if they already run their init routines.
        //
        SeiSetEntryProcessed(Peb);
        
        if (psdbQuery->trAppHelp) {
            if (!SeiDisplayAppHelp(hSDB, psdbQuery)) {
                //
                // We should never end up here because SeiDisplayApphelp
                // calls TerminateProcess before returning FALSE.
                //
                goto cleanup;
            }
        }
    }

#endif // SE_WIN2K

    bIsSetup = SeiIsSetup(pwszFullPath);

    if (!SeiInitGlobals(pwszFullPath)) {
        DPF(dlError, "[SeiInit] Failed to initialize global data\n");
        goto cleanup;
    }

    SeiLayersCheck(pwszFullPath, &bApplyExes, &bApplyToSystemExes, psdbQuery);

    //
    // This should be taken care of by apphelp, but
    // we're taking a belt-and-suspenders approach here.
    //
    if (!bApplyExes) {
        psdbQuery->atrExes[0] = TAGREF_NULL;
    }

    pShimArray = SeiBuildShimRefArray(hSDB,
                                      psdbQuery,
                                      &dwShimsCount,
                                      bApplyExes,
                                      bApplyToSystemExes,
                                      bIsSetup);

    if (pShimArray == NULL) {
        DPF(dlError, "[SeiInit] Failed to build the shimref array\n");
        goto cleanup;
    }

    //
    // Set some global variables so we'll know if we're using a layer,
    // an exe entry, or both.
    //
    // These variables are only used for debug purposes.
    //
    if (psdbQuery->atrExes[0] != TAGREF_NULL) {
        g_bUsingExe = TRUE;
    }

    if (psdbQuery->atrLayers[0] != TAGREF_NULL) {
        g_bUsingLayer = TRUE;
    }

    //
    // Debug spew for matching notification.
    //
    DPF(dlPrint, "[SeiInit] Matched entry: \"%S\"\n", pwszFullPath);

    //
    // Send the name of the process to the pipe
    //
    swprintf(g_szPipeData, L"New process created: %s", pwszFullPath);
    SeiSendDataToPipe();

#ifndef SE_WIN2K

    //
    // Get the apphack flags. These can only be enabled if the shimengine is not
    // dynamically initialized.
    //
    if (!bDynamic && !(bUsingApphackFlags = SeiSetApphackFlags(hSDB, psdbQuery))) {
        DPF(dlPrint, "[SeiInit] No apphack flags for this app \"%S\".\n", pwszFullPath);
    }

#endif // SE_WIN2K

    //
    // See if there are any shims.
    //
    if (dwShimsCount == 0) {
        DPF(dlPrint, "[SeiInit] No new SHIMs for this app \"%S\".\n", pwszFullPath);
        goto OnlyPatches;
    }

    //
    // We need to load the global inclusion/exclusion list if any.
    //
    SeiBuildGlobalInclList(hSDB);

    if (g_dwShimsCount == 0) {
        //
        // Increment the shims count to allow for our internal stubs.
        // Also reserve space for up to MAX_DYNAMIC_SHIMS more dynamic shims.
        //
        dwShimsCount++;

        g_dwMaxShimsCount = dwShimsCount + MAX_DYNAMIC_SHIMS;

        //
        // Allocate a storage pointer for the hook information.
        //
        g_pHookArray = (PHOOKAPI*)(*g_pfnRtlAllocateHeap)(g_pShimHeap,
                                                          HEAP_ZERO_MEMORY,
                                                          sizeof(PHOOKAPI) * g_dwMaxShimsCount);
        if (g_pHookArray == NULL) {
            DPF(dlError,
                "[SeiInit] Failure allocating %d bytes for the hook array\n",
                sizeof(PHOOKAPI) * g_dwMaxShimsCount);
            goto cleanup;
        }

        //
        // Allocate the array that keeps information about the shims.
        //
        g_pShimInfo = (PSHIMINFO)(*g_pfnRtlAllocateHeap)(g_pShimHeap,
                                                         HEAP_ZERO_MEMORY,
                                                         sizeof(SHIMINFO) * g_dwMaxShimsCount);
        if (g_pShimInfo == NULL) {
            DPF(dlError,
                "[SeiInit] Failure allocating %d bytes for the SHIMINFO array\n",
                sizeof(SHIMINFO) * g_dwMaxShimsCount);
            goto cleanup;
        }

        //
        // Point the local variables to the beginning of the arrays.
        //
        pHookArray = g_pHookArray;
        pShimInfo = g_pShimInfo;
    } else {

        if (g_dwShimsCount + dwShimsCount >= g_dwMaxShimsCount) {
            DPF(dlError, "[SeiInit] Too many shims\n");
            goto cleanup;
        }

        //
        // Point the local variables to the end of the existing arrays.
        //
        pHookArray = g_pHookArray + g_dwShimsCount;
        pShimInfo = g_pShimInfo + g_dwShimsCount;
    }

    //
    // Get the first shim.
    //
    nShimRef = 0;

    while (nShimRef < pShimArray->nShimRefCount) {

        PVOID               pModuleHandle = NULL;
        UNICODE_STRING      UnicodeString;
        ANSI_STRING         ProcedureNameString;
        PFNGETHOOKAPIS      pfnGetHookApis = NULL;
        TAGREF              trShimName = TAGREF_NULL;
        LPWSTR              pwszDllShortName;
        DWORD               i, dwShimIndex;

        trShimRef = pShimArray->parrShimRef[nShimRef].trShimRef;

        //
        // Retrieve the shim name.
        //
        wszShimName[0] = 0;
        trShimName = SdbFindFirstTagRef(hSDB, trShimRef, TAG_NAME);
        if (trShimName == TAGREF_NULL) {
            DPF(dlError, "[SeiInit] Could not retrieve shim name tag from entry.\n");
            goto cleanup;
        }

        if (!SdbReadStringTagRef(hSDB, trShimName, wszShimName, MAX_PATH)) {
            DPF(dlError, "[SeiInit] Could not retrieve shim name from entry.\n");
            goto cleanup;
        }

        //
        // Check for duplicate shims.
        //
        for (i = 0; i < g_dwShimsCount + dwCounter; ++i) {
            if (_wcsnicmp(g_pShimInfo[i].wszName, wszShimName, MAX_SHIM_NAME_LEN) == 0) {
                dwShimIndex = i;
                goto nextShim;
            }
        }

        //
        // Save off the name of the shim.
        //
        wcsncpy(pShimInfo[dwCounter].wszName, wszShimName, MAX_SHIM_NAME_LEN);
        pShimInfo[dwCounter].wszName[MAX_SHIM_NAME_LEN] = 0;

        if (!SdbGetDllPath(hSDB, trShimRef, wszDLLPath)) {
            DPF(dlError, "[SeiInit] Failed to get DLL Path\n");
            goto cleanup;
        }

        pwszDllShortName = SeiGetShortName(wszDLLPath);

        RtlInitUnicodeString(&UnicodeString, wszDLLPath);

        //
        // Check if we already loaded this DLL.
        //
        status = LdrGetDllHandle(NULL,
                                 NULL,
                                 &UnicodeString,
                                 &pModuleHandle);

        if (!NT_SUCCESS(status)) {

            //
            // Load the DLL that hosts this shim.
            //

#ifndef SE_WIN2K
            //
            // Save the name of the DLL that we're about to load so we don't screw
            // it's init routine.
            //
            wcscpy(g_wszShimDllInLoading, pwszDllShortName);
#endif // SE_WIN2K

            status = LdrLoadDll(UNICODE_NULL, NULL, &UnicodeString, &pModuleHandle);

            if (!NT_SUCCESS(status)) {
                DPF(dlError,
                    "[SeiInit] Failed to load DLL \"%S\" Status 0x%lx\n",
                    wszDLLPath, status);
                goto cleanup;
            }

            DPF(dlPrint,
                "[SeiInit] Shim DLL 0x%X \"%S\" loaded\n",
                pModuleHandle,
                wszDLLPath);
        }

        DPF(dlPrint, "[SeiInit] Using SHIM \"%S!%S\"\n",
            wszShimName, pwszDllShortName);

        pShimInfo[dwCounter].pDllBase = pModuleHandle;

        //
        // Check for command line.
        //
        if (SeiGetShimCommandLine(hSDB, trShimRef, szCmdLine)) {
            DPF(dlPrint,
                "[SeiInit] Command line for Shim \"%S\" : \"%s\"\n",
                wszShimName,
                szCmdLine);
        }

        //
        // Send this shim name to the pipe
        //
        swprintf(g_szPipeData,
                 L"%s - Applying shim %s(%S) from %s",
                 g_szExeName,
                 wszShimName,
                 szCmdLine,
                 pwszDllShortName);
        
        SeiSendDataToPipe();
        
        //
        // Get the GetHookApis entry point.
        //
        RtlInitString(&ProcedureNameString, "GetHookAPIs");

        status = LdrGetProcedureAddress(pModuleHandle,
                                        &ProcedureNameString,
                                        0,
                                        (PVOID*)&pfnGetHookApis);

        if (!NT_SUCCESS(status) || pfnGetHookApis == NULL) {
            DPF(dlError,
                "[SeiInit] Failed to get 'GetHookAPIs' address, DLL \"%S\"\n",
                wszDLLPath);
            goto cleanup;
        }

        dwTotalHooks = 0;

        //
        // Call the proc and then store away its hook params.
        //
        pHookArray[dwCounter] = (*pfnGetHookApis)(szCmdLine, wszShimName, &dwTotalHooks);
        
        dwAPIsHooked += dwTotalHooks;

        DPF(dlInfo,
            "[SeiInit] GetHookAPIs returns %d hooks for DLL \"%S\" SHIM \"%S\"\n",
            dwTotalHooks, wszDLLPath, wszShimName);

        pShimInfo[dwCounter].dwHookedAPIs = dwTotalHooks;
        pShimInfo[dwCounter].pLdrEntry    = SeiGetLoaderEntry(Peb, pModuleHandle);

        if (dwTotalHooks > 0) {

            //
            // Initialize the HOOKAPIEX structure.
            //
            for (i = 0; i < dwTotalHooks; ++i) {
                PHOOKAPIEX pHookEx;

                pHookEx = (PHOOKAPIEX)(*g_pfnRtlAllocateHeap)(g_pShimHeap,
                                                              HEAP_ZERO_MEMORY,
                                                              sizeof(HOOKAPIEX));

                if (!pHookEx) {
                    DPF(dlError,
                        "[SeiInit] Failed to allocate %d bytes (HOOKAPIEX)\n",
                        sizeof(HOOKAPIEX));
                    goto cleanup;
                }

                pHookArray[dwCounter][i].pHookEx = pHookEx;
                pHookArray[dwCounter][i].pHookEx->dwShimID = g_dwShimsCount + dwCounter;
            }

#if DBG
            //
            // Give a debugger warning if uninitialized HOOKAPI structures
            // are used.
            //
            {
                DWORD dwUninitCount = 0;

                for (i = 0; i < dwTotalHooks; ++i) {
                    if (pHookArray[dwCounter][i].pszModule == NULL ||
                        pHookArray[dwCounter][i].pszFunctionName == NULL) {

                        dwUninitCount++;
                    }
                }

                if (dwUninitCount > 0) {
                    DPF(dlWarning,
                        "[SeiInit] Shim \"%S\" using %d uninitialized HOOKAPI structures.\n",
                        pShimInfo[dwCounter].wszName, dwUninitCount);
                }
            }
#endif // DBG
        }

        dwShimIndex = g_dwShimsCount + dwCounter;
        dwCounter++;

nextShim:
        //
        // Read the inclusion/exclusion list for this shim.
        //
        if (bDynamic && lpszModuleToShim != NULL) {

            if (!SeiBuildInclListWithOneModule(dwShimIndex, lpszModuleToShim)) {
                DPF(dlError,
                    "[SeiInit] Couldn't build the inclusion list w/ one module for Shim \"%S\"\n",
                    wszShimName);
                goto cleanup;
            }
        } else {
            if (!SeiBuildInclExclList(hSDB, trShimRef, dwShimIndex, pwszFullPath)) {
                DPF(dlError,
                    "[SeiInit] Couldn't build the inclusion/exclusion list for Shim \"%S\"\n",
                    wszShimName);
                goto cleanup;
            }
        }

        //
        // Go to the next shim ref.
        //
        nShimRef++;
    }

    if (dwAPIsHooked > 0) {
        //
        // We need to add our internal hooks
        //
        if (SeiAddInternalHooks(dwCounter)) {
            dwCounter++;
        }
    }

    //
    // Update the shim counter.
    //
    g_dwShimsCount += dwCounter;

OnlyPatches:

    for (dw = 0; dw < SDB_MAX_EXES; dw++) {

        if (psdbQuery->atrExes[dw] == TAGREF_NULL) {
            break;
        }

        //
        // Loop through available in-memory patches for this EXE.
        //
        SeiLoadPatches(hSDB, psdbQuery->atrExes[dw]);
    }

    if (g_dwMemoryPatchCount == 0) {
        DPF(dlPrint, "[SeiInit] No patches for this app \"%S\".\n", pwszFullPath);

    }

    if (g_dwMemoryPatchCount == 0 && g_dwShimsCount == 0 && !bUsingApphackFlags) {
        DPF(dlError, "[SeiInit] No fixes in the DB for this app \"%S\".\n", pwszFullPath);
        goto cleanup;
    }

    //
    // Walk the hook list and fixup available APIs
    //
    if (!PatchNewModules(bDynamic)) {
        DPF(dlError, "[SeiInit] Unsuccessful fixing up APIs, EXE \"%S\"\n", pwszFullPath);
        goto cleanup;
    }

    //
    // Notify the shims that static link DLLs have run their init routine.
    //
    if (bDynamic) {
        NotifyShims(SN_STATIC_DLLS_INITIALIZED, 1);
    }

    //
    // The shim has successfuly initialized
    //
    g_bShimInitialized = TRUE;
    bResult = TRUE;

cleanup:

    //
    // Cleanup
    //
    if (pShimArray) {
        if (pShimArray->parrShimRef) {
            (*g_pfnRtlFreeHeap)(g_pShimHeap, 0, pShimArray->parrShimRef);
        }
        (*g_pfnRtlFreeHeap)(g_pShimHeap, 0, pShimArray);
    }

    if (!bDynamic) {
        //
        // We can pass back any one of the exes. The first is fine.
        //
        if (psdbQuery->atrExes[0] != TAGREF_NULL) {
            SdbReleaseMatchingExe(hSDB, psdbQuery->atrExes[0]);
        }

        if (hSDB != NULL) {
            SdbReleaseDatabase(hSDB);
        }

#ifndef SE_WIN2K
        SeiResetEntryProcessed(Peb);
#endif // SE_WIN2K
    }

    g_bShimDuringInit = FALSE;

    if (!bResult) {
#if DBG
        if (!bUsingApphackFlags) {
            DbgPrint("[SeiInit] Shim engine failed to initialize.\n");
        }
#endif // DBG

        //
        // Unload the shim DLLs that are loaded so far.
        //
        // Don't do this during dynamic shimming.
        //
        if (!bDynamic) {
            if (g_pShimInfo != NULL) {
                for (dwCounter = 0; dwCounter < g_dwShimsCount; dwCounter++) {
                    if (g_pShimInfo[dwCounter].pDllBase == NULL) {
                        break;
                    }

                    LdrUnloadDll(g_pShimInfo[dwCounter].pDllBase);
                }
            }

            if (g_pShimHeap != NULL) {
                RtlDestroyHeap(g_pShimHeap);
                g_pShimHeap = NULL;
            }
        }
    }

    return bResult;
}

HSDB
SeiGetShimData(
    IN  PPEB    Peb,
    IN  PVOID   pShimData,
    OUT LPWSTR  pwszFullPath,       // this is supplied on Whistler and returned on Win2k
    OUT SDBQUERYRESULT* psdbQuery
    )
{
    HSDB  hSDB = NULL;
    BOOL  bResult;

#ifdef DEBUG_SPEW
    SeiInitDebugSupport();
#endif // DEBUG_SPEW

    //
    // Get the name of the executable being run.
    //
    if (!SeiGetExeName(Peb, pwszFullPath)) {
        DPF(dlError, "[SeiGetShimData] Can't get EXE name\n");
        return NULL;
    }

    if (!_wcsicmp(g_szExeName, L"ntsd.exe") ||
        !_wcsicmp(g_szExeName, L"windbg.exe")) {
        DPF(dlPrint, "[SeiGetShimData] not shimming ntsd.exe\n");
        return NULL;
    }

    //
    // Open up the Database and see if there's any blob information about this EXE.
    //
    hSDB = SdbInitDatabase(0, NULL);

    if (hSDB == NULL) {
        DPF(dlError, "[SeiGetShimData] Can't open shim DB.\n");
        return NULL;
    }

    //
    // Ensure that the sdbQuery starts out clean.
    //
    ZeroMemory(psdbQuery, sizeof(SDBQUERYRESULT));

#ifdef SE_WIN2K
    bResult = SdbGetMatchingExe(hSDB, pwszFullPath, NULL, NULL, 0, psdbQuery);
#else
    bResult = SdbUnpackAppCompatData(hSDB, pwszFullPath, pShimData, psdbQuery);
#endif // SE_WIN2K

    if (!bResult) {
        DPF(dlError, "[SeiGetShimData] Can't get EXE data\n");
        goto failure;
    }

    return hSDB;

failure:
    SdbReleaseDatabase(hSDB);
    return NULL;
}


#ifdef SE_WIN2K

BOOL
LoadPatchDll(
    IN LPCSTR pszCmdLine        // The command line from the registry.
                                // Unused parameter.
    )
/*++
    Return: TRUE on success, FALSE otherwise. However user32.dll ignores the return
            value of this function.

    Desc:   This function is called from user32.dll to initialize the shim engine.
            It queries the shim database and loads all the shim DLLs and patches that
            are available for this EXE.
--*/
{
    PPEB            Peb = NtCurrentPeb();
    WCHAR           wszFullPath[MAX_PATH];
    HSDB            hSDB;
    SDBQUERYRESULT  sdbQuery;

    hSDB = SeiGetShimData(Peb, NULL, wszFullPath, &sdbQuery);

    if (hSDB == NULL) {
        DPF(dlError, "[LoadPatchDll] Failed to get shim data\n");
        return FALSE;
    }

    return SeiInit(wszFullPath, hSDB, &sdbQuery, NULL, FALSE);
}

#else

BOOL
SeiDisplayAppHelp(
    IN  HSDB            hSDB,
    IN  PSDBQUERYRESULT pSdbQuery
    )
/*++
    Return: void

    Desc:   This function launches apphelp for the starting EXE.
--*/
{
    PDB                 pdb;
    TAGID               tiExe;
    GUID                guidDB;
    WCHAR               wszGuid[MAX_PATH];
    WCHAR               wszCommandLine[MAX_PATH];
    DWORD               dwExit;
    char                szName[50];
    PVOID               hModule, pAddress;
    WCHAR               wszTemp[MAX_PATH];
    UNICODE_STRING      ustrTemp;
    UNICODE_STRING      ustrGuid;
    ANSI_STRING         strName;
    STARTUPINFOW        StartupInfo;
    PROCESS_INFORMATION ProcessInfo;

    //
    // If we have any errors along the way, go ahead and run the app.
    //
    if (!SdbTagRefToTagID(hSDB, pSdbQuery->trAppHelp, &pdb, &tiExe)) {
        DPF(dlError, "[SeiDisplayAppHelp] Failed to convert tagref to tagid.\n");
        return TRUE;
    }

    if (!SdbGetDatabaseGUID(hSDB, pdb, &guidDB)) {
        DPF(dlError, "[SeiDisplayAppHelp] Failed to get DB guid.\n");
        return TRUE;
    }

    ustrGuid.Buffer = wszGuid;
    ustrGuid.Length = 0;
    ustrGuid.MaximumLength = MAX_PATH;

    if (RtlStringFromGUID(&guidDB, &ustrGuid) != STATUS_SUCCESS) {
        DPF(dlError, "[SeiDisplayAppHelp] Failed to convert guid to string.\n");
        return TRUE;
    }

    swprintf(wszCommandLine, L"ahui.exe %s 0x%x", ustrGuid.Buffer, tiExe);

    //
    // We need a hack here to get kernel32.dll to initialize. We load aclayers.dll
    // to trigger the init routine of kernel32.
    //
    SdbpGetAppPatchDir(wszTemp);
    wcscat(wszTemp, L"\\aclayers.dll");
    RtlInitUnicodeString(&ustrTemp, wszTemp);

    //
    // Save the name of the DLL that we're about to load so we don't screw
    // it's init routine.
    //
    wcscpy(g_wszShimDllInLoading, L"aclayers.dll");
    
    LdrLoadDll(UNICODE_NULL, NULL, &ustrTemp, &hModule);

    g_hApphelpDllHelper = hModule;

    RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
    RtlZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
    StartupInfo.cb = sizeof(StartupInfo);

    if (!CreateProcessW(NULL, wszCommandLine, NULL, NULL, FALSE, 0, NULL, NULL,
                        &StartupInfo, &ProcessInfo)) {
        DPF(dlError, "[SeiDisplayAppHelp] Failed to launch apphelp process.\n");
        return TRUE;
    }

    WaitForSingleObject(ProcessInfo.hProcess, INFINITE);

    GetExitCodeProcess(ProcessInfo.hProcess, &dwExit);

    if (!dwExit) {
        SeiResetEntryProcessed(NtCurrentPeb());
        TerminateProcess(GetCurrentProcess(), 0);
        return FALSE;
    }

    return TRUE;

}

#ifdef SE_WIN2K

#define SeiCheckComPlusImage(Peb)

#else
void
SeiCheckComPlusImage(
    PPEB Peb
    )
{
    PIMAGE_NT_HEADERS NtHeader;
    ULONG Cor20HeaderSize;
    
    NtHeader = RtlImageNtHeader(Peb->ImageBaseAddress);
    
    g_bComPlusImage = FALSE;
    
    g_bComPlusImage = (RtlImageDirectoryEntryToData(Peb->ImageBaseAddress,
                                                    TRUE,
                                                    IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR,
                                                    &Cor20HeaderSize) != NULL);
    
    DPF(dlPrint, "[SeiCheckComPlusImage] COM+ executable %s\n",
        (g_bComPlusImage ? "TRUE" : "FALSE"));
}

#endif // SE_WIN2K

void
SE_InstallBeforeInit(
    IN  PUNICODE_STRING pstrFullPath,   // The name of the starting EXE
    IN  PVOID           pShimExeData    // The pointer provided by apphelp.dll
    )
/*++
    Return: void

    Desc:   This function installs the shim support for the starting EXE.
--*/
{
    PPEB            Peb = NtCurrentPeb();
    HSDB            hSDB;
    SDBQUERYRESULT  sdbQuery;

    hSDB = SeiGetShimData(Peb, pShimExeData, pstrFullPath->Buffer, &sdbQuery);

    if (hSDB == NULL) {
        DPF(dlError, "[SE_InstallBeforeInit] Failed to get shim data\n");
        return;
    }

    //
    // Check if the image is a COM+ image
    //
    SeiCheckComPlusImage(Peb);

    SeiInit(pstrFullPath->Buffer, hSDB, &sdbQuery, NULL, FALSE);

    if (pShimExeData != NULL) {

        SIZE_T dwSize;

        dwSize = SdbGetAppCompatDataSize(pShimExeData);

        if (dwSize > 0) {
            NtFreeVirtualMemory(NtCurrentProcess(),
                                &pShimExeData,
                                &dwSize,
                                MEM_RELEASE);
        }
    }

    return;
}

#endif // SE_WIN2K


#ifndef SE_WIN2K

void
SE_ProcessDying(
    void
    )
{
    NotifyShims(SN_PROCESS_DYING, 0);
    return;
}

#endif // SE_WIN2K


BOOL
SE_DynamicShim(
    IN  LPCWSTR         lpszFullPath,
    IN  HSDB            hSDB,
    IN  SDBQUERYRESULT* psdbQuery,
    IN  LPCSTR          lpszModuleToShim
    )
/*++
    Return: TRUE on success, FALSE otherwise.

    Desc:   This function attempts to inject shims dynamically.
--*/
{
    SeiInitDebugSupport();

    if (lpszModuleToShim != NULL && *lpszModuleToShim == 0) {
        lpszModuleToShim = NULL;
    }

    SeiInit(lpszFullPath, hSDB, psdbQuery, lpszModuleToShim, TRUE);

#ifndef SE_WIN2K
    LdrInitShimEngineDynamic(g_hModule);
#endif

    return TRUE;
}


BOOL
SeiSendDataToPipe(
    void
    )
{
    OBJECT_ATTRIBUTES       ObjA;
    FILE_PIPE_INFORMATION   fpi;
    UNICODE_STRING          uName;
    IO_STATUS_BLOCK         ioStatusBlock;
    HANDLE                  hPipe;
    NTSTATUS                status;
    LARGE_INTEGER           liOffset;
    ULONG                   uBytes;
    DWORD                   dwRet, dwCount;

    RtlInitUnicodeString(&uName, PIPE_NAME);

    InitializeObjectAttributes(&ObjA,
                               &uName,
                               OBJ_CASE_INSENSITIVE,
                               NULL,
                               NULL);

    ioStatusBlock.Status = 0;
    ioStatusBlock.Information = 0;

    //
    // Open the named pipe
    //
    status = NtCreateFile(&hPipe,
                          FILE_GENERIC_WRITE,
                          &ObjA,
                          &ioStatusBlock,
                          NULL,
                          FILE_ATTRIBUTE_NORMAL,
                          0,
                          FILE_OPEN,
                          0,
                          NULL,
                          0);

    if ((!NT_SUCCESS(status) || INVALID_HANDLE_VALUE == hPipe)) {
        dwRet = RtlNtStatusToDosError(status);
        
        DPF(dlError,
            "[SeiSendDataToPipe] Failed to open named pipe. Error code: %d\n",
            dwRet);
        return FALSE;
    }

    //
    // Change the mode of the named pipe to message mode
    //
    fpi.ReadMode       = FILE_PIPE_MESSAGE_MODE;
    fpi.CompletionMode = FILE_PIPE_QUEUE_OPERATION;

    status = NtSetInformationFile(hPipe,
                                  &ioStatusBlock,
                                  &fpi,
                                  sizeof(FILE_PIPE_INFORMATION),
                                  FilePipeInformation);

    if (!NT_SUCCESS(status)) {
        dwRet = RtlNtStatusToDosError(status);
        
        DPF(dlError,
            "[SeiSendDataToPipe] Failed to change handle state. Error code: %d\n",
            dwRet);
        return FALSE;
    }

    ioStatusBlock.Status = 0;
    ioStatusBlock.Information = 0;

    liOffset.LowPart  = 0;
    liOffset.HighPart = 0;

    //
    // Send the data to the named pipe
    //
    uBytes = wcslen(g_szPipeData) * sizeof(WCHAR);

    status = NtWriteFile(hPipe,
                         NULL,
                         NULL,
                         NULL,
                         &ioStatusBlock,
                         (PVOID)g_szPipeData,
                         uBytes,
                         &liOffset,
                         NULL);

    if (!NT_SUCCESS(status)) {
        dwRet = RtlNtStatusToDosError(status);
        DPF(dlError,
            "[SeiSendDataToPipe] Failed to send data. Error code: %d\n",
            dwRet);
        NtClose(hPipe);
        return FALSE;
    }

    NtClose(hPipe);

    return TRUE;
}

BOOL WINAPI
DllMain(
    HINSTANCE hInstance,
    DWORD     dwreason,
    LPVOID    reserved
    )
{
    //
    // The init routine for DLL_PROCESS_ATTACH will NEVER be called because
    // ntdll calls LdrpLoadDll for this shim engine w/o calling the init routine.
    // Look in ntdll\ldrinit.c LdrpLoadShimEngine.
    // The only case when we will have hInstance is when we are loaded dynamically
    //
    if (dwreason == DLL_PROCESS_ATTACH) {
        g_hModule = (HMODULE)hInstance;
    }
    return TRUE;

    UNREFERENCED_PARAMETER(reserved);
}