• Ищем качественного (не новичок) разработчиков Xenforo для этого форума! В идеале, чтобы ты был фулл стек программистом. Если у тебя есть что показать, то свяжись с нами по контактным данным: https://t.me/DREDD

Гайд Debugport

  • Автор темы Автор темы n0entry
  • Дата начала Дата начала
Пользователь
Пользователь
Статус
Оффлайн
Регистрация
12 Дек 2022
Сообщения
131
Реакции
46
Существуют хорошо известные функции, используемые в целях отладки и защиты:

~ Обнуление DebugPort
~ NtOpenProcess
~ NtOpenThread
~ KiAttachProcess
~ NtReadVirtualMemory
~ NtWriteVirtualMemory

Следующие две функции используются для обмена сообщениями между процессом и отладчиком. Если они не обрабатываются, отладчик не сможет получать или передавать информацию:

~ DbgkpSetProcessDebugObject
~ DbgkpQueueMessage

Эти функции уже многократно описаны в интернете, поэтому здесь я лишь кратко остановлюсь на них
На мой взгляд, обнуление DebugPort — один из самых сложных моментов в защите TP, при этом в сети об этом почти нет подробностей, а имеющиеся объяснения неполные

Начнём с двухмашинной отладки:

TP постоянно вызывает KdDisableDebugger, чтобы отключить отладку. Мы просто заставляем этот вызов сразу возвращаться, так как в начале функции нет проверок. По исходному коду WRK видно, что функция также обрабатывает связанные переменные и состояния отладки — их тоже нужно корректно подменить

При первом запуске TP отладчик останавливается на:

Код:
Expand Collapse Copy
TesSafe+0x56d5: меняем на 0x74 (NOP — 2 байта 0x90)
b13c76d5 75b0 -> jne -> 0x74

Код:
Expand Collapse Copy
TesSafe+0x5803: меняем на 0xeb
b13c7803 7402 -> je -> 0xeb

При втором запуске TP:

Код:
Expand Collapse Copy
TesSafe+0x59dd: меняем на 0x9090
b10419dd 75b0 -> jne -> NOP

Код:
Expand Collapse Copy
TesSafe+0x5b0b: меняем на 0xeb
b1041b0b 7402 -> je -> 0xeb


Таким образом, TP запускается дважды: первый запуск — ложный

Hook на NtOpenProcess:

TP делает hook на NtOpenProcess, в результате чего мы должны перехватить вызов и, в зависимости от того, кто вызывает, выполнить нужную логику:

C++:
Expand Collapse Copy
__declspec( naked ) void FuckNtOpenProcess( )
{
    __asm {
        pushad
        pushfd
        call isGameAccess
        mov g_isGameAccess, al
        popfd
        popad
        cmp g_isGameAccess, 1
        jnz NOISGAME

        push dword ptr[ebp - 38h]
        push dword ptr[ebp - 24h]
        mov eax, g_uNtOpenProcessHookAddr
        add eax, 6
        jmp eax

        NOISGAME :
        push dword ptr[ebp - 38h]
            push dword ptr[ebp - 24h]
            mov eax, g_uObOpenObjectByPointerAddr
            call eax
            push g_uHookNtOpenProcessRet
            ret
    }
}


Hook на NtOpenThread:

Логика полностью идентична NtOpenProcess. Код:

C++:
Expand Collapse Copy
__declspec( naked ) void FuckNtOpenThread( )
{
    __asm {
        pushad
        pushfd
        call isGameAccess
        mov g_isGameAccess, al
        popfd
        popad
        cmp g_isGameAccess, 1
        jnz NOISGAME

        push dword ptr[ebp - 34h]
        push dword ptr[ebp - 20h]
        mov eax, g_uNtOpenThreadHookAddr
        add eax, 6
        jmp eax

        NOISGAME :
        push dword ptr[ebp - 34h]
            push dword ptr[ebp - 20h]
            mov eax, g_uObOpenObjectByPointerAddr
            call eax
            push g_uHookNtOpenThreadRet
            ret
    }
}

Обработка KiAttachProcess:

Функция не экспортируется, поэтому её адрес ищется через KeAttachProcess. По сигнатуре и шаблону байт находится адрес и производится восстановление:

C++:
Expand Collapse Copy
VOID My_HookKiAttachProcess( )
{
    BYTE bJmpAddr[7] = { 0x8b, 0xff, 0x55, 0x8b, 0xec, 0x53, 0x8b };
    BYTE* bKeAttachProcessAddr = ( BYTE* ) GetFunAddress( L"KeAttachProcess" );
    if ( bKeAttachProcessAddr == NULL ) return;

    // Поиск call nt!KiAttachProcess
    while ( TRUE )
    {
        if ( *( bKeAttachProcessAddr ) == 0xE8 &&
             *( bKeAttachProcessAddr + 5 ) == 0x5F &&
             *( bKeAttachProcessAddr + 6 ) == 0x5E &&
             *( bKeAttachProcessAddr + 7 ) == 0x5D &&
             *( bKeAttachProcessAddr + 8 ) == 0xC2 &&
             *( bKeAttachProcessAddr - 1 ) == 0x56 &&
             *( bKeAttachProcessAddr - 2 ) == 0x57 &&
             *( bKeAttachProcessAddr - 5 ) == 0xFF &&
             *( bKeAttachProcessAddr - 6 ) == 0x50 )
        {
            g_uKiAttachProcessAddr = *( ULONG* ) ( bKeAttachProcessAddr + 1 ) + ( ULONG ) ( bKeAttachProcessAddr + 5 );
            break;
        }
        bKeAttachProcessAddr++;
    }

    KIRQL klrql = KeRaiseIrqlToDpcLevel( );
    CleanPageProtect( TRUE );
    RtlCopyMemory( ( BYTE* ) g_uKiAttachProcessAddr, bJmpAddr, 7 );
    KeLowerIrql( klrql );
    CleanPageProtect( FALSE );
}

Восстановление NtReadVirtualMemory, NtWriteVirtualMemory, DbgkpSetProcessDebugObject, DbgkpQueueMessage:

C++:
Expand Collapse Copy
VOID My_HookNtReadAndNtWriteVirtualMemory( )
{
    BYTE bReadAndWritePush[2] = { 0x6a, 0x1c };
    BYTE bReadPush[5] = { 0x68, 0xe0, 0xa4, 0x4d, 0x80 };
    BYTE bWritePush[5] = { 0x68, 0xf8, 0xa4, 0x4d, 0x80 };
    BYTE* uNtReadVirtualMemoryAddr = ( BYTE* ) GetSsdtFunAddress( 0xBA );
    BYTE* uNtWriteVirtualMemoryAddr = ( BYTE* ) GetSsdtFunAddress( 0x115 );

    if ( !uNtReadVirtualMemoryAddr || !uNtWriteVirtualMemoryAddr ) return;

    KIRQL klrql = KeRaiseIrqlToDpcLevel( );
    CleanPageProtect( TRUE );

    RtlCopyMemory( uNtReadVirtualMemoryAddr, bReadAndWritePush, 2 );
    RtlCopyMemory( uNtReadVirtualMemoryAddr + 2, bReadPush, 5 );
    RtlCopyMemory( uNtWriteVirtualMemoryAddr, bReadAndWritePush, 2 );
    RtlCopyMemory( uNtWriteVirtualMemoryAddr + 2, bWritePush, 5 );

    KeLowerIrql( klrql );
    CleanPageProtect( FALSE );
}

Обнуление DebugPort и контроль:

Самая интересная часть — очистка DebugPort в структуре EPROCESS, поскольку это основной объект отладки. Нужно установить аппаратную точку останова по доступу к этому полю (например, ba r4 адрес) и отслеживать обращения. Было обнаружено 4 места обнуления и 2 проверки.

Чтобы найти эти участки, нужно представить себя разработчиком игры: если игра хочет проверить DebugPort, она обязательно будет обращаться к этому адресу. Мы ставим точку останова, наблюдаем за срабатываниями и анализируем дизассемблированный код. Некоторые участки могут быть защищены виртуальной машиной (VM), и вмешиваться в них опасно.

Пример:

Первая точка:
Код:
Expand Collapse Copy
TesSafe+0x219e:
    mov ecx, dword ptr [ecx]

Функция начинается с:
Код:
Expand Collapse Copy
TesSafe+0x2124:
    mov edi, edi


Анализ показывает, что это стандартное обнуление — просто возвращаемся из функции. Проверки нет
 
Назад
Сверху Снизу