MemoryModule icon indicating copy to clipboard operation
MemoryModule copied to clipboard

Create Activation Context Implementation (with code)

Open Elmue opened this issue 4 years ago • 0 comments

Whenever you load a DLL or start an EXE which has a manifest you should create an AcrivationContext before loading any dependent libraries (before calling BuildImportTable())

This is required when any of the imported libraries exists in multiple versions in the SXS Side-by-Side store of Windows. The ActivationContext is required to tell Windows for example which version of COMCTL32.dll or MSVCR80.dll has to be loaded.

I made lots of changes in the original code. Therefore I don't make a pull request now. Apart from that Joachim Bauch does not merge pull requests anymore. He does not even answer emails.

So here is my code:

// Required for all DLL's and EXE's which load dependent SXS DLL's like COMCTL32.dll or MSVCR80.dll
// Returns API error
DWORD cMemMod::CreateActivationContext(PMEMORYMODULE module, HANDLE* ph_ActCtx, ULONG_PTR* pu32_ActCtxCookie)
{
    DWORD u32_Error = 0;

    LPCWSTR u16_Manifest = module->isDLL ? ISOLATIONAWARE_MANIFEST_RESOURCE_ID : CREATEPROCESS_MANIFEST_RESOURCE_ID;
    HRSRC   h_Resource   = FindResource((HMODULE)module->codeBase, u16_Manifest, RT_MANIFEST);
    if (!h_Resource)
        return 0; // The EXE / DLL has no manifest

    BYTE* u8_MemPtr = (BYTE*)LoadResource  ((HMODULE)module->codeBase, h_Resource);
    DWORD u32_Size  =        SizeofResource((HMODULE)module->codeBase, h_Resource);

    WCHAR u16_TempPath[MAX_PATH];
    GetTempPath(MAX_PATH, u16_TempPath); // terminated with backslash

    // Create a random temporary file name
    WCHAR u16_TmpFile[MAX_PATH];
    swprintf(u16_TmpFile, L"%sDLL%X.manifest", u16_TempPath, (DWORD)__rdtsc());
    
    HANDLE h_File = CreateFileW(u16_TmpFile, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, 0);
    if (h_File == INVALID_HANDLE_VALUE)
        return GetLastError();

    DWORD u32_Written;
    if (!WriteFile(h_File, u8_MemPtr, u32_Size, &u32_Written, 0))
        u32_Error = GetLastError();

    CloseHandle(h_File);

    if (u32_Error)
        return u32_Error;

    // Getting a manifest from memory does not work.
    // Even with Flags = ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID --> Windows searches for the *.DLL file 
    // and for the *.MANIFEST file specified in lpSource on disk.
    // If lpSource == NULL --> ERROR_MOD_NOT_FOUND. On Windows XP bug --> ERROR_NOT_ENOUGH_MEMORY
    ACTCTXW k_Ctx  = {0};
    k_Ctx.cbSize   = sizeof(k_Ctx);
    k_Ctx.lpSource = u16_TmpFile; 

    *ph_ActCtx = CreateActCtxW(&k_Ctx);
    u32_Error  = GetLastError();

    DeleteFile(u16_TmpFile);

    if (*ph_ActCtx == INVALID_HANDLE_VALUE)
        return u32_Error;
    
    if (!ActivateActCtx(*ph_ActCtx, pu32_ActCtxCookie))
        return GetLastError();

    TRACE(L">> Activate ActCtx: %p", *ph_ActCtx);
    return 0;
}

You must call this function before calling BuildImportTable().

    HANDLE    h_ActContext     = NULL;
    ULONG_PTR u32_ActCtxCookie = 0;

    // Create Activation Context (and activate) before loading dependent modules
    if (u32_Error = CreateActivationContext(pi_ModData, &h_ActContext, &u32_ActCtxCookie))
        goto error;

After calling the entry point of the DLL you don't need the activation context anymore.

    if (h_ActContext)
    {
        TRACE(L"<< Deactivate ActCtx");

        if (u32_ActCtxCookie)
            DeactivateActCtx(0, u32_ActCtxCookie); 
            
        ReleaseActCtx(h_ActContext);
    }

Elmue avatar Jul 02 '20 16:07 Elmue