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

D2

Администратор
Регистрация
19 Фев 2025
Сообщения
4,380
Реакции
0
Всем привет друзья!

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

В моей прошлой конкурсной статье вами были заданы вопросы про загрузчик, и я тогда ответил:
Загрузчик это отдельная интересная тема, на которую можно будет статью написать и разобрать все нюансы, такие как правильный хэндлинг 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ы и будет вообще малина.

Скачать
View hidden content is available for registered users!


View hidden content is available for registered users!
 
Сверху Снизу