64-разрядный Code Cave возвращает неверное местоположение точки входа

#c #c #windows #security #code-injection

#c #c #Windows #Безопасность #Внедрение кода

Вопрос:

Я пытался запустить 64-разрядную DLL исключительно в виртуальной памяти процессов без «ручного сопоставления» (т. Е. Вручную Разрешая перемещения / импорт).

План состоял в том, чтобы внедрить код в целевое приложение и загрузить модуль с помощью обычных средств, таких как LoadLibrary.

Я предполагал, что LoadLibrary исправит перемещение / импорт модулей самостоятельно, поскольку именно для этого он и предназначен.

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

Я считаю, что на этом последнем шаге возникает ошибка.

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


Вот некоторая общая информация, которая может быть или не быть полезной:

  • Оба приложения (инжектор и DLL) скомпилированы для запуска в 64-разрядных архитектурах
  • Тестовое приложение, которое я использовал для тестирования метода внедрения, — это приложение центра обновления Windows (wuauclt.exe — находится в /System32/), он, конечно, компилируется для запуска как 64-разрядный PE
  • Хост-компьютер: Windows 7 Home Premium (тип системы: 64-разрядная операционная система)

Что касается информации, относящейся непосредственно к инжектору, идет:

  • Метод первичной инъекции кода работает (насколько я могу судить), и я доказал это с помощью отладки caveman с помощью MessageBoxA
  • В проекте используется многобайтовый набор символов с отключенной оптимизацией кода. Код был скомпилирован с использованием VS 2013 Ultimate (оба проекта созданы для версии x64)
  • Проверки SDL отключены, поскольку используются небезопасные функции (strcpy и друзья)
  • Инжектор отлаживается с повышенными привилегиями (такими же высокими, как SE_DEBUG_PRIVILEGES) при каждом запуске.

Предисловие к коду: Приведенный ниже код никоим образом не предназначен для того, чтобы выглядеть красиво или демонстрировать хорошие методы программирования. Имейте это в виду при просмотре кода. Он был специально разработан для тестирования метода внедрения кода, чтобы убедиться, что он работает. Если у вас возникли проблемы с компоновкой, структурой и т. Д. Программы, Не стесняйтесь исправлять их и / или реструктурировать их самостоятельно. Я здесь не по этой причине. Если это не то, что привело к ошибке, то это полностью причина, по которой я здесь 🙂

Код для инжектора: http://pastebin.com/FF5G9nnR

 /*
    Some of the code was truncated (functions not pertaining to the injection), but
    I have verified the code compiles and works correctly with it's injeteme.dll counterpart
*/

#include <Windows.h>
#include <Psapi.h>

#define TARGET_PID                1124

typedef BOOL(WINAPI* pFreeLibrary)(HMODULE);
typedef HMODULE(WINAPI* pLoadLibraryA)(LPCSTR);
typedef HANDLE(WINAPI* pGetCurrentProcess)(void);
typedef BOOL(WINAPI* DLL_MAIN)(HMODULE, DWORD, LPVOID);
typedef HANDLE(WINAPI* pOpenProcess)(DWORD, BOOL, DWORD);
typedef BOOL(WINAPI* pVirtualFree)(LPVOID, SIZE_T, DWORD);
typedef int(__stdcall* pMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT);
typedef LPVOID(WINAPI* pVirtualAlloc)(LPVOID, SIZE_T, DWORD, DWORD);
typedef BOOL(WINAPI* pGetModuleInformation)(HANDLE, HMODULE, LPMODULEINFO, DWORD);
typedef BOOL(WINAPI* pWriteProcessMemory)(HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T*);

//////////////////////////////////////////////////////////////////
struct IINFO
{
    LPVOID stubAddr;
    LPVOID retStatusPtr;
    char fullModulePath[MAX_PATH];
    DWORD pId, sizeOfCurrStruct;

    // DEBUG
    pMessageBoxA messageBox;

    pOpenProcess openProcess;
    pVirtualFree virtualFree;
    pFreeLibrary freeLibrary;
    pLoadLibraryA loadLibrary;
    pVirtualAlloc virtualAlloc;
    pGetCurrentProcess getCurrProc;
    pWriteProcessMemory writeMemory;
    pGetModuleInformation getModInfo;
};

static DWORD WINAPI stub(IINFO *iInfo)
{
    HMODULE hMod;
    MODULEINFO mInfo;
    DLL_MAIN dllMain;
    LPVOID lpNewMod, lpTempModBuff;

    PIMAGE_DOS_HEADER pIDH;
    PIMAGE_NT_HEADERS pINH;

    iInfo->messageBox(NULL, iInfo->fullModulePath, NULL, 0);

    hMod = iInfo->loadLibrary(iInfo->fullModulePath);
    if (!hMod)
        return 0;

    if (!iInfo->getModInfo(iInfo->getCurrProc(), hMod, amp;mInfo, sizeof(MODULEINFO)))
        return 0;

    lpTempModBuff = iInfo->virtualAlloc(NULL, mInfo.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!lpTempModBuff)
        return 0;
    if (!iInfo->writeMemory(iInfo->getCurrProc(), lpTempModBuff, mInfo.lpBaseOfDll, mInfo.SizeOfImage, NULL))
        return 0;

    if (!iInfo->freeLibrary(hMod))
        return 0;

    lpNewMod = iInfo->virtualAlloc(mInfo.lpBaseOfDll, mInfo.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!lpNewMod)
        return 0;

    // using wpm since we have already acquired the function
    if (!iInfo->writeMemory(iInfo->getCurrProc(), lpNewMod, lpTempModBuff, mInfo.SizeOfImage, NULL))
        return 0;

    if (!iInfo->virtualFree(lpTempModBuff, 0, MEM_RELEASE))
        return 0;

    /*if (!iInfo->virtualFree(iInfo, 0, MEM_RELEASE))
    return 0;
    iInfo->messageBox(NULL, NULL, NULL, 0); */

    pIDH = (PIMAGE_DOS_HEADER)lpNewMod;
    if (!pIDH)
        return 0;
    pINH = (PIMAGE_NT_HEADERS)((LPBYTE)lpNewMod   pIDH->e_lfanew);
    if (!pINH)
        return 0;
    dllMain = (DLL_MAIN)((LPBYTE)lpNewMod   pINH->OptionalHeader.AddressOfEntryPoint);
    if (!dllMain)
        return 0;
    iInfo->messageBox(NULL, NULL, NULL, 0);
    dllMain((HINSTANCE)lpNewMod, DLL_PROCESS_ATTACH, NULL);
    return 1;
}
static DWORD WINAPI stubEnd(){ return 0; }
//////////////////////////////////////////////////////////////////

int main()
{
    HANDLE hThread = 0;
    DWORD dwStubSize = 0;
    int sucResp = 0, count = 0;
    HMODULE hUser32 = 0, hNtdll = 0;
    char fullPathName[] = "C:\injectme.dll";

    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, TARGET_PID);
    if (!hProc || hProc == INVALID_HANDLE_VALUE)
        return 0;

    __int64 SizeOfStub = (LPBYTE)stubEnd - (LPBYTE)stub;
    LPVOID lpStub = VirtualAllocEx(hProc, NULL, SizeOfStub, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!lpStub)
        return 0;

    hUser32 = LoadLibraryA("user32.dll");
    if (!hUser32)
        return 0;
    hNtdll = LoadLibraryA("kernel32.dll");
    if (!hNtdll)
        return 0;

    IINFO iInfo = {};
    iInfo.retStatusPtr = amp;sucResp;
    strcpy(iInfo.fullModulePath, fullPathName);
    iInfo.sizeOfCurrStruct = sizeof(IINFO);
    iInfo.stubAddr = lpStub;
    iInfo.pId = GetCurrentProcessId();
    iInfo.messageBox = (pMessageBoxA)GetProcAddress(hUser32, "MessageBoxA");
    iInfo.openProcess = (pOpenProcess)GetProcAddress(hNtdll, "OpenProcess");
    iInfo.virtualFree = (pVirtualFree)GetProcAddress(hNtdll, "VirtualFree");
    iInfo.freeLibrary = (pFreeLibrary)GetProcAddress(hNtdll, "FreeLibrary");
    iInfo.loadLibrary = (pLoadLibraryA)GetProcAddress(hNtdll, "LoadLibraryA");
    iInfo.virtualAlloc = (pVirtualAlloc)GetProcAddress(hNtdll, "VirtualAlloc");
    iInfo.getCurrProc = (pGetCurrentProcess)GetProcAddress(hNtdll, "GetCurrentProcess");
    iInfo.writeMemory = (pWriteProcessMemory)GetProcAddress(hNtdll, "WriteProcessMemory");
    iInfo.getModInfo = (pGetModuleInformation)GetProcAddress(hNtdll, "K32GetModuleInformation");

    LPVOID lpStubInfo = VirtualAllocEx(hProc, NULL, sizeof(IINFO), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!lpStubInfo)
        return 0;

    if (!WriteProcessMemory(hProc, lpStub, stub, SizeOfStub, NULL))
        return 0;
    if (!WriteProcessMemory(hProc, lpStubInfo, amp;iInfo, sizeof(iInfo), NULL))
        return 0;

    hThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)lpStub, lpStubInfo, 0, NULL);
    if (!hThread || hThread == INVALID_HANDLE_VALUE)
        return 0;

    WaitForSingleObject(hThread, INFINITE);

    return 1;
}
  

Код для вводимой библиотеки DLL: http://pastebin.com/8WXxcpu1

 #include <Windows.h>

BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpParam)
{
    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
        {
            MessageBoxA(NULL, "Hello from injectme.dll!", "", MB_OK | MB_ICONINFORMATION);
            break;
        }
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }

    return TRUE;
}
  

Ошибка при выполнении приведенного выше кода дословно (при условии, что вы также применили приведенные выше настройки и используете аналогичную среду) в отладчике VS2013 выглядит следующим образом:

«Необработанное исключение в 0x000007FEEA5125D4 в wuauclt.exe : 0xC0000005: Место выполнения с нарушением доступа 0x000007FEEA5125D4.»

После просмотра процесса «wuauclt.exe » в Process Hacker я ясно вижу, что модуль был выделен изначально (при загрузке через LoadLibrary) в 0x7fef67c0000. Это показано в контекстном меню -> в разделе разное-> выгруженные модули.

После двойного щелчка «wuauclt.exe «, вы можете просмотреть виртуальную память приложения, чтобы убедиться, что все работает так, как должно быть. Я могу подтвердить, что для этого текущего сеанса буфер памяти RWX был выделен в 0x7fef67c0000 с точным размером выгруженного модуля, содержащего injectme.Модуль dll. При копании в injectme.dll с CFF Explorer, тогда RVA точки входа кажется 0x132C, что не складывается, учитывая, что ошибка находится намного дальше в памяти. Кроме того, я могу проверить еще два буфера памяти RWX, содержащие заглушку для ввода кода и информационную структуру. Оглядываясь назад, информационная структура, вероятно, не нуждается в RWX. В любом случае, я не могу хоть на жизнь разобраться в ошибке.

Я надеюсь, что вы сможете мне помочь. Я чрезвычайно благодарен за ваше время.

Комментарии:

1. принесите код сюда

2. @manetsus мой плохой, код был перенесен из pastebin. Извините за любые неудобства.

Ответ №1:

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

Сама Windows очень, очень мало заботится о языке программирования, который вы использовали при разработке. Вы получаете либо CLR-код (.Net), либо машинный код. В данном случае это x64. Но Windows действительно не заботится о strcpy проверках or SDL. С этим должен иметь дело компилятор, а не ОС. Скорее всего strcpy , он даже не выживет, когда его код будет полностью встроен. Но, по-видимому, по какой-то странной причине у вас отключена оптимизация — опять путаница между компилятором и ОС.

Однако Windows заботится о других концепциях, которые вы не упоминаете. В основном это были бы рандомизация ASLR и DEP — рандомизация компоновки адресного пространства и предотвращение выполнения данных. Это методы, позволяющие не допустить хакеров, а вы взламываете. Так что это не удивительно.

Я не уверен, что под «RWX» вы подразумеваете «Чтение, запись, выполнение», потому что вы должны знать, что это вызывает проблемы. DEP вдохновлен более подходящим названием W ^ X, Write XOR ИЛИ eXecute .

Однако более вероятным виновником является ASLR. Windows по замыслу пытается загружать DLL-файлы по непредсказуемым адресам, поскольку это устраняет целый класс взломов. Похоже, вы предполагаете адрес загрузки, в то время как Windows действительно использует другой адрес.

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

Комментарии:

1. Прежде всего, я хотел бы искренне поблагодарить вас за ответ. Полное раскрытие, я работаю над этим всего несколько дней и не думал о DEP / ASLR.

2. (Извините, мой предыдущий комментарий был прерван до того, как я смог полностью ответить)

3. Ну, похоже, что нажатие return автоматически отправит комментарий. Я новичок в этом и собираюсь предположить, что они не должны быть очень длинными. Я пытаюсь спросить, продвигаясь вперед, как вы думаете, где я должен сосредоточить свое внимание? Насколько я понимаю, LoadLibrary должен выполнять любые изменения, чтобы переданный ему модуль мог эффективно выполняться. Как только у него появляется возможность сделать это, он просто перемещается в памяти. Его последнее пристанище находится там, где оно началось. Захватывает точку входа «на лету» и не предполагает. Извините, но у меня осталось немного путаницы.

4. @astra: Мое высокоуровневое наблюдение заключается в том, что вы, вероятно, столкнулись с проблемой XY. Хакерство, которое вы пытаетесь осуществить, почти наверняка не лучший способ решить вашу реальную проблему.

5. Это чрезвычайно справедливый ответ. Все вышесказанное сделано в попытке обойти механизмы обнаружения другого процесса. Более того, описанный выше метод будет предназначен исключительно для предварительной инъекции . Процесс, пытающийся обнаружить мой модуль, проанализирует все процессы, которые в данный момент взаимодействуют с ним, поэтому мне требуется использование, казалось бы, доброкачественного хоста, который будет выступать в качестве прикрытия для окончательного внедрения. Процесс, пытающийся заблокировать мой модуль, является процессом античита. Еще раз спасибо за всю помощь, надеюсь, я не докучаю вам всеми своими вопросами.