Начинающий
- Статус
- Оффлайн
- Регистрация
- 6 Июн 2024
- Сообщения
- 298
- Реакции
- 21
Обзор новых anti-debug и анти-vm в VMProtect 3.8.7 - 3.10.4
Не хочу много писать, просто коротко пройдусь по новым техникам и модифицирую TItanHide для обхода
Борьба с syscall
Все usermode-инструменты вроде ScyllaHide и ShardOD полагались на возможность заставить VMP выполнять прямые вызовы Nt API, но начиная с версии 3.8.7 (возможно, и раньше) это перестало работать.
Ранее, если VMP не находил syscall для известной версии из PEB->BuildVersion, он отображал ntdll.dll в память и проверял его resource-поля. В новых версиях это было убрано в пользу другого подхода.
Теперь VMProtect проверяет PEB->MajorVersion, и если значение ниже 10 - сразу выбрасывает Initialization error 3. Влияния PEB->MinorVersion я не заметил.
Затем проверяется KUSER_SHARED_DATA->NtBuildVersion, и если известная версия не найдена - маппится ntdll.dll, после чего начинается разбор PE Header и Export Section, проход по каждой функции и присвоение ей SSN (Syscall Number) на основе её порядкового номера. Остальные секции не трогает.
Мне не удалось вмешаться в этот процесс - любые попытки приводили к некорректной работе вызовов. Обнуление всех syscall уже после того, как они были получены на стек, также вызывает Initialization error 3.
А как насчёт Wine?
Для совместимости с Wine VMP раньше проверял бинарник на наличие этих функций в экспорте - wine_get_version и wine_get_host_version, при их обнаружении сразу начинал использовать Nt API напрямую. Сейчас это не сработает.
Интересные приёмы в анти-отладочных вызовах
Все новые приёмы нацелены либо на TitanHide, либо на плохо написанные реализации обхода.
Syscall с невыровненным указателем
В вызов намеренно передаётся невыровненный указатель
C++:
PPROCESS_BASIC_INFORMATION pbi =
(PPROCESS_BASIC_INFORMATION)(buf + 1);
ULONG retLen = 0;
NTSTATUS status =
NtQueryInformationProcess(
GetCurrentProcess(),
ProcessBasicInformation,
pbi, //<---- Невыровненный указатель
sizeof(PROCESS_BASIC_INFORMATION),
&retLen
);
if (status != STATUS_DATATYPE_MISALIGNMENT)
debugger_found();
Старый TitanHide проверял ProcessInformation != nullptr, но не проверял выравнивание указателя. Если передать pbi со смещением +1, настоящий kernel вернёт STATUS_DATATYPE_MISALIGNMENT, а хук нет.
NtCreateDebugObject + NtQueryObject
VMProtect создаёт debug object через NtCreateDebugObject, затем вызывает NtQueryObject(ObjectTypesInformation) и считает, сколько объектов типа DebugObject существует в системе.
C++:
HANDLE hDebug = NULL;
NtCreateDebugObject(&hDebug, DEBUG_ALL_ACCESS, nullptr, 0);
OBJECT_TYPE_INFORMATION info = {};
ULONG retLen = 0;
NtQueryObject(
hDebug,
ObjectTypeInformation,
&info,
sizeof(info),
&retLen
);
if (info.TotalNumberOfObjects == 0)
debugger_found();
Старый TitanHide просто обнулял счётчик TotalNumberOfObjects = 0.
Проверка размера для NtQueryObject
C++:
ULONG len = 0;
NTSTATUS st =
NtQueryObject(
hDbg,
ObjectTypeInformation,
nullptr,
0,
&len
);
if (st != STATUS_INFO_LENGTH_MISMATCH)
debugger_found();
В норме функция должна вернуть STATUS_INFO_LENGTH_MISMATCH, но hook TitanHide возвращает SUCCESS.
Что насчёт anti-vm?
Было бы неплохо добавить anti-vm в титанхайд, вот вызов который проверяет виртуалку
C++:
SYSTEM_FIRMWARE_TABLE_INFORMATION* fti = ...;
fti->ProviderSignature = "ACPI";
fti->Action = SystemFirmwareTable_Get;
fti->TableID = "FACP";
NtQuerySystemInformation(
SystemFirmwareTableInformation,
fti,
bufSize,
&retLen
);
static const char* vmStrings[] = { "VMware", "VBOX", "innotek", "QEMU", "bochs", "Hyper-V" };
for (auto& str : vmStrings)
if (memmem(fti->TableBuffer, fti->TableBufferLength, str, strlen(str)))
vm_found();
Поэтому мы хукнем вызов NtQuerySystemInformation с SystemFirmwareTableInformation, это таблицы ACPI/SMBIOS/firmware, содержащие сырые дампы BIOS/UEFI со строками вроде "VMware BIOS", "VBOX", "QEMU" и т.д.
Пожалуйста, авторизуйтесь для просмотра ссылки.
Пожалуйста, авторизуйтесь для просмотра ссылки.
Последнее редактирование: