Продавец
- Статус
- Оффлайн
- Регистрация
- 6 Май 2025
- Сообщения
- 259
- Реакции
- 249
Всем привет. В этой статье я поделюсь с вами информацией о том, как взламывался популярный чит Nixware на игру Counter-Strike 2.
На взлом данного чита было потрачено полтора дня.
**ИНЖЕКТ**
Вот так выглядит дерево действий лоадера:
NtMapViewOfSection ( выделение памяти для чита )
- > 4 потока на загрузку нужных модулей чита ( в нашем случае : kernel32, shell32, imm32, user32 )
- > поток RtlAddFunctionTable
- > поток RtlInsertFunctionTable
- > поток LdrpHandleTlsData
- > поток TlsCallback0 ( суть этого tls callback'a буквально в том, что там anti-debug чеки от vmp и если они что-то обнаружат, то в rax вернётся 6 и скорее всего аккаунт будет забанен )
- > поток TlsCallback1
- > поток EntryPoint
**Привязки / Расшифровки**
Что из динамического использует чит:
- 0x7FFE0260 ( KUSER_SHARED_DATA + 0x260 ) => NtBuildNumber
- 0x7FFE026C ( KUSER_SHARED_DATA + 0x26C ) => NtMajorVersion
- 0x7FFE02E8 ( KUSER_SHARED_DATA + 0x2e8 ) => NumberOfPhysicalPages
- 0x7FFE0020 ( KUSER_SHARED_DATA + 0x20 ) => TimeZoneBias
А также базовые адресы модулей из Peb->Ldr, при этом нужно ещё понимать, что если порядок загрузки модулей будет изменён, декрипты перестанут работать( но за всё время наблюдений, использовались только самые первые модули: ntdll, kernel32, kernelbase )
**Импорты**
Трудно что-либо сказать об импортах, ибо большинство попали под VMPROTECT, но есть и более интересные случаи.
Например, имплементация VirtualAlloc'a в чите:
Имплементация GetModuleHandleW:
По сути всё самое основное было указано, было ещё несколько моментов, из-за которых под виртой вызывался FatalExit, но почему это происходило указывать не стану. Честно говоря, от защиты чита ожидалось куда больше различных интересных проверок, но всё выглядит так, как будто этому не особо уделяли время. Всем пока
На взлом данного чита было потрачено полтора дня.
**ИНЖЕКТ**
Вот так выглядит дерево действий лоадера:
NtMapViewOfSection ( выделение памяти для чита )
- > 4 потока на загрузку нужных модулей чита ( в нашем случае : kernel32, shell32, imm32, user32 )
- > поток RtlAddFunctionTable
- > поток RtlInsertFunctionTable
- > поток LdrpHandleTlsData
- > поток TlsCallback0 ( суть этого tls callback'a буквально в том, что там anti-debug чеки от vmp и если они что-то обнаружат, то в rax вернётся 6 и скорее всего аккаунт будет забанен )
- > поток TlsCallback1
- > поток EntryPoint
**Привязки / Расшифровки**
Что из динамического использует чит:
- 0x7FFE0260 ( KUSER_SHARED_DATA + 0x260 ) => NtBuildNumber
- 0x7FFE026C ( KUSER_SHARED_DATA + 0x26C ) => NtMajorVersion
- 0x7FFE02E8 ( KUSER_SHARED_DATA + 0x2e8 ) => NumberOfPhysicalPages
- 0x7FFE0020 ( KUSER_SHARED_DATA + 0x20 ) => TimeZoneBias
А также базовые адресы модулей из Peb->Ldr, при этом нужно ещё понимать, что если порядок загрузки модулей будет изменён, декрипты перестанут работать( но за всё время наблюдений, использовались только самые первые модули: ntdll, kernel32, kernelbase )
**Импорты**
Трудно что-либо сказать об импортах, ибо большинство попали под VMPROTECT, но есть и более интересные случаи.
Например, имплементация VirtualAlloc'a в чите:
Код:
LPVOID __stdcall virtual_alloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
{
void *v5; // [rsp+48h] [rbp-18h] BYREF
SIZE_T v6[2]; // [rsp+50h] [rbp-10h] BYREF
v6[0] = dwSize;
v5 = lpAddress;
__asm { syscall; Low latency system call } // decrypted NtAllocateVirtualMemory index
if ( (int)(dword_167F7814B88
^ (((LODWORD(NtCurrentPeb()->Ldr->InMemoryOrderModuleList.Flink->Flink[2].Flink) ^ 0x7BAD7AEB)
* (LODWORD(NtCurrentPeb()->Ldr->InMemoryOrderModuleList.Flink->Flink->Flink[2].Flink)
^ (unsigned int)qword_167F7814B90
^ 0x7C2D7AB1)
+ (MEMORY[0x7FFE02E8] ^ (unsigned int)qword_167F7814B98 ^ 0xBF4D6FF6)) >> (MEMORY[0x7FFE026C]
^ qword_167F7814BA0
^ 0x2F))
& (MEMORY[0x7FFE0020]
^ qword_167F7814BA8
^ 0x80AD7ABF)) < (int)(LODWORD(NtCurrentPeb()->Ldr->InMemoryOrderModuleList.Flink->Flink[2].Flink)
^ qword_167F7814BC0
^ 0x8F2D7BB5) )
return 0;
if ( v5 && !byte_167F782D190 )
{
if ( *((_QWORD *)&xmmword_167F782D168 + 1) == qword_167F782D178 )
{
sub_167F704ABD0(&xmmword_167F782D168, *((_QWORD *)&xmmword_167F782D168 + 1), &v5, v6, 631720197, 631720293);
}
else
{
**((_QWORD **)&xmmword_167F782D168 + 1) = v5;
*((_QWORD *)&xmmword_167F782D168 + 1) += 8LL;
}
}
return v5;
}
Имплементация GetModuleHandleW:
Код:
__int64 __fastcall get_module_handle_w(__int64 mod_name)
{
return ((__int64 (__fastcall *)(__int64))(qword_167F7814B20
^ (((MEMORY[0x7FFE0020] ^ 0x572D7AE6)
* (MEMORY[0x7FFE0260] ^ (unsigned int)qword_167F7814B28 ^ 0x57AD7A6C)
+ (LODWORD(NtCurrentPeb()->Ldr->InMemoryOrderModuleList.Flink->Flink[2].Flink)
^ (unsigned int)qword_167F7814B30
^ 0xBF4D6FF6)) >> (LOBYTE(NtCurrentPeb()->Ldr->InMemoryOrderModuleList.Flink->Flink[2].Flink)
^ qword_167F7814B38
^ 0x14))
& (LODWORD(NtCurrentPeb()->Ldr->InMemoryOrderModuleList.Flink->Flink->Flink[2].Flink)
^ (unsigned int)qword_167F7814B40
^ 0x582D7A32)))(mod_name);
}
По сути всё самое основное было указано, было ещё несколько моментов, из-за которых под виртой вызывался FatalExit, но почему это происходило указывать не стану. Честно говоря, от защиты чита ожидалось куда больше различных интересных проверок, но всё выглядит так, как будто этому не особо уделяли время. Всем пока
