Статья [Reverse-Engineering] Обходим проверку CRC в VMProtect 3.X и решаем ByPassMe

  • 28
  • 3
  • 42
Всем привет!
Сейчас будет разбор решения для ByPassMe из этой темы - [BypassMe] Interium Game

Я создал отдельную тему так как здесь будет демонстрироваться 1 из путей обхода проверки CRC в VMProtect 3.X
Таргет напичкан различными анти-дебаг триками, и при срабатывании одного из них отладчик сообщает нам о каком то непонятном исключении:
vmware_vaHeQ7JGSp.png

Исключение происходит во время трассировки кода, это значит что оно срабатывает из другого потока. Следуя логике ставим бряк на CreateThread и выходим на код создающий исключение при срабатывании одного из анти-дебаг трика:
vmware_DkZnjsJni0.png


Автор этого байпассми сказал что в программе используется пользовательская ВМ. Что это значит? Это значит что для каждого вызова (call) есть свой хендлер, если мы протрассируем код из скрина выше то наткнёмся на тот самый хендлер:
vmware_rjSqjwDhRn.png


Теперь наша задача сделать патч всего этого дела. Мешают сделать патч здесь 2 вещи:
1) Проверка CRC от VMProtect
2) Хук на NtProtectVirtualMemory для выдачи прав на запись региону памяти.

Начнём с первого пункта. Все ведь знают что винда при загрузке длл из таблицы импорта сначала ищет её в папке с exe? Будем использовать этот стандартный метод атаки (с его помощью кстати часто повышают привилегии в UNIX системе). Смотрим таблицу импорта и видим дллку которая вообще не используется в программе вместе с импортом:
vmware_Lta8aUh6Z1.png


Создаём проект в визуалке и добавляем эту функцию в экспорт:
C++:
#define DLL_EXPORT extern "C" __declspec( dllexport )

DLL_EXPORT BOOL WTSSendMessageW(
    HANDLE hServer,
    DWORD  SessionId,
    LPWSTR pTitle,
    DWORD  TitleLength,
    LPWSTR pMessage,
    DWORD  MessageLength,
    DWORD  Style,
    DWORD  Timeout,
    DWORD* pResponse,
    BOOL   bWait
)
{
    return TRUE;
}

Компилируем длл с именем "WTSAPI32.dll" и суём её в папку с exe. Теперь винда будет грузить нашу длл вместо оригинальной и мы можем прописать в ней всё что душе угодно.

Теперь второй пункт, автор хукнул NtProtectVirtualMemory что б нам жизнь малиной не казалась. Обходим это через шеллкод, ниже код из нашей длл-заглушки:
C++:
int StartRoutine()
{
    HMODULE main_module = GetModuleHandleA( 0 );
    if ( main_module )
    {
        //
        // Ждём распаковки оригинального кода программы
        //
        uintptr_t patch_addr = (uintptr_t)main_module + 0x7179;
        while ( *(WORD*)( patch_addr ) != 0x50FF )
        {
            Sleep( 100 );
        }
       
        //
        // Ждём установку хука
        //
        FARPROC nt_protect = GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "NtProtectVirtualMemory" );
        while ( *(BYTE*)nt_protect == 0xE9 )
        {
            Sleep( 100 );
        }

        Sleep( 500 ); // Дополнительный слип так как триггерится CRC VMProtect'a если патчить сразу после распаковки кода

        //
        // Шеллкод для вызова NtProtectVirtualMemory
        //
        unsigned char shell_syscall[] = {
            0xB8, 0x50, 0x00, 0x00, 0x00, // mov eax, number ($+0x00)
            0xBA, 0x40, 0x8D, 0x70, 0x77, // mov edx, Wow64Transition ($+0x05)
            0xFF, 0xD2,                      // call edx ($+0x0A)
            0xC2, 0x14, 0x00              // ret 0x14 ($+0x0C)
        };

        //
        // Заполняем шеллкод нужными данными
        //
        uintptr_t shell_mem = (uintptr_t)VirtualAlloc( 0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE );
        memcpy( (void*)shell_mem, shell_syscall, sizeof( shell_syscall ) );
        *(uint32_t*)( shell_mem + 0x1 ) = GetSystemNumber( "NtProtectVirtualMemory" );
        *(uintptr_t*)( shell_mem + 0x6 ) = *(uintptr_t*)( GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "Wow64Transition" ) );

        t_NtProtectVirtualMemory m_NtProtectVirtualMemory = (t_NtProtectVirtualMemory)shell_mem;

        //
        // Выдаём региону памяти права на запись через шеллкод
        //
        PVOID BaseAddress = (PVOID)patch_addr;
        SIZE_T RegionSize = 0x1000;
        ULONG oldProtect = 0;
        m_NtProtectVirtualMemory( NtCurrentProcess, &BaseAddress, &RegionSize, PAGE_EXECUTE_READWRITE, &oldProtect );

        //
        // Патч vm_call
        //
        memset( (void*)patch_addr, 0x90, 3 );
    }
    return 0;
}

Теперь отрубаем все плагины и запускаем софт в отладчике:
vmware_tWtYMKZpXg.png
 

Вложения

  • interium_game_patch.zip
    5.2 MB · Просмотры: 110
  • 10
  • 14
Контакты для связи отсутствуют.
где должен ставиться бряк на createtheard?
ставил в kenrel32, но не выходил на код анти-дебаг трика
1624985555218.png
 
  • 112
  • 92
Контакты для связи отсутствуют.
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
где должен ставиться бряк на createtheard?
ставил в kenrel32, но не выходил на код анти-дебаг трика
Посмотреть вложение 20438
CTRL+G и вбиваешь CreateThread затем ставишь бряк. Когда он срабатывает нажимаешь 2 раза левой кнопкой мыши на return в стеке, и смотришь что там выше
 
  • 2
  • 0
Контакты для связи отсутствуют.
Arting, colby57 здравствуйте, не подскажите где посмотреть реализацию функции GetSystemNumber?
И еще, почему у меня в ntdll.dll нет Wow64Transition?
GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "Wow64Transition" ) ничего не возвращает. Я даже через CE посмотрел функции внутри ntdll.dll, но там "Wow64Transition" попросту - нет.
 
Последнее редактирование:
  • 14
  • 13
Arting, colby57 здравствуйте, не подскажите где посмотреть реализацию функции GetSystemNumber?
И еще, почему у меня в ntdll.dll нет Wow64Transition?
GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "Wow64Transition" ) ничего не возвращает. Я даже через CE посмотрел функции внутри ntdll.dll, но там "Wow64Transition" попросту - нет.
__forceinline NTSTATUS RemapModule(const wchar_t* ModuleName, PVOID* ModuleBaseAddress) { NTSTATUS status = STATUS_NOT_SUPPORTED; HANDLE sectionHandle = nullptr; SIZE_T viewSize = NULL; UNICODE_STRING usSectionName{}; OBJECT_ATTRIBUTES objAttrib{}; wchar_t buffer[MAX_PATH]; NoCRT::mem::memset(buffer, 0, MAX_PATH); #ifdef _WIN64 auto str_KnowDll = L"\\KnownDlls\\"; #else auto str_KnowDll = L"\\KnownDlls32\\"; #endif NoCRT::string::strcatW(buffer, str_KnowDll); NoCRT::string::strcatW(buffer, ModuleName); usSectionName = ApiWrapper::InitUnicodeString(buffer); InitializeObjectAttributes(&objAttrib, &usSectionName, OBJ_CASE_INSENSITIVE, NULL, NULL); auto ZwOpenSection = (t_ZwOpenSection)ApiWrapper::GetProcAddress(L"ntdll.dll", "ZwOpenSection"); auto ZwMapViewOfSection = (t_ZwMapViewOfSection)ApiWrapper::GetProcAddress(L"ntdll.dll", "ZwMapViewOfSection"); auto NtClose = (t_NtClose)ApiWrapper::GetProcAddress(L"ntdll.dll", "NtClose"); if (!NtClose || !ZwMapViewOfSection || !ZwOpenSection) { return 0; } status = ZwOpenSection(&sectionHandle, SECTION_MAP_READ, &objAttrib); if (!NT_SUCCESS(status)) { return status; } status = ZwMapViewOfSection(sectionHandle, NtCurrentProcess, ModuleBaseAddress, NULL, NULL, nullptr, &viewSize, (SECTION_INHERIT)1, NULL, PAGE_READONLY); if (!NT_SUCCESS(status)) { return status; } if (sectionHandle) { status = NtClose(sectionHandle); if (!NT_SUCCESS(status)) { return status; } } return status; } short GetSyscallNumber(const wchar_t* moduleName, const char* ApiName) { short originalSyscallNumber = 0; PVOID mapped_dll = nullptr; RemapModule(moduleName, &mapped_dll); auto baseNtDll = ApiWrapper::GetModuleBaseAddress(L"ntdll.dll"); if (!mapped_dll || !baseNtDll) { return 0; } auto originalFunc = (uint64_t)ApiWrapper::GetProcAddress((DWORD64)mapped_dll, ApiName); auto ZwUnmapViewOfSection = (t_ZwUnmapViewOfSection)ApiWrapper::GetProcAddress(baseNtDll, "ZwUnmapViewOfSection"); if (!originalFunc) //check for prevent SEH { //under x32dbg return 0 ????? ZwUnmapViewOfSection(NtCurrentProcess, mapped_dll); return false; } #ifdef _WIN64 originalSyscallNumber = *(short*)(originalFunc + 4); #else originalSyscallNumber = *(short*)(originalFunc + 1); #endif ZwUnmapViewOfSection(NtCurrentProcess, mapped_dll); return originalSyscallNumber; }
Api оболочки можешь найти в интернете
 
  • colby57
  • UNEXPECTED_KERNEL_MODE_TRAP_M (1000007f)
  • 155
  • 2
  • 240
Arting, colby57 здравствуйте, не подскажите где посмотреть реализацию функции GetSystemNumber?
И еще, почему у меня в ntdll.dll нет Wow64Transition?
GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "Wow64Transition" ) ничего не возвращает. Я даже через CE посмотрел функции внутри ntdll.dll, но там "Wow64Transition" попросту - нет.
адрес Wow64Transition достается через
mov eax, dword ptr fs:[0xC0]
 
  • 2
  • 0
Контакты для связи отсутствуют.
адрес Wow64Transition достается через
mov eax, dword ptr fs:[0xC0]
А может ли быть такое, что это не будет работать на Win7? Ибо даже готовое решение(архив прикрепленный выше от автора поста) у меня не хочет работать, попросту крашит.
 
Сверху Снизу