Гайд Различные методы хукинга ( The different ways of hooking )

Арбитр
Арбитр
Статус
Оффлайн
Регистрация
13 Июл 2018
Сообщения
1,520
Реакции[?]
1,635
Поинты[?]
278K
Всем привет. Решил сделать перевод достаточно старой темы.
Конечно очень многие знают английский язык и сами смогут это перевести, но
надеюсь на то, что данная тема будет полезна кому-то.

Автор данной статьи: @JD96
Помощь с переводом: @Ch1rk0v - vk.com/chirkovnh (не реклама).
Если вы нашли ошибку пожалуйста отпишите в лс, исправлю.

- О хуках-
Хуки это одни из самых важных вещей в интернал читах и больше всего читов детектятся потому-что метод хука палится анти читом или же сигнатура чита задетекчена анти читом.
По этому тебе нужно знать в чем заключается разница между разными методами хукинга и как и для чего они могут использоваться.
Перейдем к наиболее популярным и различным методам хукинга)).

- Byte patching (.text section) -
Скорость выполнения: 10 баллов
Уровень скилла шобы хукнуть: 2 балла
Риск обнаружения: 5 - 7 балла

Byte patching самый простой способ чтобы хукнуть какую либо функцию.
Часто используются библиотеки для хуков, такие как Microsoft Detours.
Некоторые анти-читы до сих пор не сканят .text section, но большинство анти читов на данный момент все таки вам дадут по ебучке, так что не рекомендую хукать таким образом, разве что какие-то мемные игры.
Есть разные способы перенаправить поток кода. Можно разместить обычную инструкцию JMP (размером 5 байт) или попробовать выполнить hotpatching с использованием короткого JMP (размером 2 байта) в какое-либо место, где есть больше места для 5-байтового JMP.
Можно разместить инструкцию CALL, которая воркает так же, как JMP, но перед пушем помещает обратный адрес в стек. Так же можно просто поместить адрес в стек, а затем вызвать RETN, который перейдет к последнему адресу в стеке и, следовательно, будет вести себя как JMP.

Пример этого метода :
C++:
JMP 0xDEADBEEF (short and long)

CALL 0xDEADBEEF

PUSH 0xDEADBEEF
RETN
Я обнаружил, что GameGuard сканирует только на те прямые манипуляции, о которых я упоминал. Вы можете сделать гораздо больше!
Вы можете вызвать исключение. Во-первых, вы должны будете зарегистрировать фильтр исключений (AddVectoredExceptionHandler, SetUnhandledExceptionFilter), который улавливает это исключение.
Затем вам нужно как-то триггернуть исключение. Например, вы можете разместить точку остановки INT3 (брикпоинт) которая представляет собой однобайтный 0xCC. Затем в своем фильтре вы можете перенаправить указатель инструкции (EIP) на свою собственную функцию:
C++:
LONG WINAPI UnhandledExceptionFilter(EXCEPTION_POINTERS *pExceptionInfo)
{
    //Check exception type
    if(pExceptionInfo->ExceptionRecord->ExceptionCode == UNK)
    {
        //Check exception address
        if(pExceptionInfo->ExceptionRecord->ExceptionAddress == 0xDEADBEEF)
        {
            dwEndsceneJMP = (DWORD)pExceptionInfo->ExceptionRecord->ExceptionAddress + 5; //Save the address where we return after our hook
            pExceptionInfo->ContextRecord->Eip = (DWORD)hkEndscene; //Change instruction pointer - will jump to our hook
       
            return EXCEPTION_CONTINUE_EXECUTION; //Copies all registers from pExceptionInfo to the real registers
        }
    }

    return EXCEPTION_CONTINUE_SEARCH; //Keep searching for any exceptions
}
Это работает со всеми видами исключений, вам просто нужно вызвать их, либо разыменовав нулевой указатель, либо разделив на ноль:
Код:
xor eax, eax
div eax

mov eax, dword ptr ds:[0]

...
Эти перенаправления могут быть размещены повсюду, в большинстве случаев они размещаются в начале функции, поэтому стек и регистры полностью правильны и готовы к использованию через typedefs..
Можно так же поместить перенаправление ниже / глубже в функцию, которая затем вызывается хуком Midfunction.
Для этого вам нужны некоторые дополнительные знания ассемблера, потому что стек и регистры обычно не идеальны для использования, и вам нужно получить параметры другим способом.

Тутор по midfunction хукингу будет переведен чуть позже если вам зайдет эта тема.

- IAT/EAT Hooking -
Скорость выполнения: 10 баллов
Уровень скилла шобы хукнуть: 3 балла
Риск обнаружения: 5 баллов

Этот метод хукинга базирован на том как PE-файлы работают в Windows.
Это "Import/Export Address Table". - ссылка на вики прочитайте.( кликабельно )
Эта адресная таблица содержит указатель на API и корректируется загрузчиком PE при выполнении файла.
Вы можете либо зациклить всю таблицу, найти функцию и перенаправить ее, либо найти ее вручную с помощью OllyDbg или IDA.
Основная идея состоит в том, что вы заменяете определенный API на свою подключенную функцию.
Это не только хорошо для простого подключения API, но также может использоваться для подключения например DirectX.
C++:
pEnterCriticalSection = *(EnterCriticalSection_t*)hCriticalSection;
*(DWORD*)hCriticalSection = (DWORD)nEnterCriticalSection;
Как видите, это простое перенаправление указателя. Забавно то, что этот старый метод до сих пор не обнаруживается некоторыми античитами, включая HackShield и XignCode(не актуальные анти читы на сегодняшний день) не рекомендую использовать данный метод сейчас.


- VMT Hooking / Pointer redirections -
Скорость выполнения: 10 баллов
Уровень скилла шобы хукнуть: 3 - 5 балла
Риск обнаружения: 3 балла

Один из лучших методов хукинга (бл*ть не знаю как адаптировать это слово иначе), потому что нет API или базового способа обнаружения этих хуков..
Большинство античитов обнаруживают хуки VMT на D3D-устройстве движка, но это не то, что мы хотим делать в любом случае.
Почти у каждого движка есть внутренний класс рендеринга, который можно подключить. Например, вы можете просто хукнуть Endscene с помощью детуров и логнуть returnaddress .
Когда вы проверяете код по returnaddress, вы найдете функцию, которая вызывает Endscene. Теперь найдите ссылку на эту функцию и немного реверсните, вы, скорее всего, получите указатель в разделе .data, который представляет виртуальную таблицу.
Эти таблицы содержат просто адреса функций и могут быть легко заменены даже без использования VirtualProtect, поскольку .data обычно имеет флаги Read/Write. Вот небольшой пример хука D3DXFont::DrawTextA на довольно неизвестном движке:
C++:
DWORD pNewVtable[18];
DWORD dw1 = *(DWORD*)0x10235BA8;
DWORD dw2 = *(DWORD*)(dw1 + 0x86C);
DWORD dw3 = *(DWORD*)dw2; //DWORD pNewVtable[18];
DWORD dw1 = *(DWORD*)0x10235BA8;
DWORD dw2 = *(DWORD*)(dw1 + 0x86C);
DWORD dw3 = *(DWORD*)dw2; //D3DXFont

memcpy(pNewVtable, (void*)dw3, 18 * 4); //VT has 18 functions -> 18*4 bytes
oDrawTextA = (tDrawTextA)pNewVtable[14]; //Index: 14 == DrawTextA
pNewVtable[14] = (DWORD)hkDrawTextA;
*(DWORD*)dw2 = (DWORD)pNewVtable;D3DXFont

memcpy(pNewVtable, (void*)dw3, 18 * 4); //VT has 18 functions -> 18*4 bytes
oDrawTextA = (tDrawTextA)pNewVtable[14]; //Index: 14 == DrawTextA
pNewVtable[14] = (DWORD)hkDrawTextA;
*(DWORD*)dw2 = (DWORD)pNewVtable;
Как видите, он полностью основан на движке и требует некоторого базового понимания того, как работают VTables.
Ваш хук может быть задетекчен только в том случае, если провайдер античита добавит ваш VTable в список сканирования. Конечно, есть миллионы способов наебать это, и не все таблицы VT включены в этот список.
Так что проявите изобретательность, и ваш хук останется андетект.

- HWBP Hooking -
Скорость выполнения: 6 баллов
Уровень скилла шобы хукнуть: 6 баллов
Риск обнаружения: 4 балла

Мы уже говорил раньше о брикпоинтах (точка остановки) но в этот раз мы не хотим какие-либо байты в .text section.
Как я сказал раньше вам так же необходимо разместить обработчик исключений, чтобы перехватить исключение!

Их можно разместить отдельно для каждого треда, но это также означает, что нам нужен хендел треда. Некоторые античиты скрывают все треды с помощью руткитов, но это не значит, что мы не можем попасть в тред! Здесь я собираюсь представить вам два способа установки брикпоинтов, сначала простой метод, просто зацикливая все треды:
C++:
HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);

if (hThreadSnap)
{
    te32.dwSize = sizeof(THREADENTRY32);

    if (!Thread32First(hThreadSnap, &te32))
    {
        CloseHandle(hThreadSnap);
        return 0;
    }

    do
    {
        if (te32.th32OwnerProcessID == GetCurrentProcessId() && te32.th32ThreadID != GetCurrentThreadId()) //Ignore threads from other processes AND the own thread of course
        {
            HANDLE hThread = OpenThread(THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME, 0, te32.th32ThreadID);
            if (hThread)
            {
                CONTEXT context;
                context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
                SuspendThread(hThread); //Suspend the thread so we can safely set a breakpoint
           
                if (GetThreadContext(hThread, &context))
                {
                    context.Dr0 = 0; //Dr0 - Dr3 contain the address you want to break at
                    context.Dr1 = 0; //You dont have to set them all
                    context.Dr2 = 0;
                    context.Dr3 = 0;
               
                    //Those flags activate the breakpoints set in Dr0 - Dr3
                    //You can either set break on: EXECUTE, WRITE or ACCESS
                    //The Flags I'm using represent the break on execute
                    context.Dr7 = (1 << 0) | (1 << 2) | (1 << 4);

                    SetThreadContext(hThread, &context);
                }

                ResumeThread(hThread);
                CloseHandle(hThread);
            }
        }
    } while (Thread32Next(hThreadSnap, &te32));
    CloseHandle(hThreadSnap);
}
Второй способ немного другой, но суть такая же.
Мы просто хукаем функцию, которая выполняется в том же треде, используя один из других методов, о которых мы уже говорили ранее.
Это не должно быть задетекчено мгновенно, но оно МОЖЕТ быть задетекчено через несколько секунд / минут.
После того, как мы хукнули тред, мы можем получить его дескриптор с помощью GetCurrentThread (), что делает ненужным цикл для всех потоков.
Конечно, вы должны восстановить первый хук, как только вы разместили HWBP, чтобы не было детекта.
Этот метод задетекчен такими античитами HackShield и некоторых версий XignCode, отлично работает с GameGuard, Punkbuster, всеми другими античитами gay r3.
Вы можете просто хукнуть GetThreadContext (лучше хукнуть версию ntdll), чтобы он не был детект. Многие античиты перезагружают фиксированные версии ntdll, kernel32 и других, поэтому вы не можете напрямую размещать хуки.
В этом случае вы можете либо найти эти области памяти, либо перечислить загруженные библиотеки DLL, либо сравнить EAT (экспортную таблицу адресов)

- PageGuard Hooking -
Скорость выполнения: 1 балл
Уровень скилла шобы хукнуть: 8 баллов
Риск обнаружения: 1 балл


Хуки PageGuard понастоящему стеслово, их почти не детектят античиты.
Сначала вам нужно зарегистрировать обработчик исключений.
Затем вы должны вызвать исключение, на этот раз пометив все пейдж мемори с помощью PAGE_GUARD с помощью VirtualProtect, что приведет к исключению.
Когда вы прочитаете о PAGE_GUARD в msdn, вы обнаружите, что он автоматически удаляется после первого исключения.
В нашем обработчике исключений мы теперь устанавливаем одношаговый флаг и пошагово выполняем все инструкции, пока не дойдем до нужного адреса.
Мы можем снова изменить EIP, как мы это делали раньше, но теперь мы должны снова пометить пейдж мемори как PAGE_GUARD, иначе хук не сработает снова!
Этот метод хукинга работает очень медленно из-за использования одношагового флага и должен использоваться только для функций, которые вызываются очень редко.
Вот небольшой пример:

C++:
DWORD dwOld;
SYSTEM_INFO sSysInfo;
GetSystemInfo(&sSysInfo);
VirtualProtect((void*)0xDEADBEEF, sSysInfo.dwPageSize, PAGE_EXECUTE | PAGE_GUARD, &dwOld);

...

LONG WINAPI UnhandledExceptionFilter(EXCEPTION_POINTERS *pExceptionInfo)
{
    if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION)
    {
        if (pExceptionInfo->ContextRecord->Eip == 0xDEADBEEF)
        {
            dwJmpBack = (DWORD)pExceptionInfo->ContextRecord->Eip + 5;
            pExceptionInfo->ContextRecord->Eip = (DWORD)hkFunction;
        }

        pExceptionInfo->ContextRecord->EFlags |= 0x100; //Set single step flag

        return EXCEPTION_CONTINUE_EXECUTION;
    }

    if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP)
    {
        DWORD dwOld;
        SYSTEM_INFO sSysInfo;
        GetSystemInfo(&sSysInfo);
        VirtualProtect((void*)0xDEADBEEF, sSysInfo.dwPageSize, PAGE_EXECUTE | PAGE_GUARD, &dwOld);

        return EXCEPTION_CONTINUE_EXECUTION;
    }

    return EXCEPTION_CONTINUE_SEARCH;
}
- Forced Exception hooking -
Скорость выполнения: 5 баллов
Уровень скилла шобы хукнуть: 8 баллов
Риск обнаружения: 2 балла

Вы можете принудительно создавать исключения в программе, манипулируя указателями и сохраненными значениями.
Например, вы можете захватить указатель устройства игры и установить для него значение null, а затем подождать в обработчике исключений, пока программа не выдаст исключение.
Само исключение должно быть разыменованием нулевого указателя, просто выполните свои действия в перенаправленном обработчике EIP, а затем сбросьте исходные значения и продолжите выполнение.
Поскольку теперь указатель снова в порядке, он будет выполняться, пока вы снова не установите указатель в нуль. Есть еще много способов использовать это.
Вам может потребоваться большая работа, чтобы исправить все исключения, что требует большого скилла.
Примера нет, но вы держитесь.

Спасибо всем за прочтение статьи, ждите еще постов.
 
Последнее редактирование:
Олдфаг
Статус
Оффлайн
Регистрация
4 Янв 2020
Сообщения
2,992
Реакции[?]
1,274
Поинты[?]
19K
Обожаю когда челики пишут полезно ни разу не заходя в тот же Визуал Студио в жизни
 
[Яifk⁷] > all
Участник
Статус
Оффлайн
Регистрация
4 Июн 2019
Сообщения
472
Реакции[?]
165
Поинты[?]
0
Неплохо, было интересно заместо русской литературы читать.
 
элси элси
Пользователь
Статус
Оффлайн
Регистрация
13 Мар 2020
Сообщения
225
Реакции[?]
36
Поинты[?]
0
Ооо тема с юц, старая даже, спасибо за перевод:seemsgood:
 
Нефор
Забаненный
Статус
Оффлайн
Регистрация
9 Ноя 2018
Сообщения
1,042
Реакции[?]
663
Поинты[?]
0
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
молодец камаз, радуешь.
 
Арбитр
Арбитр
Статус
Оффлайн
Регистрация
13 Июл 2018
Сообщения
1,520
Реакции[?]
1,635
Поинты[?]
278K
Хочу выразить огромную благодарность людям, которые не прошли мимо и оценили эту тему. Ее перевод занял достаточно много времени. Думаю перевести ещё пару интересных статей.
 
Эксперт
Статус
Оффлайн
Регистрация
3 Апр 2020
Сообщения
1,152
Реакции[?]
598
Поинты[?]
5K
Хочу выразить огромную благодарность людям, которые не прошли мимо и оценили эту тему. Ее перевод занял достаточно много времени. Думаю перевести ещё пару интересных статей.
понимаю,кликнуть пкм перевести на русский очень много времени...
 
Арбитр
Арбитр
Статус
Оффлайн
Регистрация
13 Июл 2018
Сообщения
1,520
Реакции[?]
1,635
Поинты[?]
278K
понимаю,кликнуть пкм перевести на русский очень много времени...
Я это делал не так, Гугл переводит многие предложения не так как нужно. Я 2 часа сидел с чирковым и переводил.
 
Нефор
Забаненный
Статус
Оффлайн
Регистрация
9 Ноя 2018
Сообщения
1,042
Реакции[?]
663
Поинты[?]
0
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
понимаю,кликнуть пкм перевести на русский очень много времени...
во-первых камаза не трогай, а во-вторых как никак человек постарался все-же, другой бы мог просто забить хуй и скопипастить тему ничего не переводя.
 
Легенда форума
Статус
Оффлайн
Регистрация
10 Дек 2018
Сообщения
4,339
Реакции[?]
2,261
Поинты[?]
165K
Качественныйкачественныйкачественный контеент
 
Участник
Статус
Оффлайн
Регистрация
26 Апр 2018
Сообщения
852
Реакции[?]
181
Поинты[?]
0
Я это делал не так, Гугл переводит многие предложения не так как нужно. Я 2 часа сидел с чирковым и переводил.
самое жалкое что никто не добавит тему в закреп, она умрет через 2-4 недели, то есть сайт помойкой будет. Я не понимаю админов почему они не могут закреплять такие темы или помогать юным пастерам понять как сделать гребаные фейки, десинки и прочую лубудень.
 
Сверху Снизу