- Статус
- Оффлайн
- Регистрация
- 13 Фев 2026
- Сообщения
- 682
- Реакции
- 18
Решил покопаться в недрах старичка Halo Combat Evolved и глянуть, как там реализована проверка целостности через BCrypt. Для тех, кто ковыряет движок или пишет свои инструменты под Halo, этот кусок кода может быть полезен, так как он наглядно показывает работу с RSA внутри игры.
Суть такова: функция sub_180C5CFAC принимает хеш, сигнатуру и некий индекс. По сути, это типичный валидатор, который сверяет данные с публичным ключом. Ниже сам код из IDA:
Что тут происходит на человеческом:
Логика с goto здесь — это просто классическая обработка ошибок в Си-стайле, чтобы всегда доходить до деструктора ключа в LABEL_12 и не оставлять висящих хендлов.
Кто-то пробовал подменять эти ключи в памяти для обхода проверки файлов или патчинга ресурсов?
Суть такова: функция sub_180C5CFAC принимает хеш, сигнатуру и некий индекс. По сути, это типичный валидатор, который сверяет данные с публичным ключом. Ниже сам код из IDA:
Код:
bool __fastcall sub_180C5CFAC(PUCHAR pbHash, PUCHAR pbSignature, __int64 a3)
{
int v5;
__int64 v6;
bool v7;
BCRYPT_ALG_HANDLE v8;
BCRYPT_KEY_HANDLE phKey;
const WCHAR *pPaddingInfo;
phKey = nullptr;
if ( !pbSignature || !a3 )
goto LABEL_11;
v5 = sub_180C5CF44(a3);
v6 = v5;
if ( v5 == -1 || v5 == 1 )
return 0;
v8 = phAlgorithm;
if ( !phAlgorithm )
{
if ( BCryptOpenAlgorithmProvider(&phAlgorithm, L"RSA", nullptr, 0) < 0 )
goto LABEL_11;
v8 = phAlgorithm;
}
if ( BCryptImportKeyPair(
v8,
nullptr,
L"RSAPUBLICBLOB",
&phKey,
*(PUCHAR *)&xmmword_182C98390[3 * v6],
*((_QWORD *)&xmmword_182C98390[3 * v6] + 1),
0) >= 0 )
{
pPaddingInfo = L"SHA1";
v7 = BCryptVerifySignature(phKey, &pPaddingInfo, pbHash, 0x14u, pbSignature, 0x100u, 2u) >= 0;
goto LABEL_12;
}
LABEL_11:
v7 = 0;
LABEL_12:
if ( phKey )
BCryptDestroyKey(phKey);
return v7;
}
Что тут происходит на человеческом:
- Сначала идет базовая проверка входных параметров. Если сигнатура пустая — сразу уходим в LABEL_11 (возврат false).
- Далее игра проверяет, открыт ли провайдер алгоритма RSA через BCryptOpenAlgorithmProvider. Если хендл phAlgorithm пустой, он инициализируется.
- Самый важный момент — импорт ключа. Используется RSAPUBLICBLOB. Данные ключа вытягиваются из глобального массива xmmword_182C98390 по индексу. Это отличная точка для реверса, если нужно найти сами публичные ключи в дампе.
- Затем вызывается BCryptVerifySignature. В качестве заполнения (padding) используется SHA1. Параметр 0x14u как раз намекает на длину хеша SHA1 (20 байт).
- В конце чистим за собой: BCryptDestroyKey уничтожает временный ключ, чтобы не текла память.
Логика с goto здесь — это просто классическая обработка ошибок в Си-стайле, чтобы всегда доходить до деструктора ключа в LABEL_12 и не оставлять висящих хендлов.
Кто-то пробовал подменять эти ключи в памяти для обхода проверки файлов или патчинга ресурсов?