Загрузка...

Reversion Engineering Remote Protection

Thread in Reversing / Assembler created by SaintHuman Aug 10, 2025. 542 views

  1. SaintHuman
    В мире реверс-инжиниринга мы часто встречаем два типа защит (только если вы не вебер :roflanBuldiga:):

    Немного пояснений

    Локальные - вся логика проверки лицензии внутри исполняемого файла, и достаточно найти условие if (access) и патчить его на jmp в нужное место
    Удалённые - ключ, токен или вообще весь функционал проверяется на сервере. И тут уже никаким NOP-ом в бинарнике ты не заставишь сервер сказать "да" - он просто выдаст тебе 403

    Вторая категория куда интереснее (и сложнее). Когда вся критичная логика на стороне сервера, обычный патчинг клиентского кода теряет смысл - придётся лезть глубже в сетевое взаимодействие, протоколы и API.

    Представим, что у нас есть приложение, которое:

    1. При запуске обращается к api.lolzserver.com/auth
    2. Передаёт туда логин/пароль (или токен)
    3. Получает от сервера ответ "ОК" или "Ошибка ключа"
    4. Без положительного ответа — просто не даёт работать

    Задача реверсера в таких условиях - либо понять и воспроизвести протокол, чтобы подменить сервер, либо вклиниться между приложением и сервером, чтобы изменять ответы "на лету"

    Практический пример

    Берём исполняемый файл lolz_desktop.exe. При старте он:

    1. Подключается к api.lolzserver.com по HTTPS
    2. Проверяет TLS-сертификат через жёстко зашитый публичный ключ
    3. Запрашивает session_key для HMAC-подписи
    4. Все запросы и ответы дополнительно упакованы в AES-контейнер

    Что нам нужно? Всего то: убрать pinning, перехватывать session_key, понять криптографию траффика

    Как убрать TLS pinning
    TLS pinning — это механизм безопасности, который помогает защитить от атак посредника (MITM) и других атак, связанных с сертификатами


    В IDA Pro ищем строку api.lolzserver.com. От неё по cross-reference находим вызов WinHTTP API.
    Натыкаемся на это, в большинстве десктопа вы встретите нечто похожее:

    C
    .text:0045A1B0  call    ds:WinHttpReceiveResponse
    .text:0045A1B6 test eax, eax
    .text:0045A1B8 jz short loc_45A210
    .text:0045A1BA mov ecx, [ebp+cert_ctx]
    .text:0045A1BD call sub_401230 ; verify_cert_pubkey
    .text:0045A1C2 test eax, eax
    .text:0045A1C4 jz short loc_45A210
    Открываем sub_401230 — внутри хардкод на публичный ключ (256 байт в .rdata). Её надо будет изучать.
    Ловим HMAC session_key


    Через поиск по Imports находим CryptGenRandom, CryptImportKey, CryptEncrypt, CryptDecrypt.
    HMAC-ключ появляется при вызове CryptImportKey. Сделать это можно, хукнув функцию

    C
    BOOL WINAPI CryptImportKey(HCRYPTPROV hProv, const BYTE *pbData,
    DWORD dwDataLen, HCRYPTKEY hPubKey,
    DWORD dwFlags, HCRYPTKEY *phKey) {
    FILE *f = fopen("keydump.bin", "wb");
    fwrite(pbData, 1, dwDataLen, f);
    fclose(f);
    return Real_CryptImportKey(hProv, pbData, dwDataLen, hPubKey, dwFlags, phKey);
    }
    Расшифровка трафика
    Проверяем вызовы CryptDecrypt. Ставим хук:
    C
    BOOL WINAPI CryptDecrypt(HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final,
    DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen) {
    BOOL res = Real_CryptDecrypt(hKey, hHash, Final, dwFlags, pbData, pdwDataLen);
    FILE *f = fopen("decrypted.log", "ab");
    fwrite(pbData, 1, *pdwDataLen, f);
    fwrite("\n\n", 1, 2, f);
    fclose(f);
    return res;
    }
    Теперь после каждого расшифрования у нас в decrypted.log лежит чистый JSON

    После остается сделать лишь подмену ответов "на лету" через проверку вхождения


    (приложу скрины чуть позже)
     
Loading...