Гайд [Reverse-Engineering] Анализ анти-чита SCP: Secret Laboratory

Разработчик
Статус
Оффлайн
Регистрация
18 Мар 2020
Сообщения
439
Реакции[?]
870
Поинты[?]
195K
Летний шалом! Целых 4 месяца я не выпускал контент, связанный с анализом семплов и вот пришло время исправляться. Люди, подписанные на меня знают причину долгого отсутствия, если кому интересно, почитать можно здесь:
Пожалуйста, авторизуйтесь для просмотра ссылки.


- ПРЕДИСЛОВИЕ

Исходя из мнений моих коллег по ЦЕХу было принято решение проанализировать анти-чит из игры SCP: Secret Laboratory
В этой статье будет представлен анализ механизмов SCP Anti-Cheat, которые предназначены для предотвращения манипуляции с памятью игры.

По традиции, которую я позаимствовал у Arting, в начале статьи кратко опишу какими утилитами я пользовался во время анализа анти-чита:

Мой основной инструмент для проведения динамического анализа - x64dbg
Изредка, но всё же если и провожу статический анализ, то делаю это через IDA Pro 7.7.220118

Так же список плагинов для отладчика, которые я использовал:
- SharpOD — Анти-анти-дебаг плагин, который как по мне в некоторых моментах эффективнее ScyllaHide против протекторов (VMProtect, Themida).​
- x64dbgpy — для выполнения скриптов написанных на языке Python, использующие SDK отладчика.​

- Scylla (x64) — утилита для реконструктции импортов.
- Visual Studio 2019 — для написания своей DLL, которая выполняет все необходимые хуки и патчи.

Переходим непосредственно к самому анализу :)

- ЛАУНЧЕР И МОДУЛЬ В ОБЩЕМ ПЛАНЕ

Оба таргета имеют х64 архитектуру и накрыты протектором Themida, по всем канонам современной защиты имеют фулл-пресет защиты + разработчики во время валидации они будут использовать функцию из SDK Фемиды для проверки целостности модуля - SECheckCodeIntegrity.

Игра у нас на движке Unity, но почти вся защита находится в модуле SL-AC, который полностью написан на нативном С++ без всяких намёков на CLR-сборку.

- МОДУЛЬ АНТИ-ЧИТА

- Механизм обнаружения отладчика от Фемиды
Фемида палила мой отладчик даже после того как я перехукал всю таблицу системных вызовов, понятное дело, что системные вызовы Фемиды никто не отменял, но тем не менее само обнаружение отладчика состояло в том, чтобы обнаруживать название окна, я переименовал свой отладчик и на удивление протектор перестал выбивать сообщения о детектах :kappa:


- Инициализация
Я обратил внимание на то, что анти-чит тоскает за собой библиотеку MinHook, понял я это по Enum, которую оставляет за собой библиотека.

Код:
00007FFE3927DC0C  | 48:8D05 ED131200         | LEA RAX,QWORD PTR DS:[7FFE3939F000]         | 00007FFE3939F000:"MH_UNKNOWN"
00007FFE3927DC13  | EB 7C                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC15  | 48:8D05 F0131200         | LEA RAX,QWORD PTR DS:[7FFE3939F00C]         | 00007FFE3939F00C:"MH_OK"
00007FFE3927DC1C  | EB 73                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC1E  | 48:8D05 F3131200         | LEA RAX,QWORD PTR DS:[7FFE3939F018]         | 00007FFE3939F018:"MH_ERROR_ALREADY_INITIALIZED"
00007FFE3927DC25  | EB 6A                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC27  | 48:8D05 0A141200         | LEA RAX,QWORD PTR DS:[7FFE3939F038]         | 00007FFE3939F038:"MH_ERROR_NOT_INITIALIZED"
00007FFE3927DC2E  | EB 61                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC30  | 48:8D05 21141200         | LEA RAX,QWORD PTR DS:[7FFE3939F058]         | 00007FFE3939F058:"MH_ERROR_ALREADY_CREATED"
00007FFE3927DC37  | EB 58                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC39  | 48:8D05 38141200         | LEA RAX,QWORD PTR DS:[7FFE3939F078]         | 00007FFE3939F078:"MH_ERROR_NOT_CREATED"
00007FFE3927DC40  | EB 4F                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC42  | 48:8D05 47141200         | LEA RAX,QWORD PTR DS:[7FFE3939F090]         | 00007FFE3939F090:"MH_ERROR_ENABLED"
00007FFE3927DC49  | EB 46                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC4B  | 48:8D05 56141200         | LEA RAX,QWORD PTR DS:[7FFE3939F0A8]         | 00007FFE3939F0A8:"MH_ERROR_DISABLED"
00007FFE3927DC52  | EB 3D                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC54  | 48:8D05 65141200         | LEA RAX,QWORD PTR DS:[7FFE3939F0C0]         | 00007FFE3939F0C0:"MH_ERROR_NOT_EXECUTABLE"
00007FFE3927DC5B  | EB 34                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC5D  | 48:8D05 74141200         | LEA RAX,QWORD PTR DS:[7FFE3939F0D8]         | 00007FFE3939F0D8:"MH_ERROR_UNSUPPORTED_FUNCTION"
00007FFE3927DC64  | EB 2B                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC66  | 48:8D05 8B141200         | LEA RAX,QWORD PTR DS:[7FFE3939F0F8]         | 00007FFE3939F0F8:"MH_ERROR_MEMORY_ALLOC"
00007FFE3927DC6D  | EB 22                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC6F  | 48:8D05 9A141200         | LEA RAX,QWORD PTR DS:[7FFE3939F110]         | 00007FFE3939F110:"MH_ERROR_MEMORY_PROTECT"
00007FFE3927DC76  | EB 19                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC78  | 48:8D05 A9141200         | LEA RAX,QWORD PTR DS:[7FFE3939F128]         | 00007FFE3939F128:"MH_ERROR_MODULE_NOT_FOUND"
00007FFE3927DC7F  | EB 10                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC81  | 48:8D05 C0141200         | LEA RAX,QWORD PTR DS:[7FFE3939F148]         | 00007FFE3939F148:"MH_ERROR_FUNCTION_NOT_FOUND"
00007FFE3927DC88  | EB 07                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC8A  | 48:8D05 D7141200         | LEA RAX,QWORD PTR DS:[7FFE3939F168]         | 00007FFE3939F168:"(unknown)"
00007FFE3927DC91  | 48:83C4 10               | ADD RSP,10                                  |
Функция DllMain не виртуализирована Фемидой, это даёт нам возможность найти его по паттерну и поставить бряк на него.

1659943920873.png

Во время своей инициализации анти-чит вызывает виртуализированный CreateThread, этот поток считается главным, и первым что он сделает, так это установит свои хуки.

1659943930782.png

Рассмотрим список расставляемых хуков анти-читом (и чуть-чуть темидой)
Список хуков:

Код:
Импортируемая библиотека: GameAssembly.dll, экспортируемая функция: il2cpp_resolve_icall
Импортируемая библиотека: kernel32.dll, экспортируемая функция: GetModuleHandleW
Импортируемая библиотека: kernel32.dll, экспортируемая функция: GetProcAddress
Импортируемая библиотека: kernel32.dll, экспортируемая функция: LoadLibraryA
Импортируемая библиотека: kernel32.dll, экспортируемая функция: LoadLibraryW
Импортируемая библиотека: user32.dll, экспортируемая функция: GetAsyncKetState
Импортируемая библиотека: ntdll.dll, экспортируемая функция: DbgUiRemoteBreakin (Поставлен Фемидой, чтобы при попытке присоединиться к процессу кидало на LdrShutdownProcess :kappa:)
Импортируемая библиотека: ntdll.dll, экспортируемая функция: NtOpenFile
- Блокировка инжекта (Хук NtOpenFile)
Он устанавливается сразу же после установки хука на GetProcAddress.

В декомпилированном hkNtOpenFile прослеживаются вызовы конструктора std::wstring, что может говорить о том, что скорее всего конструктор в дальнейшем будет использоваться для работы со структурой POBJECT_ATTRIBUTES.

Чтобы получать имя объекта хук читает сразу два указателя на структуры: POBJECT_ATTRIBUTES и PUNICODE_STRING
Чтение происходит таким образом: pObjectAttributes->pObjectName->Buffer.

1659943992549.png

В последующем анти-чит будет проверять находится ли бинарник в папке Windows и имеет при себе разные проверки, связанные с сертификатом.



Если же файла не было в Windows, то в связке с вызовами CryptQueryObject, CertGetNameStringW, CertFindCertificateInStore, он начинает проверять издателя цифровой подписи и получать имя эмитента.





Пройдя дальше я попал на завиртуализированную функцию, которую я назвал IsBlacklistedCert.
Функция внутри виртуальной машины темиды сравнивает полученный сертификат с запрещенными сертификатами, который хранится также под виртуализацией.

Под проверку попали такие сертификаты, как: ReShade, MIDKNIGHT LLC и Cheat Engine, если что-то из них он обнаруживал у файла, то функция возвращала TRUE.



Под конец всей проверки он делает для себя лог: [Windows Folder - %s]; [Signing: %s - %s]

- ОБХОДИМ БЛОКИРОВКУ ИНЖЕКТА

Есть много способов обмануть анти-чит и дать заинжектиться своей длл, начиная от инжекта до того как анти-чит установит свои хуки и заканчивая тем, что в наглую можно блокировать хук анти-чита. Мой способ будет связан с перехватом функции для расстановки хуков анти-чита.

Так как функции либы с хуками не находится под виртой, то нам ничего не мешает найти MH_EnableHook и MH_CreateHook по паттернам.

Для нахождения этих функции я составил два паттерна:

Код:
4C 24 08 57 48 81 EC 90 00 00 00 48 8B FC B9 24 00 00 00 B8 CC CC CC CC F3 AB 48 8B 8C 24 A0 00 00 00 - MH_CreateHook
48 89 4C 24 08 57 48 83 EC 20 48 8B FC B9 08 00 00 - MH_EnableHook
И вуаля! Наши функции для перехватывания были найдены





Помимо того, что с помощью перехвата MH_EnableHook я буду блокировать активацию hkNtOpenFile, возвращая MH_OK параллельно я мониторил какие хуки он ещё расставлял, и в своей консоли оставлял адреса детуров анти-чита.



Конечно же эти проверки на подлинность файла не являются эффективными, можно пропустить эти проверки одним патчем инструкции JNE, или же поставить точку останова на CloseHandle, потому что именно он отвечает за блокировку дескриптора. Никакого сисколла NtClose, просто обыкновенный CloseHandle.

А вот и мемный обход блокировки инжекта

Недавно разработчики обновляли анти-чит, из-за этой обновы все мои комментарии и метки на функции пошли по пизде, и они виртуализировали пару функции и ещё некоторый пользовательский код, CloseHandle был в их числе. :roflanEbalo:



- Хук GetAsyncKeyState
Этот хук передает анти-читу информацию о адресе возврата. А также в хуке вызывается RtlPcToFileHeader для чтения PE хэдера у файла, с которого была вызвана эта функция, если у файла не оказалось первых двух байтов - 0x5A4D, то для анти-чита это первый звоночек зарепортить пользователя.

Всё это примерно выглядит таким образом:

C++:
PVOID imageBase;

RtlPcToFileHeader( _ReturnAddress( ), &imageBase );

if ( *( WORD * ) imageBase != 0x5A4D )
     // Hidden module
else
    // OK
- Обнаружение по окнам/процессу наше всё!
На дворе уже прошла половина 2022 года, и к сожалению если вы ждали здесь гениальные механизмы обнаружения отладчика, то забудьте про это, потому что и Фемида и разработчики Northwood Studios решили использовать одинаковые техники обнаружения. Чтобы обнаруживать нежелательные процессы такие как: OllyDbg, Cheat Engine, Process Hacker, Process Monitor Фемида использует FindWindowA, а анти-чит в свою очередь использует два механизма: Обнаружение окна и обнаружение процесса.

- Обнаружение виртуальной машины
Опять же, если вы думали, что анти-чит будет использовать методы обхода песочницы/вм, основанные на времени, то вы сильно заблуждались.
В совокупности с обнаружением нежелательных процессов второй механизм использует в своём списке и системные процессы VMWare. Всё это реализовано в булевой функции, если функция вернула - 1, значит он обнаружил что-то из разряда VMWare и выбил ошибку. Патчится это двумя инструкциями "xor eax, eax; ret;"



Ну и сам мем



- Второй механизм обнаружения
Второй метод заключается в злоупотреблении функции EnumWindows, если быть точнее, то его каллбеком, который выступает первым параметром. (
Пожалуйста, авторизуйтесь для просмотра ссылки.
)

Эта функция будет вызываться частенько, поэтому перехватываем её и возвращаем FALSE, потому что анти-чит не проверяет возвращаемое значение функции.

Получаем последнее китайское предупреждение от анти-чита и пропускаем его со спокойной душой, потому что игра продолжит загрузку в главное меню.



Эту технику опрометчиво использовать в своих механизмах обнаружений, только если на ваш продукт не покушаются реверс-инженеры, не имеющие опыта в динамическом анализе.

- Техника Heaven's Gate
Да-да, анти-чит под х64, но тем не менее используют данную технику, и по всей видимости для предотвращения трассировки, потому что как мы все знаем xdbg не умеет проходить сквозь инструкцию ret far, как тот же Cheat Engine или WinDbg.

Эти функции проверяют наличие дебаг флагов и дебаг порта у текущего процесса

Код:
00007FF907136CB1  | 6A 33                    | PUSH 33                                                 | 0x33 - Доступ к х64 сегменту кода, 0x23 - к х32
00007FF907136CB3  | E8 00000000              | CALL sl-ac.7FF907136CB8                                 | call $0
00007FF907136CB8  | 830424 05                | ADD DWORD PTR SS:[RSP],5                                |
00007FF907136CBC  | CB                       | RET FAR                                                 |
00007FF907136CBD  | 49:89D8                  | MOV R8,RBX                                              |
00007FF907136CC0  | 48:89E3                  | MOV RBX,RSP                                             |
00007FF907136CC3  | FF3424                   | PUSH QWORD PTR SS:[RSP]                                 |
00007FF907136CC6  | FF3424                   | PUSH QWORD PTR SS:[RSP]                                 |
00007FF907136CC9  | 40:80E4 F0               | AND SPL,F0                                              | Выравнивание стека 16 байт
00007FF907136CCD  | 48:83C4 10               | ADD RSP,10                                              |
00007FF907136CD1  | 48:83EC 30               | SUB RSP,30                                              |
00007FF907136CD5  | 48:C7C1 FFFFFFFF         | MOV RCX,FFFFFFFFFFFFFFFF                                | Текущий процесс
00007FF907136CDC  | 48:C7C2 1E000000         | MOV RDX,1E                                              | Process Debug Port
00007FF907136CE3  | 49:C7C1 08000000         | MOV R9,8                                                |
00007FF907136CEA  | 48:C74424 20 00000000    | MOV QWORD PTR SS:[RSP+20],0                             |
00007FF907136CF3  | FFD6                     | CALL RSI                                                | Вызываем системный вызов
00007FF907136CF5  | 48:83C4 30               | ADD RSP,30                                              |
00007FF907136CF9  | 48:89DC                  | MOV RSP,RBX                                             |
00007FF907136CFC  | E8 00000000              | CALL sl-ac.7FF907136D01                                 | call $0
00007FF907136D01  | C74424 04 23000000       | MOV DWORD PTR SS:[RSP+4],23                             |
00007FF907136D09  | 830424 0D                | ADD DWORD PTR SS:[RSP],D                                |
00007FF907136D0D  | CB                       | RET FAR                                                 |
Код:
00007FF90721501C  | E8 00000000              | CALL sl-ac.7FF907215021                                 | call $0
00007FF907215021  | 830424 05                | ADD DWORD PTR SS:[RSP],5                                |
00007FF907215025  | CB                       | RET FAR                                                 |
00007FF907215026  | 49:89D8                  | MOV R8,RBX                                              |
00007FF907215029  | 48:89E3                  | MOV RBX,RSP                                             |
00007FF90721502C  | FF3424                   | PUSH QWORD PTR SS:[RSP]                                 |
00007FF90721502F  | FF3424                   | PUSH QWORD PTR SS:[RSP]                                 |
00007FF907215032  | 40:80E4 F0               | AND SPL,F0                                              | Выравнивание стека 16 байт
00007FF907215036  | 48:83C4 10               | ADD RSP,10                                              |
00007FF90721503A  | 48:83EC 30               | SUB RSP,30                                              |
00007FF90721503E  | 48:C7C1 FFFFFFFF         | MOV RCX,FFFFFFFFFFFFFFFF                                |
00007FF907215045  | 48:C7C2 07000000         | MOV RDX,7                                               | Process Debug Flags
00007FF90721504C  | 49:C7C1 08000000         | MOV R9,8                                                |
00007FF907215053  | 48:C74424 20 00000000    | MOV QWORD PTR SS:[RSP+20],0                             |
00007FF90721505C  | FFD6                     | CALL RSI                                                |
00007FF90721505E  | 48:83C4 30               | ADD RSP,30                                              |
00007FF907215062  | 48:89DC                  | MOV RSP,RBX                                             |
00007FF907215065  | E8 00000000              | CALL sl-ac.7FF90721506A                                 | call $0
00007FF90721506A  | C74424 04 23000000       | MOV DWORD PTR SS:[RSP+4],23                             |
00007FF907215072  | 830424 0D                | ADD DWORD PTR SS:[RSP],D                                |
00007FF907215076  | CB                       | RET FAR                                                 |
Что ещё меня поразило, так это их попытка завернуть данную технику под виртуальную машину Фемиды, потому что это эта функция находилась в сегменте Фемиды, где выполняется ВМ. Видимо, Nortwood Studios не вкурсе, что протекторы не могут закинуть технику Heaven's Gate как раз из-за инструкции RET FAR. Поэтому такой чистый код валяется в сегменте среди виртуализированного кода.

- Сканирование памяти
На этом этапе защиты анти-чит с помощью GetStartUpInfo и VirtualQuery проходится по регионам памяти

Код:
00007FFEF2E19B70  | 48:895424 10             | MOV QWORD PTR SS:[RSP+10],RDX          | Memory Scan
00007FFEF2E19B75  | 48:894C24 08             | MOV QWORD PTR SS:[RSP+8],RCX           |
00007FFEF2E19B7A  | 48:81EC 88000000         | SUB RSP,88                             |
00007FFEF2E19B81  | 48:8B05 985A1300         | MOV RAX,QWORD PTR DS:[7FFEF2F4F620]    |
00007FFEF2E19B88  | 48:33C4                  | XOR RAX,RSP                            |
00007FFEF2E19B8B  | 48:894424 78             | MOV QWORD PTR SS:[RSP+78],RAX          |
00007FFEF2E19B90  | 0FB605 C1E21300          | MOVZX EAX,BYTE PTR DS:[7FFEF2F57E58]   |
00007FFEF2E19B97  | 85C0                     | TEST EAX,EAX                           |
00007FFEF2E19B99  | 75 0D                    | JNE sl-ac.7FFEF2E19BA8                 |
00007FFEF2E19B9B  | 48:8D0D 7EE41300         | LEA RCX,QWORD PTR DS:[7FFEF2F58020]    |
00007FFEF2E19BA2  | FF15 58350F00            | CALL QWORD PTR DS:[7FFEF2F0D100]       | Virtualized GetSystemInfo
00007FFEF2E19BA8  | 48:8B05 79E41300         | MOV RAX,QWORD PTR DS:[7FFEF2F58028]    |
00007FFEF2E19BAF  | 48:894424 20             | MOV QWORD PTR SS:[RSP+20],RAX          |
00007FFEF2E19BB4  | 48:8B05 75E41300         | MOV RAX,QWORD PTR DS:[7FFEF2F58030]    |
00007FFEF2E19BBB  | 48:894424 28             | MOV QWORD PTR SS:[RSP+28],RAX          |
00007FFEF2E19BC0  | 48:8B4424 28             | MOV RAX,QWORD PTR SS:[RSP+28]          |
00007FFEF2E19BC5  | 48:394424 20             | CMP QWORD PTR SS:[RSP+20],RAX          |
00007FFEF2E19BCA  | 0F83 83000000            | JAE sl-ac.7FFEF2E19C53                 |
00007FFEF2E19BD0  | 41:B8 30000000           | MOV R8D,30                             |
00007FFEF2E19BD6  | 48:8D5424 48             | LEA RDX,QWORD PTR SS:[RSP+48]          |
00007FFEF2E19BDB  | 48:8B4C24 20             | MOV RCX,QWORD PTR SS:[RSP+20]          |
00007FFEF2E19BE0  | FF15 2A350F00            | CALL QWORD PTR DS:[7FFEF2F0D110]       | Virtualized VirtualQuery
00007FFEF2E19BE6  | 8B4424 68                | MOV EAX,DWORD PTR SS:[RSP+68]          | Полученный размер региона памяти
00007FFEF2E19BEA  | 25 00100000              | AND EAX,1000                           |
00007FFEF2E19BEF  | 3D 00100000              | CMP EAX,1000                           | Если после (regionSize & 0x1000) не дал в результате 0x1000, то скип
00007FFEF2E19BF4  | 75 43                    | JNE sl-ac.7FFEF2E19C39                 |
00007FFEF2E19BF6  | 837C24 6C 20             | CMP DWORD PTR SS:[RSP+6C],20           | Проверка на PAGE_EXECUTE_READ
00007FFEF2E19BFB  | 74 07                    | JE sl-ac.7FFEF2E19C04                  |
00007FFEF2E19BFD  | 837C24 6C 40             | CMP DWORD PTR SS:[RSP+6C],40           | Проверка на PAGE_EXECUTE_READWRITE
00007FFEF2E19C02  | 75 35                    | JNE sl-ac.7FFEF2E19C39                 |
00007FFEF2E19C04  | 48:8B4424 48             | MOV RAX,QWORD PTR SS:[RSP+48]          |
00007FFEF2E19C09  | 48:894424 30             | MOV QWORD PTR SS:[RSP+30],RAX          |
00007FFEF2E19C0E  | 48:8B4424 60             | MOV RAX,QWORD PTR SS:[RSP+60]          |
00007FFEF2E19C13  | 48:894424 38             | MOV QWORD PTR SS:[RSP+38],RAX          |
00007FFEF2E19C18  | 48:8B4424 30             | MOV RAX,QWORD PTR SS:[RSP+30]          |
00007FFEF2E19C1D  | 48:894424 40             | MOV QWORD PTR SS:[RSP+40],RAX          |
00007FFEF2E19C22  | 4C:8B4424 38             | MOV R8,QWORD PTR SS:[RSP+38]           |
00007FFEF2E19C27  | 48:8B5424 40             | MOV RDX,QWORD PTR SS:[RSP+40]          |
00007FFEF2E19C2C  | 48:8B8C24 98000000       | MOV RCX,QWORD PTR SS:[RSP+98]          |
00007FFEF2E19C34  | E8 17040000              | CALL sl-ac.7FFEF2E1A050                |
00007FFEF2E19C39  | 48:8B4424 60             | MOV RAX,QWORD PTR SS:[RSP+60]          |
00007FFEF2E19C3E  | 48:8B4C24 20             | MOV RCX,QWORD PTR SS:[RSP+20]          |
00007FFEF2E19C43  | 48:03C8                  | ADD RCX,RAX                            |
00007FFEF2E19C46  | 48:8BC1                  | MOV RAX,RCX                            |
00007FFEF2E19C49  | 48:894424 20             | MOV QWORD PTR SS:[RSP+20],RAX          |
00007FFEF2E19C4E  | E9 6DFFFFFF              | JMP sl-ac.7FFEF2E19BC0                 |
00007FFEF2E19C53  | 48:8B8C24 98000000       | MOV RCX,QWORD PTR SS:[RSP+98]          |
00007FFEF2E19C5B  | E8 800AFAFF              | CALL sl-ac.7FFEF2DBA6E0                |
00007FFEF2E19C60  | 48:8B4C24 78             | MOV RCX,QWORD PTR SS:[RSP+78]          |
00007FFEF2E19C65  | 48:33CC                  | XOR RCX,RSP                            |
00007FFEF2E19C68  | E8 B3160B00              | CALL sl-ac.7FFEF2ECB320                |
00007FFEF2E19C6D  | 48:81C4 88000000         | ADD RSP,88                             |
00007FFEF2E19C74  | C3                       | RET                                    |
В декомпилированном виде:



- Сканирование паттернов читов в памяти

Помимо того что анти-чит сканирует всю память, он также успевает в полученных регионах памяти проверять список паттернов, которые он собирал у других читов.

Код:
"\x41\x69\x6D\x62\x6F\x74\x20\x54\x61\x72\x67\x65\x74" - Aimbot Target
"\x53\x63\x69\x65\x6E\x74\x69\x73\x74\x20\x4B\x65\x79\x63\x61\x72\x64" - Scientist Keycard
"\x70\x65\x64\x6F\x70\x68\x69\x6C\x65\x67\x61\x6D\x69\x6E\x67\x2E\x63\x63" - pedophilegaming.cc
"\x6B\x69\x74\x65\x68\x34" - kiteh4x
"\x3C\x63\x6F\x6C\x6F\x72\x3D\x77\x68\x69\x74\x65\x3E\x70\x6C\x61\x79\x65\x72\x73\x3A\x20\x25\x69\x3C\x2F\x63\x6F\x6C\x6F\x72\x3E" - <color=white>players: %i</color>
"\x3C\x63\x6F\x6C\x6F\x72\x3D\x25\x73\x3E\x25\x73\x20\x2D\x20\x25\x2E\x32\x66\x3C\x2F\x63\x6F\x6C\x6F\x72\x3E"- <color=%s>%s - %.2f</color>
"\x23\x23\x4D\x61\x69\x6E\x4D\x65\x6E\x75\x42\x61\x72" - ##MainMenuBar
- Связь с защищаемым потоком

Для того чтобы поддерживать защищаемый поток анти-чит создаёт 1 поток. Анти-чит берёт айди потока и будет всячески проверять его через некоторое время, используя Sleep.

Проверка на то, работает ли защищаемый поток:

Код:
00007FFE2C77B3D9  | 48:8B4424 28             | MOV RAX,QWORD PTR SS:[RSP+28]               |
00007FFE2C77B3DE  | 48:83C0 08               | ADD RAX,8                                   |
00007FFE2C77B3E2  | 48:894424 28             | MOV QWORD PTR SS:[RSP+28],RAX               |
00007FFE2C77B3E7  | 48:8B4424 38             | MOV RAX,QWORD PTR SS:[RSP+38]               |
00007FFE2C77B3EC  | 48:394424 28             | CMP QWORD PTR SS:[RSP+28],RAX               |
00007FFE2C77B3F1  | 0F84 A4000000            | JE sl-ac.7FFE2C77B49B                       |
00007FFE2C77B3F7  | 48:8B4424 28             | MOV RAX,QWORD PTR SS:[RSP+28]               |
00007FFE2C77B3FC  | 48:894424 40             | MOV QWORD PTR SS:[RSP+40],RAX               |
00007FFE2C77B401  | 48:8D0D 48CA1300         | LEA RCX,QWORD PTR DS:[7FFE2C8B7E50]         |
00007FFE2C77B408  | E8 F384F9FF              | CALL sl-ac.7FFE2C713900                     |
00007FFE3259B40D  | 48:894424 50             | MOV QWORD PTR SS:[RSP+50],RAX               |
00007FFE3259B412  | 48:8B4424 40             | MOV RAX,QWORD PTR SS:[RSP+40]               |
00007FFE3259B417  | 48:8B00                  | MOV RAX,QWORD PTR DS:[RAX]                  |
00007FFE3259B41A  | 48:894424 48             | MOV QWORD PTR SS:[RSP+48],RAX               |
00007FFE3259B41F  | 48:8B5424 48             | MOV RDX,QWORD PTR SS:[RSP+48]               |
00007FFE3259B424  | 48:8B4C24 50             | MOV RCX,QWORD PTR SS:[RSP+50]               |
00007FFE3259B429  | E8 72BD0000              | CALL sl-ac.7FFE325A71A0                     | Проверяем не откинулся ли поток
00007FFE3259B42E  | 0FB6C0                   | MOVZX EAX,AL                                |
00007FFE3259B431  | 85C0                     | TEST EAX,EAX                                |
00007FFE3259B433  | 75 56                    | JNE sl-ac.7FFE3259B48B                      |
00007FFE3259B435  | 48:8D4424 20             | LEA RAX,QWORD PTR SS:[RSP+20]               |
00007FFE3259B43A  | 48:8BF8                  | MOV RDI,RAX                                 |
00007FFE3259B43D  | 33C0                     | XOR EAX,EAX                                 |
00007FFE3259B43F  | B9 01000000              | MOV ECX,1                                   |
00007FFE3259B444  | F3:AA                    | REP STOSB                                   |
00007FFE3259B446  | 48:8D4424 21             | LEA RAX,QWORD PTR SS:[RSP+21]               |
00007FFE3259B44B  | 48:8BF8                  | MOV RDI,RAX                                 |
00007FFE3259B44E  | 33C0                     | XOR EAX,EAX                                 |
00007FFE3259B450  | B9 01000000              | MOV ECX,1                                   |
00007FFE3259B455  | F3:AA                    | REP STOSB                                   |
00007FFE3259B457  | 48:8D4424 22             | LEA RAX,QWORD PTR SS:[RSP+22]               |
00007FFE3259B45C  | 48:8BF8                  | MOV RDI,RAX                                 |
00007FFE3259B45F  | 33C0                     | XOR EAX,EAX                                 |
00007FFE3259B461  | B9 01000000              | MOV ECX,1                                   |
00007FFE3259B466  | F3:AA                    | REP STOSB                                   |
00007FFE3259B468  | 44:0FB64C24 20           | MOVZX R9D,BYTE PTR SS:[RSP+20]              |
00007FFE3259B46E  | 44:0FB64424 21           | MOVZX R8D,BYTE PTR SS:[RSP+21]              |
00007FFE3259B474  | 0FB65424 22              | MOVZX EDX,BYTE PTR SS:[RSP+22]              |
00007FFE3259B479  | 48:8D4C24 60             | LEA RCX,QWORD PTR SS:[RSP+60]               |
00007FFE3259B47E  | E8 4D000000              | CALL sl-ac.7FFE3259B4D0                     |
00007FFE3259B483  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE3259B486  | E8 55040000              | CALL sl-ac.7FFE3259B8E0                     | Лог "A SL-AC thread has stopped execution inadvertently!"
00007FFE3259B48B  | B9 0F000000              | MOV ECX,F                                   |
00007FFE3259B490  | FF15 8A1D0F00            | CALL QWORD PTR DS:[<&Sleep>]                |
00007FFE3259B496  | E9 3EFFFFFF              | JMP sl-ac.7FFE3259B3D9                      | Возвращаемся на JE проверку
00007FFE3259B49B  | 48:8D8C24 A0000000       | LEA RCX,QWORD PTR SS:[RSP+A0]               |
00007FFE3259B4A3  | E8 7880FCFF              | CALL sl-ac.7FFE32563520                     |
00007FFE3259B4A8  | 48:8B8C24 A8000000       | MOV RCX,QWORD PTR SS:[RSP+A8]               |
00007FFE3259B4B0  | 48:33CC                  | XOR RCX,RSP                                 |
00007FFE3259B4B3  | E8 68FE0A00              | CALL sl-ac.7FFE3264B320                     |
00007FFE3259B4B8  | 48:81C4 B0000000         | ADD RSP,B0                                  |
00007FFE3259B4BF  | 5F                       | POP RDI                                     |
00007FFE3259B4C0  | C3                       | RET                                         |
- Что же будет, если поток будет заморожен? Ничего.
- Что будет, если поток будет закрыт? Анти-чит начнёт спамить логами, что потока больше нет.

- Защищаемый поток

В защищаемом потоке нет ничего необычного, вначале вызывается GetTickCount64, затем сразу же вызываются две функции по несколько раз, где тоже фигурирует GetTickCount64, всё это дело выглядит следующим образом:

Код:
00007FFE2D3CD930  | 48:894C24 08             | MOV QWORD PTR SS:[RSP+8],RCX                |
00007FFE2D3CD935  | 48:83EC 28               | SUB RSP,28                                  |
00007FFE2D3CD939  | 48:8D0D D0A41300         | LEA RCX,QWORD PTR DS:[7FFE2D507E10]         |
00007FFE2D3CD940  | E8 CB9DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD945  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD948  | E8 330F0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD94D  | 48:8D0D 5CA41300         | LEA RCX,QWORD PTR DS:[7FFE2D507DB0]         |
00007FFE2D3CD954  | E8 B79DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD959  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD95C  | E8 1F0F0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD961  | 48:8D0D 98A41300         | LEA RCX,QWORD PTR DS:[7FFE2D507E00]         |
00007FFE2D3CD968  | E8 A39DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD96D  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD970  | E8 0B0F0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD975  | 48:8D0D B4A41300         | LEA RCX,QWORD PTR DS:[7FFE2D507E30]         |
00007FFE2D3CD97C  | E8 8F9DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD981  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD984  | E8 F70E0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD989  | 48:8D0D 90A41300         | LEA RCX,QWORD PTR DS:[7FFE2D507E20]         |
00007FFE2D3CD990  | E8 7B9DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD995  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD998  | E8 E30E0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD99D  | 48:8D0D 3CA41300         | LEA RCX,QWORD PTR DS:[7FFE2D507DE0]         |
00007FFE2D3CD9A4  | E8 679DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD9A9  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD9AC  | E8 CF0E0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD9B1  | EB 00                    | JMP sl-ac.7FFE2D3CD9B3                      |
00007FFE2D3CD9B3  | 48:83C4 28               | ADD RSP,28                                  |
00007FFE2D3CD9B7  | C3                       | RET                                         |
В функции по адресу 7FFE2D3CE880 постоянно будет сравнивать текущий результат вызова GetTickCount64 с результатом, который был в прошлом вызове.

- Немного о GetTickCount64

И раз уже решили затронуть тему с GetTickCount64, то сейчас я попробую объяснить метод работы этой функции и метод обхода.
Если в дебаггере взглянуть на содержимое этой функции, то она довольно простая в реализации.

Код:
00007FFE9EDD5D30  | 8B0C25 0400FE7F          | MOV ECX,DWORD PTR DS:[7FFE0004]             |
00007FFE9EDD5D37  | B8 2003FE7F              | MOV EAX,7FFE0320                            |
00007FFE9EDD5D3C  | 48:C1E1 20               | SHL RCX,20                                  |
00007FFE9EDD5D40  | 48:8B00                  | MOV RAX,QWORD PTR DS:[RAX]                  |
00007FFE9EDD5D43  | 48:C1E0 08               | SHL RAX,8                                   |
00007FFE9EDD5D47  | 48:F7E1                  | MUL RCX                                     |
00007FFE9EDD5D4A  | 48:8BC2                  | MOV RAX,RDX                                 |
00007FFE9EDD5D4D  | C3                       | RET                                         |
000000007FFE0000 - Статичный адрес структуры KUSER_SHARED_DATA (
Пожалуйста, авторизуйтесь для просмотра ссылки.
), к которому обращается функция GetTickCount64 первой инструкцией, и получает значение TickCount.HighPart
Вторая инструкция запихивает TickCount.LowPart в EAX

Примерный декомпиль функции из иды:

C++:
ULONG GetTickCount64( )
{
    return ( ( TickCount.HighPart << 32 ) * ( TickCount.LowPart << 8 ) ) >> 64;
}
Итак, мы знаем, что в KUSER_SHARED_DATA нельзя ничего записывать, потому что структура только Read Only, но мы можем перехватить GetTickCount64, код хука я показывать не буду, а опишу лишь кратко его алгоритм.

  1. Сверяем адрес возврата (GetTickCount64 помимо анти-чита используется и в системных библиотеках)
  2. Пишем пару проверок на промежуток времени, который для анти-чита будет подозрительным
  3. Возвращаем функции свою константу.

- Сбор данных об комплектующих ПК

Анти-чит собирает информацию об аппаратных устройствах ПК за счёт поступаемых запросов WMI.

Для начала модуль вызывает функцию SysAllocString с аргументом "ROOT\\CIMV2", чтобы в дальнейшем с помощью указателя на интерфейс IWbemLocator вызвать функцию ConnectServer для подключения к локальному пространству имен ROOT\CIMV2, также в ConnectServer последним параметром выступает IWbemServices **ppNamespace, этот указатель на интерфейс понадобиться для выполнения вызовов IWbemServices, для тех кто в танке - через этот интерфейс потом можно будет выполнять запрос к WMI с помощью ExecQuery, но перед этим анти-чит ещё и вызовет CoSetProxyBlanket.

Список запросов анти-чита:

SELECT * FROM Win32_BaseBoard - класс Win32_BaseBoard предоставляет информацию о материнской плате компьютера, анти-чит берёт серийный номер и строковое значение Manufacturer, в котором содержится название прозиводителя материнской платы. В моём случае после исполнения функции анти-чит в результате получит название - Gigabyte Technology Co, Ltd.

SELECT * FROM Win32_ComputerSystem - из класса Win32_ComputerSystem анти-чит берёт только строковое значение Model.

SELECT * FROM Win32_DiskDrive - класс Win32_DiskDrive, содержащий в себе информацию о имеющихся жёстких дисках, анти-чит из этого класса читает только серийный номер.

SELECT * FROM Win32_Processor - класс Win32_Processor, содержащий в себе информацию о процессоре, анти-чит читает только имя процессора.

SELECT * FROM Win32_NetworkAdapter - класс Win32_NetworkAdapter, содержащий в себе информацию о сетевом адапторе, по документации MSDN этот класс устарел, и рекомендуют использовать вместо него класс MSFT_NetAdapter. Анти-чит читает оттуда MAC-Адрес.

SELECT * FROM Win32_VideoController - класс Win32_VideoController, позволяющий получить информацию о видеокарте, из этого класса можно много каких свойств приметить для дальнейшей генерации хвида, но разработчики, к сожалению читают тут только имя видеокарты.

Я не исключаю того, что анти-чит помимо перечисленного собирает и другую информацию, однако я продемонстрировал именно те методы, которые нашёл при анализе.

- Логгирование анти-чита как произведение исскуства
После общего анализа стоит упомянуть одну примечательную вещь, которая помогла мне выявить дальнейшие действия анти-чита - это логгирование.
Перед тем как выполнить какое-то важное действие анти-чит логгирует его для самого себя, естественно, используя для этого ксор строк.
Поэтому когда я узнал об этом я начал искать дешифрование строки, нашёл я её быстро.

Сама же функция дешифрования строки выглядит следующим образом:

Код:
00007FFEE7508470  | 4C:894424 18             | MOV QWORD PTR SS:[RSP+18],R8           |
00007FFEE7508475  | 48:895424 10             | MOV QWORD PTR SS:[RSP+10],RDX          |
00007FFEE750847A  | 48:894C24 08             | MOV QWORD PTR SS:[RSP+8],RCX           |
00007FFEE750847F  | 48:83EC 38               | SUB RSP,38                             |
00007FFEE7508483  | 48:8B4424 48             | MOV RAX,QWORD PTR SS:[RSP+48]          |
00007FFEE7508488  | F3:0F6F00                | MOVDQU XMM0,XMMWORD PTR DS:[RAX]       |
00007FFEE750848C  | 66:0F7F4424 10           | MOVDQA XMMWORD PTR SS:[RSP+10],XMM0    |
00007FFEE7508492  | 48:8B4424 50             | MOV RAX,QWORD PTR SS:[RSP+50]          |
00007FFEE7508497  | F3:0F6F00                | MOVDQU XMM0,XMMWORD PTR DS:[RAX]       |
00007FFEE750849B  | 66:0F7F0424              | MOVDQA XMMWORD PTR SS:[RSP],XMM0       |
00007FFEE75084A0  | 66:0F6F0424              | MOVDQA XMM0,XMMWORD PTR SS:[RSP]       |
00007FFEE75084A5  | 66:0FEF4424 10           | PXOR XMM0,XMMWORD PTR SS:[RSP+10]      |
00007FFEE75084AB  | 66:0F7F4424 20           | MOVDQA XMMWORD PTR SS:[RSP+20],XMM0    |
00007FFEE75084B1  | 48:8B4424 50             | MOV RAX,QWORD PTR SS:[RSP+50]          |
00007FFEE75084B6  | 66:0F6F4424 20           | MOVDQA XMM0,XMMWORD PTR SS:[RSP+20]    |
00007FFEE75084BC  | F3:0F7F00                | MOVDQU XMMWORD PTR DS:[RAX],XMM0       | В RAX будет находится декрипнутая строка
00007FFEE75084C0  | 48:83C4 38               | ADD RSP,38                             |
00007FFEE75084C4  | C3                       | RET                                    |
Это место будет многократно вызываться, поэтому будем перехватывать его.
И так я собрал список логов, которые анти-чит оставляет за собой:

Код:
[decrypt] CRC32 checks activated for %s
[decrypt] The CRC checks have failed!
[decrypt] Hooking thread started, parameter: %p
[decrypt] MinHook initialized! %d
[decrypt] Increasing working memory size to %p!
[decrypt] Waiting on %s...
[decrypt] A command has been received from the client "%s"
[decrypt] Thread Start Address
[decrypt] Hook "%s" placed on %s!
[decrypt] Hook "%s" couldn't be placed on %s
[decrypt] An uninitialized hook was activated.
[decrypt] Failed to activate the "%s" hook!
[decrypt] Managed to gather the address to il2cpp::find_game_objects in %p ms (%p tries)
[decrypt] Protected launcher thread by ID %d
[decrypt] SL-AC heartbeat tick!
[decrypt] Status nominal.
[decrypt] SL-AC thread protection tick!
[decrypt] Running integrity check on watched addresses...
[decrypt] Return Address: Thread RIP Check
[decrypt] The return address at %s was invalid.
[decrypt] A SL-AC thread has stopped execution inadvertently!
[decrypt] Window check executed!
[decrypt] Process check executed!
[decrypt] Blacklisted program detected!
[decrypt] Pattern scan executed!
[decrypt] Developer Mode Enabled!
[decrypt] Scanning mapped memory region: %p - %p
[decrypt] Scanning module memory region: %p - %p
[decrypt] Increasing working memory size to %p!
[decrypt] Watching address ID %d
[decrypt] Cheat Engine
[decrypt] MIDKNIGHT LLC
[decrypt] ReShade
- СВЯЗЬ С СЕРВЕРОМ

В коммуникации клиент/сервер разработчики отдают предпочтение open-source библиотекам cURL и Crypto++. Ссылка на репозитории: github.com/curl/curl; github.com/weidai11/cryptopp
Абсолютно все отправляемые пакеты от анти-чита серверу и получаемые ответы от сервера поступают через функции этих библиотек.

Я не буду описывать подробно их связь, опишу что собирает анти-чит для последующего шифрования и отправки на сервер.

Первым делом анти-чит начинает логгировать те же самые действия, что были до этого, но при этом заворачивает это в json, шифрует и отправляет серверу.

Дешифрованный пакет выглядит следующем образом:

Код:
"{\"detection_information\":\"Status nominal.\",\"detection_status\":0,\"game_files\":[{\"name\":\"appdata.bat\"},{\"name\":\"ConfigTemplates\"},{\"name\":\"CreditsCache.json\"},{\"name\":\"GameAssembly.dll\"},{\"name\":\"license.txt\"},{\"name\":\"log.bat\"},{\"name\":\"log.txt\"},{\"name\":\"mono.msi\"},{\"name\":\"monoinstall.vdf\"},{\"name\":\"readme.txt\"},{\"name\":\"scl-sl.log\"},{\"name\":\"SCPSL.exe\"},{\"name\":\"SCPSL_Data\"},{\"name\":\"SL-AC.dll\"},{\"name\":\"Translations\"},{\"name\":\"Un"
Код:
"{\"detection_information\":\"Initial heartbeating.\",\"detection_status\":0,\"game_files\":[{\"name\":\"appdata.bat\"},{\"name\":\"ConfigTemplates\"},{\"name\":\"CreditsCache.json\"},{\"name\":\"GameAssembly.dll\"},{\"name\":\"license.txt\"},{\"name\":\"log.bat\"},{\"name\":\"log.txt\"},{\"name\":\"mono.msi\"},{\"name\":\"monoinstall.vdf\"},{\"name\":\"readme.txt\"},{\"name\":\"scl-sl.log\"},{\"name\":\"SCPSL.exe\"},{\"name\":\"SCPSL_Data\"},{\"name\":\"SL-AC.dll\"},{\"name\":\"Translations\"},{\"name"
Затем при присоединении к какому-нибудь серверу анти-чит отсылает список загруженных модулей, их размер и базовый адрес

Код:
"\",\"loaded_modules\":[{\"module_base\":140696840306688,\"module_name\":\"C:\\\\Program Files (x86)\\\\Steam\\\\steamapps\\\\common\\\\SCP Secret Laboratory\\\\SCPSL.exe\",\"module_size\":9986048},{\"module_base\":140714089709568,\"module_name\":\""
Не обошлось и без информации о потоках, модуль проходится по всем запущенным раннее потокам, собирая их айди, стартовый адрес, текущий адрес rip и адрес стек поинтера.

Код:
",\"thread_start_address\":140712428775056},{\"thread_id\":1036,\"thread_module_start_address\":140712420179968,\"thread_rip\":140712447511423,\"thread_rsp\":140712447511423,\"thread_start_address\":140712428775056},{\"thread_id\":2252,\"thread_module_start_address\":140712420179968,\"thread_rip\":140712447511423,\"thread_rsp\":140712447511423,\"thread_start_address\":140712428775056},{\"thread_id\":8996,\"thread_module_start_address\":140712420179968,\"thread_rip\":140712447511423,\"thread_rsp\":140712"
Но все эти адреса анти-чит записывает в виде десятичных чисел, если перевести в hex, то получим к примеру

module_base: 140696840306688 -> 00007FF689300000. Базовый адрес SCP:SL
module_size: 9986048 -> 0000000000986000

Также анти-чит после инициализации heartbeating будет неоднократно обращаться к URL
Пожалуйста, авторизуйтесь для просмотра ссылки.
и вызывать GetTickCount64, можете не переходить, потому что провалите проверку на User-Agent и получите 405 ошибку. :roflanEbalo:

Заключение

Первым делом попрошу подписаться на мой бложик:
Пожалуйста, авторизуйтесь для просмотра ссылки.


Благодарю за прочтение данной статьи, я всё ленился делать её, постоянно отвлекался на что-то незначительное и просто ленился, но в один момент мотивация пришла сама ко мне, и так я за пару дней собрал достаточно материала для того, чтобы поведать вам :D
Не прекращу благодарить свою команду Team Enterial: Arting, anarh1st47, Dark_Bull, easton, nelfo57

Надеюсь статья вам понравилась и вы узнали что-то новое для себя!

Блог моей команды:
Пожалуйста, авторизуйтесь для просмотра ссылки.
 
Последнее редактирование:
Virus analysis
Забаненный
Статус
Оффлайн
Регистрация
24 Июн 2022
Сообщения
113
Реакции[?]
54
Поинты[?]
0
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Летний шалом! Целых 4 месяца я не выпускал контент, связанный с анализом семплов и вот пришло время исправляться. Люди, подписанные на меня знают причину долгого отсутствия, если кому интересно, почитать можно здесь:
Пожалуйста, авторизуйтесь для просмотра ссылки.


- ПРЕДИСЛОВИЕ

Исходя из мнений моих коллег по ЦЕХу было принято решение проанализировать анти-чит из игры SCP: Secret Laboratory
В этой статье будет представлен анализ механизмов SCP Anti-Cheat, которые предназначены для предотвращения манипуляции с памятью игры.

По традиции, которую я позаимствовал у Arting, в начале статьи кратко опишу какими утилитами я пользовался во время анализа анти-чита:

Мой основной инструмент для проведения динамического анализа - x64dbg
Изредка, но всё же если и провожу статический анализ, то делаю это через IDA Pro 7.7.220118

Так же список плагинов для отладчика, которые я использовал:
- SharpOD — Анти-анти-дебаг плагин, который как по мне в некоторых моментах эффективнее ScyllaHide против протекторов (VMProtect, Themida).​
- x64dbgpy — для выполнения скриптов написанных на языке Python, использующие SDK отладчика.​

- Scylla (x64) — утилита для реконструктции импортов.
- Visual Studio 2019 — для написания своей DLL, которая выполняет все необходимые хуки и патчи.

Переходим непосредственно к самому анализу :)

- ЛАУНЧЕР И МОДУЛЬ В ОБЩЕМ ПЛАНЕ

Оба таргета имеют х64 архитектуру и накрыты протектором Themida, по всем канонам современной защиты имеют фулл-пресет защиты + разработчики во время валидации они будут использовать функцию из SDK Фемиды для проверки целостности модуля - SECheckCodeIntegrity.

Игра у нас на движке Unity, но почти вся защита находится в модуле SL-AC, который полностью написан на нативном С++ без всяких намёков на CLR-сборку.

- МОДУЛЬ АНТИ-ЧИТА

- Механизм обнаружения отладчика от Фемиды
Фемида палила мой отладчик даже после того как я перехукал всю таблицу системных вызовов, понятное дело, что системные вызовы Фемиды никто не отменял, но тем не менее само обнаружение отладчика состояло в том, чтобы обнаруживать название окна, я переименовал свой отладчик и на удивление протектор перестал выбивать сообщения о детектах :kappa:


- Инициализация
Я обратил внимание на то, что анти-чит тоскает за собой библиотеку MinHook, понял я это по Enum, которую оставляет за собой библиотека.

Код:
00007FFE3927DC0C  | 48:8D05 ED131200         | LEA RAX,QWORD PTR DS:[7FFE3939F000]         | 00007FFE3939F000:"MH_UNKNOWN"
00007FFE3927DC13  | EB 7C                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC15  | 48:8D05 F0131200         | LEA RAX,QWORD PTR DS:[7FFE3939F00C]         | 00007FFE3939F00C:"MH_OK"
00007FFE3927DC1C  | EB 73                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC1E  | 48:8D05 F3131200         | LEA RAX,QWORD PTR DS:[7FFE3939F018]         | 00007FFE3939F018:"MH_ERROR_ALREADY_INITIALIZED"
00007FFE3927DC25  | EB 6A                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC27  | 48:8D05 0A141200         | LEA RAX,QWORD PTR DS:[7FFE3939F038]         | 00007FFE3939F038:"MH_ERROR_NOT_INITIALIZED"
00007FFE3927DC2E  | EB 61                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC30  | 48:8D05 21141200         | LEA RAX,QWORD PTR DS:[7FFE3939F058]         | 00007FFE3939F058:"MH_ERROR_ALREADY_CREATED"
00007FFE3927DC37  | EB 58                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC39  | 48:8D05 38141200         | LEA RAX,QWORD PTR DS:[7FFE3939F078]         | 00007FFE3939F078:"MH_ERROR_NOT_CREATED"
00007FFE3927DC40  | EB 4F                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC42  | 48:8D05 47141200         | LEA RAX,QWORD PTR DS:[7FFE3939F090]         | 00007FFE3939F090:"MH_ERROR_ENABLED"
00007FFE3927DC49  | EB 46                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC4B  | 48:8D05 56141200         | LEA RAX,QWORD PTR DS:[7FFE3939F0A8]         | 00007FFE3939F0A8:"MH_ERROR_DISABLED"
00007FFE3927DC52  | EB 3D                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC54  | 48:8D05 65141200         | LEA RAX,QWORD PTR DS:[7FFE3939F0C0]         | 00007FFE3939F0C0:"MH_ERROR_NOT_EXECUTABLE"
00007FFE3927DC5B  | EB 34                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC5D  | 48:8D05 74141200         | LEA RAX,QWORD PTR DS:[7FFE3939F0D8]         | 00007FFE3939F0D8:"MH_ERROR_UNSUPPORTED_FUNCTION"
00007FFE3927DC64  | EB 2B                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC66  | 48:8D05 8B141200         | LEA RAX,QWORD PTR DS:[7FFE3939F0F8]         | 00007FFE3939F0F8:"MH_ERROR_MEMORY_ALLOC"
00007FFE3927DC6D  | EB 22                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC6F  | 48:8D05 9A141200         | LEA RAX,QWORD PTR DS:[7FFE3939F110]         | 00007FFE3939F110:"MH_ERROR_MEMORY_PROTECT"
00007FFE3927DC76  | EB 19                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC78  | 48:8D05 A9141200         | LEA RAX,QWORD PTR DS:[7FFE3939F128]         | 00007FFE3939F128:"MH_ERROR_MODULE_NOT_FOUND"
00007FFE3927DC7F  | EB 10                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC81  | 48:8D05 C0141200         | LEA RAX,QWORD PTR DS:[7FFE3939F148]         | 00007FFE3939F148:"MH_ERROR_FUNCTION_NOT_FOUND"
00007FFE3927DC88  | EB 07                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC8A  | 48:8D05 D7141200         | LEA RAX,QWORD PTR DS:[7FFE3939F168]         | 00007FFE3939F168:"(unknown)"
00007FFE3927DC91  | 48:83C4 10               | ADD RSP,10                                  |
Функция DllMain не виртуализирована Фемидой, это даёт нам возможность найти его по паттерну и поставить бряк на него.

Посмотреть вложение 215958

Во время своей инициализации анти-чит вызывает виртуализированный CreateThread, этот поток считается главным, и первым что он сделает, так это установит свои хуки.

Посмотреть вложение 215959

Рассмотрим список расставляемых хуков анти-читом (и чуть-чуть темидой)
Список хуков:

Код:
Импортируемая библиотека: GameAssembly.dll, экспортируемая функция: il2cpp_resolve_icall
Импортируемая библиотека: kernel32.dll, экспортируемая функция: GetModuleHandleW
Импортируемая библиотека: kernel32.dll, экспортируемая функция: GetProcAddress
Импортируемая библиотека: kernel32.dll, экспортируемая функция: LoadLibraryA
Импортируемая библиотека: kernel32.dll, экспортируемая функция: LoadLibraryW
Импортируемая библиотека: user32.dll, экспортируемая функция: GetAsyncKetState
Импортируемая библиотека: ntdll.dll, экспортируемая функция: DbgUiRemoteBreakin (Поставлен Фемидой, чтобы при попытке присоединиться к процессу кидало на LdrShutdownProcess :kappa:)
Импортируемая библиотека: ntdll.dll, экспортируемая функция: NtOpenFile
- Блокировка инжекта (Хук NtOpenFile)
Он устанавливается сразу же после установки хука на GetProcAddress.

В декомпилированном hkNtOpenFile прослеживаются вызовы конструктора std::wstring, что может говорить о том, что скорее всего конструктор в дальнейшем будет использоваться для работы со структурой POBJECT_ATTRIBUTES.

Чтобы получать имя объекта хук читает сразу два указателя на структуры: POBJECT_ATTRIBUTES и PUNICODE_STRING
Чтение происходит таким образом: pObjectAttributes->pObjectName->Buffer.

Посмотреть вложение 215960

В последующем анти-чит будет проверять находится ли бинарник в папке Windows и имеет при себе разные проверки, связанные с сертификатом.



Если же файла не было в Windows, то в связке с вызовами CryptQueryObject, CertGetNameStringW, CertFindCertificateInStore, он начинает проверять издателя цифровой подписи и получать имя эмитента.





Пройдя дальше я попал на завиртуализированную функцию, которую я назвал IsBlacklistedCert.
Функция внутри виртуальной машины темиды сравнивает полученный сертификат с запрещенными сертификатами, который хранится также под виртуализацией.

Под проверку попали такие сертификаты, как: ReShade, MIDKNIGHT LLC и Cheat Engine, если что-то из них он обнаруживал у файла, то функция возвращала TRUE.



Под конец всей проверки он делает для себя лог: [Windows Folder - %s]; [Signing: %s - %s]

- ОБХОДИМ БЛОКИРОВКУ ИНЖЕКТА

Есть много способов обмануть анти-чит и дать заинжектиться своей длл, начиная от инжекта до того как анти-чит установит свои хуки и заканчивая тем, что в наглую можно блокировать хук анти-чита. Мой способ будет связан с перехватом функции для расстановки хуков анти-чита.

Так как функции либы с хуками не находится под виртой, то нам ничего не мешает найти MH_EnableHook и MH_CreateHook по паттернам.

Для нахождения этих функции я составил два паттерна:

Код:
4C 24 08 57 48 81 EC 90 00 00 00 48 8B FC B9 24 00 00 00 B8 CC CC CC CC F3 AB 48 8B 8C 24 A0 00 00 00 - MH_CreateHook
48 89 4C 24 08 57 48 83 EC 20 48 8B FC B9 08 00 00 - MH_EnableHook
И вуаля! Наши функции для перехватывания были найдены





Помимо того, что с помощью перехвата MH_EnableHook я буду блокировать активацию hkNtOpenFile, возвращая MH_OK параллельно я мониторил какие хуки он ещё расставлял, и в своей консоли оставлял адреса детуров анти-чита.



Конечно же эти проверки на подлинность файла не являются эффективными, можно пропустить эти проверки одним патчем инструкции JNE, или же поставить точку останова на CloseHandle, потому что именно он отвечает за блокировку дескриптора. Никакого сисколла NtClose, просто обыкновенный CloseHandle.

А вот и мемный обход блокировки инжекта

Недавно разработчики обновляли анти-чит, из-за этой обновы все мои комментарии и метки на функции пошли по пизде, и они виртуализировали пару функции и ещё некоторый пользовательский код, CloseHandle был в их числе. :roflanEbalo:



- Хук GetAsyncKeyState
Этот хук передает анти-читу информацию о адресе возврата. А также в хуке вызывается RtlPcToFileHeader для чтения PE хэдера у файла, с которого была вызвана эта функция, если у файла не оказалось первых двух байтов - 0x5A4D, то для анти-чита это первый звоночек зарепортить пользователя.

Всё это примерно выглядит таким образом:

C++:
PVOID imageBase;

RtlPcToFileHeader( _ReturnAddress( ), &imageBase );

if ( *( WORD * ) imageBase != 0x5A4D )
     // Hidden module
else
    // OK
- Обнаружение по окнам/процессу наше всё!
На дворе уже прошла половина 2022 года, и к сожалению если вы ждали здесь гениальные механизмы обнаружения отладчика, то забудьте про это, потому что и Фемида и разработчики Northwood Studios решили использовать одинаковые техники обнаружения. Чтобы обнаруживать нежелательные процессы такие как: OllyDbg, Cheat Engine, Process Hacker, Process Monitor Фемида использует FindWindowA, а анти-чит в свою очередь использует два механизма: Обнаружение окна и обнаружение процесса.

- Обнаружение виртуальной машины
Опять же, если вы думали, что анти-чит будет использовать методы обхода песочницы/вм, основанные на времени, то вы сильно заблуждались.
В совокупности с обнаружением нежелательных процессов второй механизм использует в своём списке и системные процессы VMWare. Всё это реализовано в булевой функции, если функция вернула - 1, значит он обнаружил что-то из разряда VMWare и выбил ошибку. Патчится это двумя инструкциями "xor eax, eax; ret;"



Ну и сам мем



- Второй механизм обнаружения
Второй метод заключается в злоупотреблении функции EnumWindows, если быть точнее, то его каллбеком, который выступает первым параметром. (
Пожалуйста, авторизуйтесь для просмотра ссылки.
)

Эта функция будет вызываться частенько, поэтому перехватываем её и возвращаем FALSE, потому что анти-чит не проверяет возвращаемое значение функции.

Получаем последнее китайское предупреждение от анти-чита и пропускаем его со спокойной душой, потому что игра продолжит загрузку в главное меню.



Эту технику опрометчиво использовать в своих механизмах обнаружений, только если на ваш продукт не покушаются реверс-инженеры, не имеющие опыта в динамическом анализе.

- Техника Heaven's Gate
Да-да, анти-чит под х64, но тем не менее используют данную технику, и по всей видимости для предотвращения трассировки, потому что как мы все знаем xdbg не умеет проходить сквозь инструкцию ret far, как тот же Cheat Engine или WinDbg.

Эти функции проверяют наличие дебаг флагов и дебаг порта у текущего процесса

Код:
00007FF907136CB1  | 6A 33                    | PUSH 33                                                 | 0x33 - Доступ к х64 сегменту кода, 0x23 - к х32
00007FF907136CB3  | E8 00000000              | CALL sl-ac.7FF907136CB8                                 | call $0
00007FF907136CB8  | 830424 05                | ADD DWORD PTR SS:[RSP],5                                |
00007FF907136CBC  | CB                       | RET FAR                                                 |
00007FF907136CBD  | 49:89D8                  | MOV R8,RBX                                              |
00007FF907136CC0  | 48:89E3                  | MOV RBX,RSP                                             |
00007FF907136CC3  | FF3424                   | PUSH QWORD PTR SS:[RSP]                                 |
00007FF907136CC6  | FF3424                   | PUSH QWORD PTR SS:[RSP]                                 |
00007FF907136CC9  | 40:80E4 F0               | AND SPL,F0                                              | Выравнивание стека 16 байт
00007FF907136CCD  | 48:83C4 10               | ADD RSP,10                                              |
00007FF907136CD1  | 48:83EC 30               | SUB RSP,30                                              |
00007FF907136CD5  | 48:C7C1 FFFFFFFF         | MOV RCX,FFFFFFFFFFFFFFFF                                | Текущий процесс
00007FF907136CDC  | 48:C7C2 1E000000         | MOV RDX,1E                                              | Process Debug Port
00007FF907136CE3  | 49:C7C1 08000000         | MOV R9,8                                                |
00007FF907136CEA  | 48:C74424 20 00000000    | MOV QWORD PTR SS:[RSP+20],0                             |
00007FF907136CF3  | FFD6                     | CALL RSI                                                | Вызываем системный вызов
00007FF907136CF5  | 48:83C4 30               | ADD RSP,30                                              |
00007FF907136CF9  | 48:89DC                  | MOV RSP,RBX                                             |
00007FF907136CFC  | E8 00000000              | CALL sl-ac.7FF907136D01                                 | call $0
00007FF907136D01  | C74424 04 23000000       | MOV DWORD PTR SS:[RSP+4],23                             |
00007FF907136D09  | 830424 0D                | ADD DWORD PTR SS:[RSP],D                                |
00007FF907136D0D  | CB                       | RET FAR                                                 |
Код:
00007FF90721501C  | E8 00000000              | CALL sl-ac.7FF907215021                                 | call $0
00007FF907215021  | 830424 05                | ADD DWORD PTR SS:[RSP],5                                |
00007FF907215025  | CB                       | RET FAR                                                 |
00007FF907215026  | 49:89D8                  | MOV R8,RBX                                              |
00007FF907215029  | 48:89E3                  | MOV RBX,RSP                                             |
00007FF90721502C  | FF3424                   | PUSH QWORD PTR SS:[RSP]                                 |
00007FF90721502F  | FF3424                   | PUSH QWORD PTR SS:[RSP]                                 |
00007FF907215032  | 40:80E4 F0               | AND SPL,F0                                              | Выравнивание стека 16 байт
00007FF907215036  | 48:83C4 10               | ADD RSP,10                                              |
00007FF90721503A  | 48:83EC 30               | SUB RSP,30                                              |
00007FF90721503E  | 48:C7C1 FFFFFFFF         | MOV RCX,FFFFFFFFFFFFFFFF                                |
00007FF907215045  | 48:C7C2 07000000         | MOV RDX,7                                               | Process Debug Flags
00007FF90721504C  | 49:C7C1 08000000         | MOV R9,8                                                |
00007FF907215053  | 48:C74424 20 00000000    | MOV QWORD PTR SS:[RSP+20],0                             |
00007FF90721505C  | FFD6                     | CALL RSI                                                |
00007FF90721505E  | 48:83C4 30               | ADD RSP,30                                              |
00007FF907215062  | 48:89DC                  | MOV RSP,RBX                                             |
00007FF907215065  | E8 00000000              | CALL sl-ac.7FF90721506A                                 | call $0
00007FF90721506A  | C74424 04 23000000       | MOV DWORD PTR SS:[RSP+4],23                             |
00007FF907215072  | 830424 0D                | ADD DWORD PTR SS:[RSP],D                                |
00007FF907215076  | CB                       | RET FAR                                                 |
Что ещё меня поразило, так это их попытка завернуть данную технику под виртуальную машину Фемиды, потому что это эта функция находилась в сегменте Фемиды, где выполняется ВМ. Видимо, Nortwood Studios не вкурсе, что протекторы не могут закинуть технику Heaven's Gate как раз из-за инструкции RET FAR. Поэтому такой чистый код валяется в сегменте среди виртуализированного кода.

- Сканирование памяти
На этом этапе защиты анти-чит с помощью GetStartUpInfo и VirtualQuery проходится по регионам памяти

Код:
00007FFEF2E19B70  | 48:895424 10             | MOV QWORD PTR SS:[RSP+10],RDX          | Memory Scan
00007FFEF2E19B75  | 48:894C24 08             | MOV QWORD PTR SS:[RSP+8],RCX           |
00007FFEF2E19B7A  | 48:81EC 88000000         | SUB RSP,88                             |
00007FFEF2E19B81  | 48:8B05 985A1300         | MOV RAX,QWORD PTR DS:[7FFEF2F4F620]    |
00007FFEF2E19B88  | 48:33C4                  | XOR RAX,RSP                            |
00007FFEF2E19B8B  | 48:894424 78             | MOV QWORD PTR SS:[RSP+78],RAX          |
00007FFEF2E19B90  | 0FB605 C1E21300          | MOVZX EAX,BYTE PTR DS:[7FFEF2F57E58]   |
00007FFEF2E19B97  | 85C0                     | TEST EAX,EAX                           |
00007FFEF2E19B99  | 75 0D                    | JNE sl-ac.7FFEF2E19BA8                 |
00007FFEF2E19B9B  | 48:8D0D 7EE41300         | LEA RCX,QWORD PTR DS:[7FFEF2F58020]    |
00007FFEF2E19BA2  | FF15 58350F00            | CALL QWORD PTR DS:[7FFEF2F0D100]       | Virtualized GetSystemInfo
00007FFEF2E19BA8  | 48:8B05 79E41300         | MOV RAX,QWORD PTR DS:[7FFEF2F58028]    |
00007FFEF2E19BAF  | 48:894424 20             | MOV QWORD PTR SS:[RSP+20],RAX          |
00007FFEF2E19BB4  | 48:8B05 75E41300         | MOV RAX,QWORD PTR DS:[7FFEF2F58030]    |
00007FFEF2E19BBB  | 48:894424 28             | MOV QWORD PTR SS:[RSP+28],RAX          |
00007FFEF2E19BC0  | 48:8B4424 28             | MOV RAX,QWORD PTR SS:[RSP+28]          |
00007FFEF2E19BC5  | 48:394424 20             | CMP QWORD PTR SS:[RSP+20],RAX          |
00007FFEF2E19BCA  | 0F83 83000000            | JAE sl-ac.7FFEF2E19C53                 |
00007FFEF2E19BD0  | 41:B8 30000000           | MOV R8D,30                             |
00007FFEF2E19BD6  | 48:8D5424 48             | LEA RDX,QWORD PTR SS:[RSP+48]          |
00007FFEF2E19BDB  | 48:8B4C24 20             | MOV RCX,QWORD PTR SS:[RSP+20]          |
00007FFEF2E19BE0  | FF15 2A350F00            | CALL QWORD PTR DS:[7FFEF2F0D110]       | Virtualized VirtualQuery
00007FFEF2E19BE6  | 8B4424 68                | MOV EAX,DWORD PTR SS:[RSP+68]          | Полученный размер региона памяти
00007FFEF2E19BEA  | 25 00100000              | AND EAX,1000                           |
00007FFEF2E19BEF  | 3D 00100000              | CMP EAX,1000                           | Если после (regionSize & 0x1000) не дал в результате 0x1000, то скип
00007FFEF2E19BF4  | 75 43                    | JNE sl-ac.7FFEF2E19C39                 |
00007FFEF2E19BF6  | 837C24 6C 20             | CMP DWORD PTR SS:[RSP+6C],20           | Проверка на PAGE_EXECUTE_READ
00007FFEF2E19BFB  | 74 07                    | JE sl-ac.7FFEF2E19C04                  |
00007FFEF2E19BFD  | 837C24 6C 40             | CMP DWORD PTR SS:[RSP+6C],40           | Проверка на PAGE_EXECUTE_READWRITE
00007FFEF2E19C02  | 75 35                    | JNE sl-ac.7FFEF2E19C39                 |
00007FFEF2E19C04  | 48:8B4424 48             | MOV RAX,QWORD PTR SS:[RSP+48]          |
00007FFEF2E19C09  | 48:894424 30             | MOV QWORD PTR SS:[RSP+30],RAX          |
00007FFEF2E19C0E  | 48:8B4424 60             | MOV RAX,QWORD PTR SS:[RSP+60]          |
00007FFEF2E19C13  | 48:894424 38             | MOV QWORD PTR SS:[RSP+38],RAX          |
00007FFEF2E19C18  | 48:8B4424 30             | MOV RAX,QWORD PTR SS:[RSP+30]          |
00007FFEF2E19C1D  | 48:894424 40             | MOV QWORD PTR SS:[RSP+40],RAX          |
00007FFEF2E19C22  | 4C:8B4424 38             | MOV R8,QWORD PTR SS:[RSP+38]           |
00007FFEF2E19C27  | 48:8B5424 40             | MOV RDX,QWORD PTR SS:[RSP+40]          |
00007FFEF2E19C2C  | 48:8B8C24 98000000       | MOV RCX,QWORD PTR SS:[RSP+98]          |
00007FFEF2E19C34  | E8 17040000              | CALL sl-ac.7FFEF2E1A050                |
00007FFEF2E19C39  | 48:8B4424 60             | MOV RAX,QWORD PTR SS:[RSP+60]          |
00007FFEF2E19C3E  | 48:8B4C24 20             | MOV RCX,QWORD PTR SS:[RSP+20]          |
00007FFEF2E19C43  | 48:03C8                  | ADD RCX,RAX                            |
00007FFEF2E19C46  | 48:8BC1                  | MOV RAX,RCX                            |
00007FFEF2E19C49  | 48:894424 20             | MOV QWORD PTR SS:[RSP+20],RAX          |
00007FFEF2E19C4E  | E9 6DFFFFFF              | JMP sl-ac.7FFEF2E19BC0                 |
00007FFEF2E19C53  | 48:8B8C24 98000000       | MOV RCX,QWORD PTR SS:[RSP+98]          |
00007FFEF2E19C5B  | E8 800AFAFF              | CALL sl-ac.7FFEF2DBA6E0                |
00007FFEF2E19C60  | 48:8B4C24 78             | MOV RCX,QWORD PTR SS:[RSP+78]          |
00007FFEF2E19C65  | 48:33CC                  | XOR RCX,RSP                            |
00007FFEF2E19C68  | E8 B3160B00              | CALL sl-ac.7FFEF2ECB320                |
00007FFEF2E19C6D  | 48:81C4 88000000         | ADD RSP,88                             |
00007FFEF2E19C74  | C3                       | RET                                    |
В декомпилированном виде:



- Сканирование паттернов читов в памяти

Помимо того что анти-чит сканирует всю память, он также успевает в полученных регионах памяти проверять список паттернов, которые он собирал у других читов.

Код:
"\x41\x69\x6D\x62\x6F\x74\x20\x54\x61\x72\x67\x65\x74" - Aimbot Target
"\x53\x63\x69\x65\x6E\x74\x69\x73\x74\x20\x4B\x65\x79\x63\x61\x72\x64" - Scientist Keycard
"\x70\x65\x64\x6F\x70\x68\x69\x6C\x65\x67\x61\x6D\x69\x6E\x67\x2E\x63\x63" - pedophilegaming.cc
"\x6B\x69\x74\x65\x68\x34" - kiteh4x
"\x3C\x63\x6F\x6C\x6F\x72\x3D\x77\x68\x69\x74\x65\x3E\x70\x6C\x61\x79\x65\x72\x73\x3A\x20\x25\x69\x3C\x2F\x63\x6F\x6C\x6F\x72\x3E" - <color=white>players: %i</color>
"\x3C\x63\x6F\x6C\x6F\x72\x3D\x25\x73\x3E\x25\x73\x20\x2D\x20\x25\x2E\x32\x66\x3C\x2F\x63\x6F\x6C\x6F\x72\x3E"- <color=%s>%s - %.2f</color>
"\x23\x23\x4D\x61\x69\x6E\x4D\x65\x6E\x75\x42\x61\x72" - ##MainMenuBar
- Связь с защищаемым потоком

Для того чтобы поддерживать защищаемый поток анти-чит создаёт 1 поток. Анти-чит берёт айди потока и будет всячески проверять его через некоторое время, используя Sleep.

Проверка на то, работает ли защищаемый поток:

Код:
00007FFE2C77B3D9  | 48:8B4424 28             | MOV RAX,QWORD PTR SS:[RSP+28]               |
00007FFE2C77B3DE  | 48:83C0 08               | ADD RAX,8                                   |
00007FFE2C77B3E2  | 48:894424 28             | MOV QWORD PTR SS:[RSP+28],RAX               |
00007FFE2C77B3E7  | 48:8B4424 38             | MOV RAX,QWORD PTR SS:[RSP+38]               |
00007FFE2C77B3EC  | 48:394424 28             | CMP QWORD PTR SS:[RSP+28],RAX               |
00007FFE2C77B3F1  | 0F84 A4000000            | JE sl-ac.7FFE2C77B49B                       |
00007FFE2C77B3F7  | 48:8B4424 28             | MOV RAX,QWORD PTR SS:[RSP+28]               |
00007FFE2C77B3FC  | 48:894424 40             | MOV QWORD PTR SS:[RSP+40],RAX               |
00007FFE2C77B401  | 48:8D0D 48CA1300         | LEA RCX,QWORD PTR DS:[7FFE2C8B7E50]         |
00007FFE2C77B408  | E8 F384F9FF              | CALL sl-ac.7FFE2C713900                     |
00007FFE3259B40D  | 48:894424 50             | MOV QWORD PTR SS:[RSP+50],RAX               |
00007FFE3259B412  | 48:8B4424 40             | MOV RAX,QWORD PTR SS:[RSP+40]               |
00007FFE3259B417  | 48:8B00                  | MOV RAX,QWORD PTR DS:[RAX]                  |
00007FFE3259B41A  | 48:894424 48             | MOV QWORD PTR SS:[RSP+48],RAX               |
00007FFE3259B41F  | 48:8B5424 48             | MOV RDX,QWORD PTR SS:[RSP+48]               |
00007FFE3259B424  | 48:8B4C24 50             | MOV RCX,QWORD PTR SS:[RSP+50]               |
00007FFE3259B429  | E8 72BD0000              | CALL sl-ac.7FFE325A71A0                     | Проверяем не откинулся ли поток
00007FFE3259B42E  | 0FB6C0                   | MOVZX EAX,AL                                |
00007FFE3259B431  | 85C0                     | TEST EAX,EAX                                |
00007FFE3259B433  | 75 56                    | JNE sl-ac.7FFE3259B48B                      |
00007FFE3259B435  | 48:8D4424 20             | LEA RAX,QWORD PTR SS:[RSP+20]               |
00007FFE3259B43A  | 48:8BF8                  | MOV RDI,RAX                                 |
00007FFE3259B43D  | 33C0                     | XOR EAX,EAX                                 |
00007FFE3259B43F  | B9 01000000              | MOV ECX,1                                   |
00007FFE3259B444  | F3:AA                    | REP STOSB                                   |
00007FFE3259B446  | 48:8D4424 21             | LEA RAX,QWORD PTR SS:[RSP+21]               |
00007FFE3259B44B  | 48:8BF8                  | MOV RDI,RAX                                 |
00007FFE3259B44E  | 33C0                     | XOR EAX,EAX                                 |
00007FFE3259B450  | B9 01000000              | MOV ECX,1                                   |
00007FFE3259B455  | F3:AA                    | REP STOSB                                   |
00007FFE3259B457  | 48:8D4424 22             | LEA RAX,QWORD PTR SS:[RSP+22]               |
00007FFE3259B45C  | 48:8BF8                  | MOV RDI,RAX                                 |
00007FFE3259B45F  | 33C0                     | XOR EAX,EAX                                 |
00007FFE3259B461  | B9 01000000              | MOV ECX,1                                   |
00007FFE3259B466  | F3:AA                    | REP STOSB                                   |
00007FFE3259B468  | 44:0FB64C24 20           | MOVZX R9D,BYTE PTR SS:[RSP+20]              |
00007FFE3259B46E  | 44:0FB64424 21           | MOVZX R8D,BYTE PTR SS:[RSP+21]              |
00007FFE3259B474  | 0FB65424 22              | MOVZX EDX,BYTE PTR SS:[RSP+22]              |
00007FFE3259B479  | 48:8D4C24 60             | LEA RCX,QWORD PTR SS:[RSP+60]               |
00007FFE3259B47E  | E8 4D000000              | CALL sl-ac.7FFE3259B4D0                     |
00007FFE3259B483  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE3259B486  | E8 55040000              | CALL sl-ac.7FFE3259B8E0                     | Лог "A SL-AC thread has stopped execution inadvertently!"
00007FFE3259B48B  | B9 0F000000              | MOV ECX,F                                   |
00007FFE3259B490  | FF15 8A1D0F00            | CALL QWORD PTR DS:[<&Sleep>]                |
00007FFE3259B496  | E9 3EFFFFFF              | JMP sl-ac.7FFE3259B3D9                      | Возвращаемся на JE проверку
00007FFE3259B49B  | 48:8D8C24 A0000000       | LEA RCX,QWORD PTR SS:[RSP+A0]               |
00007FFE3259B4A3  | E8 7880FCFF              | CALL sl-ac.7FFE32563520                     |
00007FFE3259B4A8  | 48:8B8C24 A8000000       | MOV RCX,QWORD PTR SS:[RSP+A8]               |
00007FFE3259B4B0  | 48:33CC                  | XOR RCX,RSP                                 |
00007FFE3259B4B3  | E8 68FE0A00              | CALL sl-ac.7FFE3264B320                     |
00007FFE3259B4B8  | 48:81C4 B0000000         | ADD RSP,B0                                  |
00007FFE3259B4BF  | 5F                       | POP RDI                                     |
00007FFE3259B4C0  | C3                       | RET                                         |
- Что же будет, если поток будет заморожен? Ничего.
- Что будет, если поток будет закрыть? Анти-чит начнёт спамить логами, что потока больше нет.

- Защищаемый поток

В защищаемом потоке нет ничего необычного, вначале вызывается GetTickCount64, затем сразу же вызываются две функции по несколько раз, где тоже фигурирует GetTickCount64, всё это дело выглядит следующим образом:

Код:
00007FFE2D3CD930  | 48:894C24 08             | MOV QWORD PTR SS:[RSP+8],RCX                |
00007FFE2D3CD935  | 48:83EC 28               | SUB RSP,28                                  |
00007FFE2D3CD939  | 48:8D0D D0A41300         | LEA RCX,QWORD PTR DS:[7FFE2D507E10]         |
00007FFE2D3CD940  | E8 CB9DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD945  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD948  | E8 330F0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD94D  | 48:8D0D 5CA41300         | LEA RCX,QWORD PTR DS:[7FFE2D507DB0]         |
00007FFE2D3CD954  | E8 B79DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD959  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD95C  | E8 1F0F0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD961  | 48:8D0D 98A41300         | LEA RCX,QWORD PTR DS:[7FFE2D507E00]         |
00007FFE2D3CD968  | E8 A39DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD96D  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD970  | E8 0B0F0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD975  | 48:8D0D B4A41300         | LEA RCX,QWORD PTR DS:[7FFE2D507E30]         |
00007FFE2D3CD97C  | E8 8F9DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD981  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD984  | E8 F70E0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD989  | 48:8D0D 90A41300         | LEA RCX,QWORD PTR DS:[7FFE2D507E20]         |
00007FFE2D3CD990  | E8 7B9DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD995  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD998  | E8 E30E0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD99D  | 48:8D0D 3CA41300         | LEA RCX,QWORD PTR DS:[7FFE2D507DE0]         |
00007FFE2D3CD9A4  | E8 679DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD9A9  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD9AC  | E8 CF0E0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD9B1  | EB 00                    | JMP sl-ac.7FFE2D3CD9B3                      |
00007FFE2D3CD9B3  | 48:83C4 28               | ADD RSP,28                                  |
00007FFE2D3CD9B7  | C3                       | RET                                         |
В функции по адресу 7FFE2D3CE880 постоянно будет сравнивать текущий результат вызова GetTickCount64 с результатом, который был в прошлом вызове.

- Немного о GetTickCount64

И раз уже решили затронуть тему с GetTickCount64, то сейчас я попробую объяснить метод работы этой функции и метод обхода.
Если в дебаггере взглянуть на содержимое этой функции, то она довольно простая в реализации.

Код:
00007FFE9EDD5D30  | 8B0C25 0400FE7F          | MOV ECX,DWORD PTR DS:[7FFE0004]             |
00007FFE9EDD5D37  | B8 2003FE7F              | MOV EAX,7FFE0320                            |
00007FFE9EDD5D3C  | 48:C1E1 20               | SHL RCX,20                                  |
00007FFE9EDD5D40  | 48:8B00                  | MOV RAX,QWORD PTR DS:[RAX]                  |
00007FFE9EDD5D43  | 48:C1E0 08               | SHL RAX,8                                   |
00007FFE9EDD5D47  | 48:F7E1                  | MUL RCX                                     |
00007FFE9EDD5D4A  | 48:8BC2                  | MOV RAX,RDX                                 |
00007FFE9EDD5D4D  | C3                       | RET                                         |
000000007FFE0000 - Статичный адрес структуры KUSER_SHARED_DATA (
Пожалуйста, авторизуйтесь для просмотра ссылки.
), к которому обращается функция GetTickCount64 первой инструкцией, и получает значение TickCount.HighPart
Вторая инструкция запихивает TickCount.LowPart в EAX

Примерный декомпиль функции из иды:

C++:
ULONG GetTickCount64( )
{
    return ( ( TickCount.HighPart << 32 ) * ( TickCount.LowPart << 8 ) ) >> 64;
}
Итак, мы знаем, что в KUSER_SHARED_DATA нельзя ничего записывать, потому что структура только Read Only, но мы можем перехватить GetTickCount64, код хука я показывать не буду, а опишу лишь кратко его алгоритм.

  1. Сверяем адрес возврата (GetTickCount64 помимо анти-чита используется и в системных библиотеках)
  2. Пишем пару проверок на промежуток времени, который для анти-чита будет подозрительным
  3. Возвращаем функции свою константу.

- Сбор данных об комплектующих ПК

Анти-чит собирает информацию об аппаратных устройствах ПК за счёт поступаемых запросов WMI.

Для начала модуль вызывает функцию SysAllocString с аргументом "ROOT\\CIMV2", чтобы в дальнейшем с помощью указателя на интерфейс IWbemLocator вызвать функцию ConnectServer для подключения к локальному пространству имен ROOT\CIMV2, также в ConnectServer последним параметром выступает IWbemServices **ppNamespace, этот указатель на интерфейс понадобиться для выполнения вызовов IWbemServices, для тех кто в танке - через этот интерфейс потом можно будет выполнять запрос к WMI с помощью ExecQuery, но перед этим анти-чит ещё и вызовет CoSetProxyBlanket.

Список запросов анти-чита:

SELECT * FROM Win32_BaseBoard - класс Win32_BaseBoard предоставляет информацию о материнской плате компьютера, анти-чит берёт серийный номер и строковое значение Manufacturer, в котором содержится название прозиводителя материнской платы. В моём случае после исполнения функции анти-чит в результате получит название - Gigabyte Technology Co, Ltd.

SELECT * FROM Win32_ComputerSystem - из класса Win32_ComputerSystem анти-чит берёт только строковое значение Model.

SELECT * FROM Win32_DiskDrive - класс Win32_DiskDrive, содержащий в себе информацию о имеющихся жёстких дисках, анти-чит из этого класса читает только серийный номер.

SELECT * FROM Win32_Processor - класс Win32_Processor, содержащий в себе информацию о процессоре, анти-чит читает только имя процессора.

SELECT * FROM Win32_NetworkAdapter - класс Win32_NetworkAdapter, содержащий в себе информацию о сетевом адапторе, по документации MSDN этот класс устарел, и рекомендуют использовать вместо него класс MSFT_NetAdapter. Анти-чит читает оттуда MAC-Адрес.

SELECT * FROM Win32_VideoController - класс Win32_VideoController, позволяющий получить информацию о видеокарте, из этого класса можно много каких свойств приметить для дальнейшей генерации хвида, но разработчики, к сожалению читают тут только имя видеокарты.

Я не исключаю того, что анти-чит помимо перечисленного собирает и другую информацию, однако я продемонстрировал именно те методы, которые нашёл при анализе.

- Логгирование анти-чита как произведение исскуства
После общего анализа стоит упомянуть одну примечательную вещь, которая помогла мне выявить дальнейшие действия анти-чита - это логгирование.
Перед тем как выполнить какое-то важное действие анти-чит логгирует его для самого себя, естественно, используя для этого ксор строк.
Поэтому когда я узнал об этом я начал искать дешифрование строки, нашёл я её быстро.

Сама же функция дешифрования строки выглядит следующим образом:

Код:
00007FFEE7508470  | 4C:894424 18             | MOV QWORD PTR SS:[RSP+18],R8           |
00007FFEE7508475  | 48:895424 10             | MOV QWORD PTR SS:[RSP+10],RDX          |
00007FFEE750847A  | 48:894C24 08             | MOV QWORD PTR SS:[RSP+8],RCX           |
00007FFEE750847F  | 48:83EC 38               | SUB RSP,38                             |
00007FFEE7508483  | 48:8B4424 48             | MOV RAX,QWORD PTR SS:[RSP+48]          |
00007FFEE7508488  | F3:0F6F00                | MOVDQU XMM0,XMMWORD PTR DS:[RAX]       |
00007FFEE750848C  | 66:0F7F4424 10           | MOVDQA XMMWORD PTR SS:[RSP+10],XMM0    |
00007FFEE7508492  | 48:8B4424 50             | MOV RAX,QWORD PTR SS:[RSP+50]          |
00007FFEE7508497  | F3:0F6F00                | MOVDQU XMM0,XMMWORD PTR DS:[RAX]       |
00007FFEE750849B  | 66:0F7F0424              | MOVDQA XMMWORD PTR SS:[RSP],XMM0       |
00007FFEE75084A0  | 66:0F6F0424              | MOVDQA XMM0,XMMWORD PTR SS:[RSP]       |
00007FFEE75084A5  | 66:0FEF4424 10           | PXOR XMM0,XMMWORD PTR SS:[RSP+10]      |
00007FFEE75084AB  | 66:0F7F4424 20           | MOVDQA XMMWORD PTR SS:[RSP+20],XMM0    |
00007FFEE75084B1  | 48:8B4424 50             | MOV RAX,QWORD PTR SS:[RSP+50]          |
00007FFEE75084B6  | 66:0F6F4424 20           | MOVDQA XMM0,XMMWORD PTR SS:[RSP+20]    |
00007FFEE75084BC  | F3:0F7F00                | MOVDQU XMMWORD PTR DS:[RAX],XMM0       | В RAX будет находится декрипнутая строка
00007FFEE75084C0  | 48:83C4 38               | ADD RSP,38                             |
00007FFEE75084C4  | C3                       | RET                                    |
Это место будет многократно вызываться, поэтому будем перехватывать его.
И так я собрал список логов, которые анти-чит оставляет за собой:

Код:
[decrypt] CRC32 checks activated for %s
[decrypt] The CRC checks have failed!
[decrypt] Hooking thread started, parameter: %p
[decrypt] MinHook initialized! %d
[decrypt] Increasing working memory size to %p!
[decrypt] Waiting on %s...
[decrypt] A command has been received from the client "%s"
[decrypt] Thread Start Address
[decrypt] Hook "%s" placed on %s!
[decrypt] Hook "%s" couldn't be placed on %s
[decrypt] An uninitialized hook was activated.
[decrypt] Failed to activate the "%s" hook!
[decrypt] Managed to gather the address to il2cpp::find_game_objects in %p ms (%p tries)
[decrypt] Protected launcher thread by ID %d
[decrypt] SL-AC heartbeat tick!
[decrypt] Status nominal.
[decrypt] SL-AC thread protection tick!
[decrypt] Running integrity check on watched addresses...
[decrypt] Return Address: Thread RIP Check
[decrypt] The return address at %s was invalid.
[decrypt] A SL-AC thread has stopped execution inadvertently!
[decrypt] Window check executed!
[decrypt] Process check executed!
[decrypt] Blacklisted program detected!
[decrypt] Pattern scan executed!
[decrypt] Developer Mode Enabled!
[decrypt] Scanning mapped memory region: %p - %p
[decrypt] Scanning module memory region: %p - %p
[decrypt] Increasing working memory size to %p!
[decrypt] Watching address ID %d
[decrypt] Cheat Engine
[decrypt] MIDKNIGHT LLC
[decrypt] ReShade
- СВЯЗЬ С СЕРВЕРОМ

В коммуникации клиент/сервер разработчики отдают предпочтение open-source библиотекам cURL и Crypto++. Ссылка на репозитории: github.com/curl/curl; github.com/weidai11/cryptopp
Абсолютно все отправляемые пакеты от анти-чита серверу и получаемые ответы от сервера поступают через функции этих библиотек.

Я не буду описывать подробно их связь, опишу что собирает анти-чит для последующего шифрования и отправки на сервер.

Первым делом анти-чит начинает логгировать те же самые действия, что были до этого, но при этом заворачивает это в json, шифрует и отправляет серверу.

Дешифрованный пакет выглядит следующем образом:

Код:
"{\"detection_information\":\"Status nominal.\",\"detection_status\":0,\"game_files\":[{\"name\":\"appdata.bat\"},{\"name\":\"ConfigTemplates\"},{\"name\":\"CreditsCache.json\"},{\"name\":\"GameAssembly.dll\"},{\"name\":\"license.txt\"},{\"name\":\"log.bat\"},{\"name\":\"log.txt\"},{\"name\":\"mono.msi\"},{\"name\":\"monoinstall.vdf\"},{\"name\":\"readme.txt\"},{\"name\":\"scl-sl.log\"},{\"name\":\"SCPSL.exe\"},{\"name\":\"SCPSL_Data\"},{\"name\":\"SL-AC.dll\"},{\"name\":\"Translations\"},{\"name\":\"Un"
Код:
"{\"detection_information\":\"Initial heartbeating.\",\"detection_status\":0,\"game_files\":[{\"name\":\"appdata.bat\"},{\"name\":\"ConfigTemplates\"},{\"name\":\"CreditsCache.json\"},{\"name\":\"GameAssembly.dll\"},{\"name\":\"license.txt\"},{\"name\":\"log.bat\"},{\"name\":\"log.txt\"},{\"name\":\"mono.msi\"},{\"name\":\"monoinstall.vdf\"},{\"name\":\"readme.txt\"},{\"name\":\"scl-sl.log\"},{\"name\":\"SCPSL.exe\"},{\"name\":\"SCPSL_Data\"},{\"name\":\"SL-AC.dll\"},{\"name\":\"Translations\"},{\"name"
Затем при присоединении к какому-нибудь серверу анти-чит отсылает список загруженных модулей, их размер и базовый адрес

Код:
"\",\"loaded_modules\":[{\"module_base\":140696840306688,\"module_name\":\"C:\\\\Program Files (x86)\\\\Steam\\\\steamapps\\\\common\\\\SCP Secret Laboratory\\\\SCPSL.exe\",\"module_size\":9986048},{\"module_base\":140714089709568,\"module_name\":\""
Не обошлось и без информации о потоках, модуль проходится по всем запущенным раннее потокам, собирая их айди, стартовый адрес, текущий адрес rip и адрес стек поинтера.

Код:
",\"thread_start_address\":140712428775056},{\"thread_id\":1036,\"thread_module_start_address\":140712420179968,\"thread_rip\":140712447511423,\"thread_rsp\":140712447511423,\"thread_start_address\":140712428775056},{\"thread_id\":2252,\"thread_module_start_address\":140712420179968,\"thread_rip\":140712447511423,\"thread_rsp\":140712447511423,\"thread_start_address\":140712428775056},{\"thread_id\":8996,\"thread_module_start_address\":140712420179968,\"thread_rip\":140712447511423,\"thread_rsp\":140712"
Но все эти адреса анти-чит записывает в виде десятичных чисел, если перевести в hex, то получим к примеру

module_base: 140696840306688 -> 00007FF689300000. Базовый адрес SCP:SL
module_size: 9986048 -> 0000000000986000

Также анти-чит после инициализации heartbeating будет неоднократно обращаться к URL
Пожалуйста, авторизуйтесь для просмотра ссылки.
и вызывать GetTickCount64, можете не переходить, потому что провалите проверку на User-Agent и получите 405 ошибку. :roflanEbalo:

Заключение

Первым делом попрошу подписаться на мой бложик:
Пожалуйста, авторизуйтесь для просмотра ссылки.


Благодарю за прочтение данной статьи, я всё ленился делать её, постоянно отвлекался на что-то незначительное и просто ленился, но в один момент мотивация пришла сама ко мне, и так я за пару дней собрал достаточно материала для того, чтобы поведать вам :D
Не прекращу благодарить свою команду Team Enterial: Arting, anarh1st47, Dark_Bull, easton, nelfo57

Надеюсь статья вам понравилась и вы узнали что-то новое для себя!

Блог моей команды:
Пожалуйста, авторизуйтесь для просмотра ссылки.
Лучший нахуй
 
Забаненный
Статус
Оффлайн
Регистрация
2 Фев 2021
Сообщения
453
Реакции[?]
82
Поинты[?]
3K
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
good
 
Пользователь
Статус
Оффлайн
Регистрация
17 Ноя 2021
Сообщения
249
Реакции[?]
34
Поинты[?]
2K
очень жаль что этот античит делал человек на зарплате который пытается банить по нексоренным стрингам в памяти:cry:
так же они недавно начали протектить global-metadata.dat
1659962361094.png
:cry:
 
Разработчик
Статус
Оффлайн
Регистрация
18 Мар 2020
Сообщения
439
Реакции[?]
870
Поинты[?]
195K
очень жаль что этот античит делал человек на зарплате который пытается банить по нексоренным стрингам в памяти:cry:
так же они недавно начали протектить global-metadata.dat
Посмотреть вложение 216029
:cry:
Можешь кидать им ссылку на эту статью, думаю, что у них начнёт гореть с этого:roflanEbalo:
 
Разработчик
Статус
Оффлайн
Регистрация
18 Мар 2020
Сообщения
439
Реакции[?]
870
Поинты[?]
195K
Пользователь
Статус
Оффлайн
Регистрация
16 Мар 2021
Сообщения
377
Реакции[?]
78
Поинты[?]
11K
Отличная тема, сколько времени потратил на написание?
 
Разработчик
Статус
Оффлайн
Регистрация
18 Мар 2020
Сообщения
439
Реакции[?]
870
Поинты[?]
195K
Отличная тема, сколько времени потратил на написание?
Начинал месяц назад, потом нахлынули разные проблемы, поэтому я очень мало уделял времени анти-читу, и вот вначале августа я за пару дней собрал материал для статьи
 
Начинающий
Статус
Оффлайн
Регистрация
10 Май 2019
Сообщения
19
Реакции[?]
22
Поинты[?]
2K
Как сложно быть настолько гениальным? Феноменальный контент на этих просторах, благодарю за то, что не уходишь от нас в столь трудный период времени, а на аватарке стоит нейтральное изображение. В общем, у меня есть только один вопрос. Как именно ты использовал возможности x64dbgpy в своем анализе?
 
Разработчик
Статус
Оффлайн
Регистрация
18 Мар 2020
Сообщения
439
Реакции[?]
870
Поинты[?]
195K
Как именно ты использовал возможности x64dbgpy в своем анализе?
Для двух мест, где использовался Heaven's Gate я писал скрипт для тамполайн хука на выделенную память с моим кодом
 
kira yoshikage
Пользователь
Статус
Оффлайн
Регистрация
21 Янв 2020
Сообщения
885
Реакции[?]
115
Поинты[?]
3K
Начинающий
Статус
Оффлайн
Регистрация
5 Сен 2020
Сообщения
78
Реакции[?]
1
Поинты[?]
0
Какой анализ лучше "познать" начинающему реверсеру? Статический или динамический?
 
Разработчик
Статус
Оффлайн
Регистрация
18 Мар 2020
Сообщения
439
Реакции[?]
870
Поинты[?]
195K
Забаненный
Статус
Оффлайн
Регистрация
4 Авг 2022
Сообщения
15
Реакции[?]
0
Поинты[?]
0
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Летний шалом! Целых 4 месяца я не выпускал контент, связанный с анализом семплов и вот пришло время исправляться. Люди, подписанные на меня знают причину долгого отсутствия, если кому интересно, почитать можно здесь:
Пожалуйста, авторизуйтесь для просмотра ссылки.


- ПРЕДИСЛОВИЕ

Исходя из мнений моих коллег по ЦЕХу было принято решение проанализировать анти-чит из игры SCP: Secret Laboratory
В этой статье будет представлен анализ механизмов SCP Anti-Cheat, которые предназначены для предотвращения манипуляции с памятью игры.

По традиции, которую я позаимствовал у Arting, в начале статьи кратко опишу какими утилитами я пользовался во время анализа анти-чита:

Мой основной инструмент для проведения динамического анализа - x64dbg
Изредка, но всё же если и провожу статический анализ, то делаю это через IDA Pro 7.7.220118

Так же список плагинов для отладчика, которые я использовал:
- SharpOD — Анти-анти-дебаг плагин, который как по мне в некоторых моментах эффективнее ScyllaHide против протекторов (VMProtect, Themida).​
- x64dbgpy — для выполнения скриптов написанных на языке Python, использующие SDK отладчика.​

- Scylla (x64) — утилита для реконструктции импортов.
- Visual Studio 2019 — для написания своей DLL, которая выполняет все необходимые хуки и патчи.

Переходим непосредственно к самому анализу :)

- ЛАУНЧЕР И МОДУЛЬ В ОБЩЕМ ПЛАНЕ

Оба таргета имеют х64 архитектуру и накрыты протектором Themida, по всем канонам современной защиты имеют фулл-пресет защиты + разработчики во время валидации они будут использовать функцию из SDK Фемиды для проверки целостности модуля - SECheckCodeIntegrity.

Игра у нас на движке Unity, но почти вся защита находится в модуле SL-AC, который полностью написан на нативном С++ без всяких намёков на CLR-сборку.

- МОДУЛЬ АНТИ-ЧИТА

- Механизм обнаружения отладчика от Фемиды
Фемида палила мой отладчик даже после того как я перехукал всю таблицу системных вызовов, понятное дело, что системные вызовы Фемиды никто не отменял, но тем не менее само обнаружение отладчика состояло в том, чтобы обнаруживать название окна, я переименовал свой отладчик и на удивление протектор перестал выбивать сообщения о детектах :kappa:


- Инициализация
Я обратил внимание на то, что анти-чит тоскает за собой библиотеку MinHook, понял я это по Enum, которую оставляет за собой библиотека.

Код:
00007FFE3927DC0C  | 48:8D05 ED131200         | LEA RAX,QWORD PTR DS:[7FFE3939F000]         | 00007FFE3939F000:"MH_UNKNOWN"
00007FFE3927DC13  | EB 7C                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC15  | 48:8D05 F0131200         | LEA RAX,QWORD PTR DS:[7FFE3939F00C]         | 00007FFE3939F00C:"MH_OK"
00007FFE3927DC1C  | EB 73                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC1E  | 48:8D05 F3131200         | LEA RAX,QWORD PTR DS:[7FFE3939F018]         | 00007FFE3939F018:"MH_ERROR_ALREADY_INITIALIZED"
00007FFE3927DC25  | EB 6A                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC27  | 48:8D05 0A141200         | LEA RAX,QWORD PTR DS:[7FFE3939F038]         | 00007FFE3939F038:"MH_ERROR_NOT_INITIALIZED"
00007FFE3927DC2E  | EB 61                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC30  | 48:8D05 21141200         | LEA RAX,QWORD PTR DS:[7FFE3939F058]         | 00007FFE3939F058:"MH_ERROR_ALREADY_CREATED"
00007FFE3927DC37  | EB 58                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC39  | 48:8D05 38141200         | LEA RAX,QWORD PTR DS:[7FFE3939F078]         | 00007FFE3939F078:"MH_ERROR_NOT_CREATED"
00007FFE3927DC40  | EB 4F                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC42  | 48:8D05 47141200         | LEA RAX,QWORD PTR DS:[7FFE3939F090]         | 00007FFE3939F090:"MH_ERROR_ENABLED"
00007FFE3927DC49  | EB 46                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC4B  | 48:8D05 56141200         | LEA RAX,QWORD PTR DS:[7FFE3939F0A8]         | 00007FFE3939F0A8:"MH_ERROR_DISABLED"
00007FFE3927DC52  | EB 3D                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC54  | 48:8D05 65141200         | LEA RAX,QWORD PTR DS:[7FFE3939F0C0]         | 00007FFE3939F0C0:"MH_ERROR_NOT_EXECUTABLE"
00007FFE3927DC5B  | EB 34                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC5D  | 48:8D05 74141200         | LEA RAX,QWORD PTR DS:[7FFE3939F0D8]         | 00007FFE3939F0D8:"MH_ERROR_UNSUPPORTED_FUNCTION"
00007FFE3927DC64  | EB 2B                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC66  | 48:8D05 8B141200         | LEA RAX,QWORD PTR DS:[7FFE3939F0F8]         | 00007FFE3939F0F8:"MH_ERROR_MEMORY_ALLOC"
00007FFE3927DC6D  | EB 22                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC6F  | 48:8D05 9A141200         | LEA RAX,QWORD PTR DS:[7FFE3939F110]         | 00007FFE3939F110:"MH_ERROR_MEMORY_PROTECT"
00007FFE3927DC76  | EB 19                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC78  | 48:8D05 A9141200         | LEA RAX,QWORD PTR DS:[7FFE3939F128]         | 00007FFE3939F128:"MH_ERROR_MODULE_NOT_FOUND"
00007FFE3927DC7F  | EB 10                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC81  | 48:8D05 C0141200         | LEA RAX,QWORD PTR DS:[7FFE3939F148]         | 00007FFE3939F148:"MH_ERROR_FUNCTION_NOT_FOUND"
00007FFE3927DC88  | EB 07                    | JMP sl-ac.7FFE3927DC91                      |
00007FFE3927DC8A  | 48:8D05 D7141200         | LEA RAX,QWORD PTR DS:[7FFE3939F168]         | 00007FFE3939F168:"(unknown)"
00007FFE3927DC91  | 48:83C4 10               | ADD RSP,10                                  |
Функция DllMain не виртуализирована Фемидой, это даёт нам возможность найти его по паттерну и поставить бряк на него.

Посмотреть вложение 215958

Во время своей инициализации анти-чит вызывает виртуализированный CreateThread, этот поток считается главным, и первым что он сделает, так это установит свои хуки.

Посмотреть вложение 215959

Рассмотрим список расставляемых хуков анти-читом (и чуть-чуть темидой)
Список хуков:

Код:
Импортируемая библиотека: GameAssembly.dll, экспортируемая функция: il2cpp_resolve_icall
Импортируемая библиотека: kernel32.dll, экспортируемая функция: GetModuleHandleW
Импортируемая библиотека: kernel32.dll, экспортируемая функция: GetProcAddress
Импортируемая библиотека: kernel32.dll, экспортируемая функция: LoadLibraryA
Импортируемая библиотека: kernel32.dll, экспортируемая функция: LoadLibraryW
Импортируемая библиотека: user32.dll, экспортируемая функция: GetAsyncKetState
Импортируемая библиотека: ntdll.dll, экспортируемая функция: DbgUiRemoteBreakin (Поставлен Фемидой, чтобы при попытке присоединиться к процессу кидало на LdrShutdownProcess :kappa:)
Импортируемая библиотека: ntdll.dll, экспортируемая функция: NtOpenFile
- Блокировка инжекта (Хук NtOpenFile)
Он устанавливается сразу же после установки хука на GetProcAddress.

В декомпилированном hkNtOpenFile прослеживаются вызовы конструктора std::wstring, что может говорить о том, что скорее всего конструктор в дальнейшем будет использоваться для работы со структурой POBJECT_ATTRIBUTES.

Чтобы получать имя объекта хук читает сразу два указателя на структуры: POBJECT_ATTRIBUTES и PUNICODE_STRING
Чтение происходит таким образом: pObjectAttributes->pObjectName->Buffer.

Посмотреть вложение 215960

В последующем анти-чит будет проверять находится ли бинарник в папке Windows и имеет при себе разные проверки, связанные с сертификатом.



Если же файла не было в Windows, то в связке с вызовами CryptQueryObject, CertGetNameStringW, CertFindCertificateInStore, он начинает проверять издателя цифровой подписи и получать имя эмитента.





Пройдя дальше я попал на завиртуализированную функцию, которую я назвал IsBlacklistedCert.
Функция внутри виртуальной машины темиды сравнивает полученный сертификат с запрещенными сертификатами, который хранится также под виртуализацией.

Под проверку попали такие сертификаты, как: ReShade, MIDKNIGHT LLC и Cheat Engine, если что-то из них он обнаруживал у файла, то функция возвращала TRUE.



Под конец всей проверки он делает для себя лог: [Windows Folder - %s]; [Signing: %s - %s]

- ОБХОДИМ БЛОКИРОВКУ ИНЖЕКТА

Есть много способов обмануть анти-чит и дать заинжектиться своей длл, начиная от инжекта до того как анти-чит установит свои хуки и заканчивая тем, что в наглую можно блокировать хук анти-чита. Мой способ будет связан с перехватом функции для расстановки хуков анти-чита.

Так как функции либы с хуками не находится под виртой, то нам ничего не мешает найти MH_EnableHook и MH_CreateHook по паттернам.

Для нахождения этих функции я составил два паттерна:

Код:
4C 24 08 57 48 81 EC 90 00 00 00 48 8B FC B9 24 00 00 00 B8 CC CC CC CC F3 AB 48 8B 8C 24 A0 00 00 00 - MH_CreateHook
48 89 4C 24 08 57 48 83 EC 20 48 8B FC B9 08 00 00 - MH_EnableHook
И вуаля! Наши функции для перехватывания были найдены





Помимо того, что с помощью перехвата MH_EnableHook я буду блокировать активацию hkNtOpenFile, возвращая MH_OK параллельно я мониторил какие хуки он ещё расставлял, и в своей консоли оставлял адреса детуров анти-чита.



Конечно же эти проверки на подлинность файла не являются эффективными, можно пропустить эти проверки одним патчем инструкции JNE, или же поставить точку останова на CloseHandle, потому что именно он отвечает за блокировку дескриптора. Никакого сисколла NtClose, просто обыкновенный CloseHandle.

А вот и мемный обход блокировки инжекта

Недавно разработчики обновляли анти-чит, из-за этой обновы все мои комментарии и метки на функции пошли по пизде, и они виртуализировали пару функции и ещё некоторый пользовательский код, CloseHandle был в их числе. :roflanEbalo:



- Хук GetAsyncKeyState
Этот хук передает анти-читу информацию о адресе возврата. А также в хуке вызывается RtlPcToFileHeader для чтения PE хэдера у файла, с которого была вызвана эта функция, если у файла не оказалось первых двух байтов - 0x5A4D, то для анти-чита это первый звоночек зарепортить пользователя.

Всё это примерно выглядит таким образом:

C++:
PVOID imageBase;

RtlPcToFileHeader( _ReturnAddress( ), &imageBase );

if ( *( WORD * ) imageBase != 0x5A4D )
     // Hidden module
else
    // OK
- Обнаружение по окнам/процессу наше всё!
На дворе уже прошла половина 2022 года, и к сожалению если вы ждали здесь гениальные механизмы обнаружения отладчика, то забудьте про это, потому что и Фемида и разработчики Northwood Studios решили использовать одинаковые техники обнаружения. Чтобы обнаруживать нежелательные процессы такие как: OllyDbg, Cheat Engine, Process Hacker, Process Monitor Фемида использует FindWindowA, а анти-чит в свою очередь использует два механизма: Обнаружение окна и обнаружение процесса.

- Обнаружение виртуальной машины
Опять же, если вы думали, что анти-чит будет использовать методы обхода песочницы/вм, основанные на времени, то вы сильно заблуждались.
В совокупности с обнаружением нежелательных процессов второй механизм использует в своём списке и системные процессы VMWare. Всё это реализовано в булевой функции, если функция вернула - 1, значит он обнаружил что-то из разряда VMWare и выбил ошибку. Патчится это двумя инструкциями "xor eax, eax; ret;"



Ну и сам мем



- Второй механизм обнаружения
Второй метод заключается в злоупотреблении функции EnumWindows, если быть точнее, то его каллбеком, который выступает первым параметром. (
Пожалуйста, авторизуйтесь для просмотра ссылки.
)

Эта функция будет вызываться частенько, поэтому перехватываем её и возвращаем FALSE, потому что анти-чит не проверяет возвращаемое значение функции.

Получаем последнее китайское предупреждение от анти-чита и пропускаем его со спокойной душой, потому что игра продолжит загрузку в главное меню.



Эту технику опрометчиво использовать в своих механизмах обнаружений, только если на ваш продукт не покушаются реверс-инженеры, не имеющие опыта в динамическом анализе.

- Техника Heaven's Gate
Да-да, анти-чит под х64, но тем не менее используют данную технику, и по всей видимости для предотвращения трассировки, потому что как мы все знаем xdbg не умеет проходить сквозь инструкцию ret far, как тот же Cheat Engine или WinDbg.

Эти функции проверяют наличие дебаг флагов и дебаг порта у текущего процесса

Код:
00007FF907136CB1  | 6A 33                    | PUSH 33                                                 | 0x33 - Доступ к х64 сегменту кода, 0x23 - к х32
00007FF907136CB3  | E8 00000000              | CALL sl-ac.7FF907136CB8                                 | call $0
00007FF907136CB8  | 830424 05                | ADD DWORD PTR SS:[RSP],5                                |
00007FF907136CBC  | CB                       | RET FAR                                                 |
00007FF907136CBD  | 49:89D8                  | MOV R8,RBX                                              |
00007FF907136CC0  | 48:89E3                  | MOV RBX,RSP                                             |
00007FF907136CC3  | FF3424                   | PUSH QWORD PTR SS:[RSP]                                 |
00007FF907136CC6  | FF3424                   | PUSH QWORD PTR SS:[RSP]                                 |
00007FF907136CC9  | 40:80E4 F0               | AND SPL,F0                                              | Выравнивание стека 16 байт
00007FF907136CCD  | 48:83C4 10               | ADD RSP,10                                              |
00007FF907136CD1  | 48:83EC 30               | SUB RSP,30                                              |
00007FF907136CD5  | 48:C7C1 FFFFFFFF         | MOV RCX,FFFFFFFFFFFFFFFF                                | Текущий процесс
00007FF907136CDC  | 48:C7C2 1E000000         | MOV RDX,1E                                              | Process Debug Port
00007FF907136CE3  | 49:C7C1 08000000         | MOV R9,8                                                |
00007FF907136CEA  | 48:C74424 20 00000000    | MOV QWORD PTR SS:[RSP+20],0                             |
00007FF907136CF3  | FFD6                     | CALL RSI                                                | Вызываем системный вызов
00007FF907136CF5  | 48:83C4 30               | ADD RSP,30                                              |
00007FF907136CF9  | 48:89DC                  | MOV RSP,RBX                                             |
00007FF907136CFC  | E8 00000000              | CALL sl-ac.7FF907136D01                                 | call $0
00007FF907136D01  | C74424 04 23000000       | MOV DWORD PTR SS:[RSP+4],23                             |
00007FF907136D09  | 830424 0D                | ADD DWORD PTR SS:[RSP],D                                |
00007FF907136D0D  | CB                       | RET FAR                                                 |
Код:
00007FF90721501C  | E8 00000000              | CALL sl-ac.7FF907215021                                 | call $0
00007FF907215021  | 830424 05                | ADD DWORD PTR SS:[RSP],5                                |
00007FF907215025  | CB                       | RET FAR                                                 |
00007FF907215026  | 49:89D8                  | MOV R8,RBX                                              |
00007FF907215029  | 48:89E3                  | MOV RBX,RSP                                             |
00007FF90721502C  | FF3424                   | PUSH QWORD PTR SS:[RSP]                                 |
00007FF90721502F  | FF3424                   | PUSH QWORD PTR SS:[RSP]                                 |
00007FF907215032  | 40:80E4 F0               | AND SPL,F0                                              | Выравнивание стека 16 байт
00007FF907215036  | 48:83C4 10               | ADD RSP,10                                              |
00007FF90721503A  | 48:83EC 30               | SUB RSP,30                                              |
00007FF90721503E  | 48:C7C1 FFFFFFFF         | MOV RCX,FFFFFFFFFFFFFFFF                                |
00007FF907215045  | 48:C7C2 07000000         | MOV RDX,7                                               | Process Debug Flags
00007FF90721504C  | 49:C7C1 08000000         | MOV R9,8                                                |
00007FF907215053  | 48:C74424 20 00000000    | MOV QWORD PTR SS:[RSP+20],0                             |
00007FF90721505C  | FFD6                     | CALL RSI                                                |
00007FF90721505E  | 48:83C4 30               | ADD RSP,30                                              |
00007FF907215062  | 48:89DC                  | MOV RSP,RBX                                             |
00007FF907215065  | E8 00000000              | CALL sl-ac.7FF90721506A                                 | call $0
00007FF90721506A  | C74424 04 23000000       | MOV DWORD PTR SS:[RSP+4],23                             |
00007FF907215072  | 830424 0D                | ADD DWORD PTR SS:[RSP],D                                |
00007FF907215076  | CB                       | RET FAR                                                 |
Что ещё меня поразило, так это их попытка завернуть данную технику под виртуальную машину Фемиды, потому что это эта функция находилась в сегменте Фемиды, где выполняется ВМ. Видимо, Nortwood Studios не вкурсе, что протекторы не могут закинуть технику Heaven's Gate как раз из-за инструкции RET FAR. Поэтому такой чистый код валяется в сегменте среди виртуализированного кода.

- Сканирование памяти
На этом этапе защиты анти-чит с помощью GetStartUpInfo и VirtualQuery проходится по регионам памяти

Код:
00007FFEF2E19B70  | 48:895424 10             | MOV QWORD PTR SS:[RSP+10],RDX          | Memory Scan
00007FFEF2E19B75  | 48:894C24 08             | MOV QWORD PTR SS:[RSP+8],RCX           |
00007FFEF2E19B7A  | 48:81EC 88000000         | SUB RSP,88                             |
00007FFEF2E19B81  | 48:8B05 985A1300         | MOV RAX,QWORD PTR DS:[7FFEF2F4F620]    |
00007FFEF2E19B88  | 48:33C4                  | XOR RAX,RSP                            |
00007FFEF2E19B8B  | 48:894424 78             | MOV QWORD PTR SS:[RSP+78],RAX          |
00007FFEF2E19B90  | 0FB605 C1E21300          | MOVZX EAX,BYTE PTR DS:[7FFEF2F57E58]   |
00007FFEF2E19B97  | 85C0                     | TEST EAX,EAX                           |
00007FFEF2E19B99  | 75 0D                    | JNE sl-ac.7FFEF2E19BA8                 |
00007FFEF2E19B9B  | 48:8D0D 7EE41300         | LEA RCX,QWORD PTR DS:[7FFEF2F58020]    |
00007FFEF2E19BA2  | FF15 58350F00            | CALL QWORD PTR DS:[7FFEF2F0D100]       | Virtualized GetSystemInfo
00007FFEF2E19BA8  | 48:8B05 79E41300         | MOV RAX,QWORD PTR DS:[7FFEF2F58028]    |
00007FFEF2E19BAF  | 48:894424 20             | MOV QWORD PTR SS:[RSP+20],RAX          |
00007FFEF2E19BB4  | 48:8B05 75E41300         | MOV RAX,QWORD PTR DS:[7FFEF2F58030]    |
00007FFEF2E19BBB  | 48:894424 28             | MOV QWORD PTR SS:[RSP+28],RAX          |
00007FFEF2E19BC0  | 48:8B4424 28             | MOV RAX,QWORD PTR SS:[RSP+28]          |
00007FFEF2E19BC5  | 48:394424 20             | CMP QWORD PTR SS:[RSP+20],RAX          |
00007FFEF2E19BCA  | 0F83 83000000            | JAE sl-ac.7FFEF2E19C53                 |
00007FFEF2E19BD0  | 41:B8 30000000           | MOV R8D,30                             |
00007FFEF2E19BD6  | 48:8D5424 48             | LEA RDX,QWORD PTR SS:[RSP+48]          |
00007FFEF2E19BDB  | 48:8B4C24 20             | MOV RCX,QWORD PTR SS:[RSP+20]          |
00007FFEF2E19BE0  | FF15 2A350F00            | CALL QWORD PTR DS:[7FFEF2F0D110]       | Virtualized VirtualQuery
00007FFEF2E19BE6  | 8B4424 68                | MOV EAX,DWORD PTR SS:[RSP+68]          | Полученный размер региона памяти
00007FFEF2E19BEA  | 25 00100000              | AND EAX,1000                           |
00007FFEF2E19BEF  | 3D 00100000              | CMP EAX,1000                           | Если после (regionSize & 0x1000) не дал в результате 0x1000, то скип
00007FFEF2E19BF4  | 75 43                    | JNE sl-ac.7FFEF2E19C39                 |
00007FFEF2E19BF6  | 837C24 6C 20             | CMP DWORD PTR SS:[RSP+6C],20           | Проверка на PAGE_EXECUTE_READ
00007FFEF2E19BFB  | 74 07                    | JE sl-ac.7FFEF2E19C04                  |
00007FFEF2E19BFD  | 837C24 6C 40             | CMP DWORD PTR SS:[RSP+6C],40           | Проверка на PAGE_EXECUTE_READWRITE
00007FFEF2E19C02  | 75 35                    | JNE sl-ac.7FFEF2E19C39                 |
00007FFEF2E19C04  | 48:8B4424 48             | MOV RAX,QWORD PTR SS:[RSP+48]          |
00007FFEF2E19C09  | 48:894424 30             | MOV QWORD PTR SS:[RSP+30],RAX          |
00007FFEF2E19C0E  | 48:8B4424 60             | MOV RAX,QWORD PTR SS:[RSP+60]          |
00007FFEF2E19C13  | 48:894424 38             | MOV QWORD PTR SS:[RSP+38],RAX          |
00007FFEF2E19C18  | 48:8B4424 30             | MOV RAX,QWORD PTR SS:[RSP+30]          |
00007FFEF2E19C1D  | 48:894424 40             | MOV QWORD PTR SS:[RSP+40],RAX          |
00007FFEF2E19C22  | 4C:8B4424 38             | MOV R8,QWORD PTR SS:[RSP+38]           |
00007FFEF2E19C27  | 48:8B5424 40             | MOV RDX,QWORD PTR SS:[RSP+40]          |
00007FFEF2E19C2C  | 48:8B8C24 98000000       | MOV RCX,QWORD PTR SS:[RSP+98]          |
00007FFEF2E19C34  | E8 17040000              | CALL sl-ac.7FFEF2E1A050                |
00007FFEF2E19C39  | 48:8B4424 60             | MOV RAX,QWORD PTR SS:[RSP+60]          |
00007FFEF2E19C3E  | 48:8B4C24 20             | MOV RCX,QWORD PTR SS:[RSP+20]          |
00007FFEF2E19C43  | 48:03C8                  | ADD RCX,RAX                            |
00007FFEF2E19C46  | 48:8BC1                  | MOV RAX,RCX                            |
00007FFEF2E19C49  | 48:894424 20             | MOV QWORD PTR SS:[RSP+20],RAX          |
00007FFEF2E19C4E  | E9 6DFFFFFF              | JMP sl-ac.7FFEF2E19BC0                 |
00007FFEF2E19C53  | 48:8B8C24 98000000       | MOV RCX,QWORD PTR SS:[RSP+98]          |
00007FFEF2E19C5B  | E8 800AFAFF              | CALL sl-ac.7FFEF2DBA6E0                |
00007FFEF2E19C60  | 48:8B4C24 78             | MOV RCX,QWORD PTR SS:[RSP+78]          |
00007FFEF2E19C65  | 48:33CC                  | XOR RCX,RSP                            |
00007FFEF2E19C68  | E8 B3160B00              | CALL sl-ac.7FFEF2ECB320                |
00007FFEF2E19C6D  | 48:81C4 88000000         | ADD RSP,88                             |
00007FFEF2E19C74  | C3                       | RET                                    |
В декомпилированном виде:



- Сканирование паттернов читов в памяти

Помимо того что анти-чит сканирует всю память, он также успевает в полученных регионах памяти проверять список паттернов, которые он собирал у других читов.

Код:
"\x41\x69\x6D\x62\x6F\x74\x20\x54\x61\x72\x67\x65\x74" - Aimbot Target
"\x53\x63\x69\x65\x6E\x74\x69\x73\x74\x20\x4B\x65\x79\x63\x61\x72\x64" - Scientist Keycard
"\x70\x65\x64\x6F\x70\x68\x69\x6C\x65\x67\x61\x6D\x69\x6E\x67\x2E\x63\x63" - pedophilegaming.cc
"\x6B\x69\x74\x65\x68\x34" - kiteh4x
"\x3C\x63\x6F\x6C\x6F\x72\x3D\x77\x68\x69\x74\x65\x3E\x70\x6C\x61\x79\x65\x72\x73\x3A\x20\x25\x69\x3C\x2F\x63\x6F\x6C\x6F\x72\x3E" - <color=white>players: %i</color>
"\x3C\x63\x6F\x6C\x6F\x72\x3D\x25\x73\x3E\x25\x73\x20\x2D\x20\x25\x2E\x32\x66\x3C\x2F\x63\x6F\x6C\x6F\x72\x3E"- <color=%s>%s - %.2f</color>
"\x23\x23\x4D\x61\x69\x6E\x4D\x65\x6E\x75\x42\x61\x72" - ##MainMenuBar
- Связь с защищаемым потоком

Для того чтобы поддерживать защищаемый поток анти-чит создаёт 1 поток. Анти-чит берёт айди потока и будет всячески проверять его через некоторое время, используя Sleep.

Проверка на то, работает ли защищаемый поток:

Код:
00007FFE2C77B3D9  | 48:8B4424 28             | MOV RAX,QWORD PTR SS:[RSP+28]               |
00007FFE2C77B3DE  | 48:83C0 08               | ADD RAX,8                                   |
00007FFE2C77B3E2  | 48:894424 28             | MOV QWORD PTR SS:[RSP+28],RAX               |
00007FFE2C77B3E7  | 48:8B4424 38             | MOV RAX,QWORD PTR SS:[RSP+38]               |
00007FFE2C77B3EC  | 48:394424 28             | CMP QWORD PTR SS:[RSP+28],RAX               |
00007FFE2C77B3F1  | 0F84 A4000000            | JE sl-ac.7FFE2C77B49B                       |
00007FFE2C77B3F7  | 48:8B4424 28             | MOV RAX,QWORD PTR SS:[RSP+28]               |
00007FFE2C77B3FC  | 48:894424 40             | MOV QWORD PTR SS:[RSP+40],RAX               |
00007FFE2C77B401  | 48:8D0D 48CA1300         | LEA RCX,QWORD PTR DS:[7FFE2C8B7E50]         |
00007FFE2C77B408  | E8 F384F9FF              | CALL sl-ac.7FFE2C713900                     |
00007FFE3259B40D  | 48:894424 50             | MOV QWORD PTR SS:[RSP+50],RAX               |
00007FFE3259B412  | 48:8B4424 40             | MOV RAX,QWORD PTR SS:[RSP+40]               |
00007FFE3259B417  | 48:8B00                  | MOV RAX,QWORD PTR DS:[RAX]                  |
00007FFE3259B41A  | 48:894424 48             | MOV QWORD PTR SS:[RSP+48],RAX               |
00007FFE3259B41F  | 48:8B5424 48             | MOV RDX,QWORD PTR SS:[RSP+48]               |
00007FFE3259B424  | 48:8B4C24 50             | MOV RCX,QWORD PTR SS:[RSP+50]               |
00007FFE3259B429  | E8 72BD0000              | CALL sl-ac.7FFE325A71A0                     | Проверяем не откинулся ли поток
00007FFE3259B42E  | 0FB6C0                   | MOVZX EAX,AL                                |
00007FFE3259B431  | 85C0                     | TEST EAX,EAX                                |
00007FFE3259B433  | 75 56                    | JNE sl-ac.7FFE3259B48B                      |
00007FFE3259B435  | 48:8D4424 20             | LEA RAX,QWORD PTR SS:[RSP+20]               |
00007FFE3259B43A  | 48:8BF8                  | MOV RDI,RAX                                 |
00007FFE3259B43D  | 33C0                     | XOR EAX,EAX                                 |
00007FFE3259B43F  | B9 01000000              | MOV ECX,1                                   |
00007FFE3259B444  | F3:AA                    | REP STOSB                                   |
00007FFE3259B446  | 48:8D4424 21             | LEA RAX,QWORD PTR SS:[RSP+21]               |
00007FFE3259B44B  | 48:8BF8                  | MOV RDI,RAX                                 |
00007FFE3259B44E  | 33C0                     | XOR EAX,EAX                                 |
00007FFE3259B450  | B9 01000000              | MOV ECX,1                                   |
00007FFE3259B455  | F3:AA                    | REP STOSB                                   |
00007FFE3259B457  | 48:8D4424 22             | LEA RAX,QWORD PTR SS:[RSP+22]               |
00007FFE3259B45C  | 48:8BF8                  | MOV RDI,RAX                                 |
00007FFE3259B45F  | 33C0                     | XOR EAX,EAX                                 |
00007FFE3259B461  | B9 01000000              | MOV ECX,1                                   |
00007FFE3259B466  | F3:AA                    | REP STOSB                                   |
00007FFE3259B468  | 44:0FB64C24 20           | MOVZX R9D,BYTE PTR SS:[RSP+20]              |
00007FFE3259B46E  | 44:0FB64424 21           | MOVZX R8D,BYTE PTR SS:[RSP+21]              |
00007FFE3259B474  | 0FB65424 22              | MOVZX EDX,BYTE PTR SS:[RSP+22]              |
00007FFE3259B479  | 48:8D4C24 60             | LEA RCX,QWORD PTR SS:[RSP+60]               |
00007FFE3259B47E  | E8 4D000000              | CALL sl-ac.7FFE3259B4D0                     |
00007FFE3259B483  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE3259B486  | E8 55040000              | CALL sl-ac.7FFE3259B8E0                     | Лог "A SL-AC thread has stopped execution inadvertently!"
00007FFE3259B48B  | B9 0F000000              | MOV ECX,F                                   |
00007FFE3259B490  | FF15 8A1D0F00            | CALL QWORD PTR DS:[<&Sleep>]                |
00007FFE3259B496  | E9 3EFFFFFF              | JMP sl-ac.7FFE3259B3D9                      | Возвращаемся на JE проверку
00007FFE3259B49B  | 48:8D8C24 A0000000       | LEA RCX,QWORD PTR SS:[RSP+A0]               |
00007FFE3259B4A3  | E8 7880FCFF              | CALL sl-ac.7FFE32563520                     |
00007FFE3259B4A8  | 48:8B8C24 A8000000       | MOV RCX,QWORD PTR SS:[RSP+A8]               |
00007FFE3259B4B0  | 48:33CC                  | XOR RCX,RSP                                 |
00007FFE3259B4B3  | E8 68FE0A00              | CALL sl-ac.7FFE3264B320                     |
00007FFE3259B4B8  | 48:81C4 B0000000         | ADD RSP,B0                                  |
00007FFE3259B4BF  | 5F                       | POP RDI                                     |
00007FFE3259B4C0  | C3                       | RET                                         |
- Что же будет, если поток будет заморожен? Ничего.
- Что будет, если поток будет закрыт? Анти-чит начнёт спамить логами, что потока больше нет.

- Защищаемый поток

В защищаемом потоке нет ничего необычного, вначале вызывается GetTickCount64, затем сразу же вызываются две функции по несколько раз, где тоже фигурирует GetTickCount64, всё это дело выглядит следующим образом:

Код:
00007FFE2D3CD930  | 48:894C24 08             | MOV QWORD PTR SS:[RSP+8],RCX                |
00007FFE2D3CD935  | 48:83EC 28               | SUB RSP,28                                  |
00007FFE2D3CD939  | 48:8D0D D0A41300         | LEA RCX,QWORD PTR DS:[7FFE2D507E10]         |
00007FFE2D3CD940  | E8 CB9DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD945  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD948  | E8 330F0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD94D  | 48:8D0D 5CA41300         | LEA RCX,QWORD PTR DS:[7FFE2D507DB0]         |
00007FFE2D3CD954  | E8 B79DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD959  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD95C  | E8 1F0F0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD961  | 48:8D0D 98A41300         | LEA RCX,QWORD PTR DS:[7FFE2D507E00]         |
00007FFE2D3CD968  | E8 A39DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD96D  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD970  | E8 0B0F0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD975  | 48:8D0D B4A41300         | LEA RCX,QWORD PTR DS:[7FFE2D507E30]         |
00007FFE2D3CD97C  | E8 8F9DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD981  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD984  | E8 F70E0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD989  | 48:8D0D 90A41300         | LEA RCX,QWORD PTR DS:[7FFE2D507E20]         |
00007FFE2D3CD990  | E8 7B9DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD995  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD998  | E8 E30E0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD99D  | 48:8D0D 3CA41300         | LEA RCX,QWORD PTR DS:[7FFE2D507DE0]         |
00007FFE2D3CD9A4  | E8 679DF9FF              | CALL sl-ac.7FFE2D367710                     |
00007FFE2D3CD9A9  | 48:8BC8                  | MOV RCX,RAX                                 |
00007FFE2D3CD9AC  | E8 CF0E0000              | CALL sl-ac.7FFE2D3CE880                     |
00007FFE2D3CD9B1  | EB 00                    | JMP sl-ac.7FFE2D3CD9B3                      |
00007FFE2D3CD9B3  | 48:83C4 28               | ADD RSP,28                                  |
00007FFE2D3CD9B7  | C3                       | RET                                         |
В функции по адресу 7FFE2D3CE880 постоянно будет сравнивать текущий результат вызова GetTickCount64 с результатом, который был в прошлом вызове.

- Немного о GetTickCount64

И раз уже решили затронуть тему с GetTickCount64, то сейчас я попробую объяснить метод работы этой функции и метод обхода.
Если в дебаггере взглянуть на содержимое этой функции, то она довольно простая в реализации.

Код:
00007FFE9EDD5D30  | 8B0C25 0400FE7F          | MOV ECX,DWORD PTR DS:[7FFE0004]             |
00007FFE9EDD5D37  | B8 2003FE7F              | MOV EAX,7FFE0320                            |
00007FFE9EDD5D3C  | 48:C1E1 20               | SHL RCX,20                                  |
00007FFE9EDD5D40  | 48:8B00                  | MOV RAX,QWORD PTR DS:[RAX]                  |
00007FFE9EDD5D43  | 48:C1E0 08               | SHL RAX,8                                   |
00007FFE9EDD5D47  | 48:F7E1                  | MUL RCX                                     |
00007FFE9EDD5D4A  | 48:8BC2                  | MOV RAX,RDX                                 |
00007FFE9EDD5D4D  | C3                       | RET                                         |
000000007FFE0000 - Статичный адрес структуры KUSER_SHARED_DATA (
Пожалуйста, авторизуйтесь для просмотра ссылки.
), к которому обращается функция GetTickCount64 первой инструкцией, и получает значение TickCount.HighPart
Вторая инструкция запихивает TickCount.LowPart в EAX

Примерный декомпиль функции из иды:

C++:
ULONG GetTickCount64( )
{
    return ( ( TickCount.HighPart << 32 ) * ( TickCount.LowPart << 8 ) ) >> 64;
}
Итак, мы знаем, что в KUSER_SHARED_DATA нельзя ничего записывать, потому что структура только Read Only, но мы можем перехватить GetTickCount64, код хука я показывать не буду, а опишу лишь кратко его алгоритм.

  1. Сверяем адрес возврата (GetTickCount64 помимо анти-чита используется и в системных библиотеках)
  2. Пишем пару проверок на промежуток времени, который для анти-чита будет подозрительным
  3. Возвращаем функции свою константу.

- Сбор данных об комплектующих ПК

Анти-чит собирает информацию об аппаратных устройствах ПК за счёт поступаемых запросов WMI.

Для начала модуль вызывает функцию SysAllocString с аргументом "ROOT\\CIMV2", чтобы в дальнейшем с помощью указателя на интерфейс IWbemLocator вызвать функцию ConnectServer для подключения к локальному пространству имен ROOT\CIMV2, также в ConnectServer последним параметром выступает IWbemServices **ppNamespace, этот указатель на интерфейс понадобиться для выполнения вызовов IWbemServices, для тех кто в танке - через этот интерфейс потом можно будет выполнять запрос к WMI с помощью ExecQuery, но перед этим анти-чит ещё и вызовет CoSetProxyBlanket.

Список запросов анти-чита:

SELECT * FROM Win32_BaseBoard - класс Win32_BaseBoard предоставляет информацию о материнской плате компьютера, анти-чит берёт серийный номер и строковое значение Manufacturer, в котором содержится название прозиводителя материнской платы. В моём случае после исполнения функции анти-чит в результате получит название - Gigabyte Technology Co, Ltd.

SELECT * FROM Win32_ComputerSystem - из класса Win32_ComputerSystem анти-чит берёт только строковое значение Model.

SELECT * FROM Win32_DiskDrive - класс Win32_DiskDrive, содержащий в себе информацию о имеющихся жёстких дисках, анти-чит из этого класса читает только серийный номер.

SELECT * FROM Win32_Processor - класс Win32_Processor, содержащий в себе информацию о процессоре, анти-чит читает только имя процессора.

SELECT * FROM Win32_NetworkAdapter - класс Win32_NetworkAdapter, содержащий в себе информацию о сетевом адапторе, по документации MSDN этот класс устарел, и рекомендуют использовать вместо него класс MSFT_NetAdapter. Анти-чит читает оттуда MAC-Адрес.

SELECT * FROM Win32_VideoController - класс Win32_VideoController, позволяющий получить информацию о видеокарте, из этого класса можно много каких свойств приметить для дальнейшей генерации хвида, но разработчики, к сожалению читают тут только имя видеокарты.

Я не исключаю того, что анти-чит помимо перечисленного собирает и другую информацию, однако я продемонстрировал именно те методы, которые нашёл при анализе.

- Логгирование анти-чита как произведение исскуства
После общего анализа стоит упомянуть одну примечательную вещь, которая помогла мне выявить дальнейшие действия анти-чита - это логгирование.
Перед тем как выполнить какое-то важное действие анти-чит логгирует его для самого себя, естественно, используя для этого ксор строк.
Поэтому когда я узнал об этом я начал искать дешифрование строки, нашёл я её быстро.

Сама же функция дешифрования строки выглядит следующим образом:

Код:
00007FFEE7508470  | 4C:894424 18             | MOV QWORD PTR SS:[RSP+18],R8           |
00007FFEE7508475  | 48:895424 10             | MOV QWORD PTR SS:[RSP+10],RDX          |
00007FFEE750847A  | 48:894C24 08             | MOV QWORD PTR SS:[RSP+8],RCX           |
00007FFEE750847F  | 48:83EC 38               | SUB RSP,38                             |
00007FFEE7508483  | 48:8B4424 48             | MOV RAX,QWORD PTR SS:[RSP+48]          |
00007FFEE7508488  | F3:0F6F00                | MOVDQU XMM0,XMMWORD PTR DS:[RAX]       |
00007FFEE750848C  | 66:0F7F4424 10           | MOVDQA XMMWORD PTR SS:[RSP+10],XMM0    |
00007FFEE7508492  | 48:8B4424 50             | MOV RAX,QWORD PTR SS:[RSP+50]          |
00007FFEE7508497  | F3:0F6F00                | MOVDQU XMM0,XMMWORD PTR DS:[RAX]       |
00007FFEE750849B  | 66:0F7F0424              | MOVDQA XMMWORD PTR SS:[RSP],XMM0       |
00007FFEE75084A0  | 66:0F6F0424              | MOVDQA XMM0,XMMWORD PTR SS:[RSP]       |
00007FFEE75084A5  | 66:0FEF4424 10           | PXOR XMM0,XMMWORD PTR SS:[RSP+10]      |
00007FFEE75084AB  | 66:0F7F4424 20           | MOVDQA XMMWORD PTR SS:[RSP+20],XMM0    |
00007FFEE75084B1  | 48:8B4424 50             | MOV RAX,QWORD PTR SS:[RSP+50]          |
00007FFEE75084B6  | 66:0F6F4424 20           | MOVDQA XMM0,XMMWORD PTR SS:[RSP+20]    |
00007FFEE75084BC  | F3:0F7F00                | MOVDQU XMMWORD PTR DS:[RAX],XMM0       | В RAX будет находится декрипнутая строка
00007FFEE75084C0  | 48:83C4 38               | ADD RSP,38                             |
00007FFEE75084C4  | C3                       | RET                                    |
Это место будет многократно вызываться, поэтому будем перехватывать его.
И так я собрал список логов, которые анти-чит оставляет за собой:

Код:
[decrypt] CRC32 checks activated for %s
[decrypt] The CRC checks have failed!
[decrypt] Hooking thread started, parameter: %p
[decrypt] MinHook initialized! %d
[decrypt] Increasing working memory size to %p!
[decrypt] Waiting on %s...
[decrypt] A command has been received from the client "%s"
[decrypt] Thread Start Address
[decrypt] Hook "%s" placed on %s!
[decrypt] Hook "%s" couldn't be placed on %s
[decrypt] An uninitialized hook was activated.
[decrypt] Failed to activate the "%s" hook!
[decrypt] Managed to gather the address to il2cpp::find_game_objects in %p ms (%p tries)
[decrypt] Protected launcher thread by ID %d
[decrypt] SL-AC heartbeat tick!
[decrypt] Status nominal.
[decrypt] SL-AC thread protection tick!
[decrypt] Running integrity check on watched addresses...
[decrypt] Return Address: Thread RIP Check
[decrypt] The return address at %s was invalid.
[decrypt] A SL-AC thread has stopped execution inadvertently!
[decrypt] Window check executed!
[decrypt] Process check executed!
[decrypt] Blacklisted program detected!
[decrypt] Pattern scan executed!
[decrypt] Developer Mode Enabled!
[decrypt] Scanning mapped memory region: %p - %p
[decrypt] Scanning module memory region: %p - %p
[decrypt] Increasing working memory size to %p!
[decrypt] Watching address ID %d
[decrypt] Cheat Engine
[decrypt] MIDKNIGHT LLC
[decrypt] ReShade
- СВЯЗЬ С СЕРВЕРОМ

В коммуникации клиент/сервер разработчики отдают предпочтение open-source библиотекам cURL и Crypto++. Ссылка на репозитории: github.com/curl/curl; github.com/weidai11/cryptopp
Абсолютно все отправляемые пакеты от анти-чита серверу и получаемые ответы от сервера поступают через функции этих библиотек.

Я не буду описывать подробно их связь, опишу что собирает анти-чит для последующего шифрования и отправки на сервер.

Первым делом анти-чит начинает логгировать те же самые действия, что были до этого, но при этом заворачивает это в json, шифрует и отправляет серверу.

Дешифрованный пакет выглядит следующем образом:

Код:
"{\"detection_information\":\"Status nominal.\",\"detection_status\":0,\"game_files\":[{\"name\":\"appdata.bat\"},{\"name\":\"ConfigTemplates\"},{\"name\":\"CreditsCache.json\"},{\"name\":\"GameAssembly.dll\"},{\"name\":\"license.txt\"},{\"name\":\"log.bat\"},{\"name\":\"log.txt\"},{\"name\":\"mono.msi\"},{\"name\":\"monoinstall.vdf\"},{\"name\":\"readme.txt\"},{\"name\":\"scl-sl.log\"},{\"name\":\"SCPSL.exe\"},{\"name\":\"SCPSL_Data\"},{\"name\":\"SL-AC.dll\"},{\"name\":\"Translations\"},{\"name\":\"Un"
Код:
"{\"detection_information\":\"Initial heartbeating.\",\"detection_status\":0,\"game_files\":[{\"name\":\"appdata.bat\"},{\"name\":\"ConfigTemplates\"},{\"name\":\"CreditsCache.json\"},{\"name\":\"GameAssembly.dll\"},{\"name\":\"license.txt\"},{\"name\":\"log.bat\"},{\"name\":\"log.txt\"},{\"name\":\"mono.msi\"},{\"name\":\"monoinstall.vdf\"},{\"name\":\"readme.txt\"},{\"name\":\"scl-sl.log\"},{\"name\":\"SCPSL.exe\"},{\"name\":\"SCPSL_Data\"},{\"name\":\"SL-AC.dll\"},{\"name\":\"Translations\"},{\"name"
Затем при присоединении к какому-нибудь серверу анти-чит отсылает список загруженных модулей, их размер и базовый адрес

Код:
"\",\"loaded_modules\":[{\"module_base\":140696840306688,\"module_name\":\"C:\\\\Program Files (x86)\\\\Steam\\\\steamapps\\\\common\\\\SCP Secret Laboratory\\\\SCPSL.exe\",\"module_size\":9986048},{\"module_base\":140714089709568,\"module_name\":\""
Не обошлось и без информации о потоках, модуль проходится по всем запущенным раннее потокам, собирая их айди, стартовый адрес, текущий адрес rip и адрес стек поинтера.

Код:
",\"thread_start_address\":140712428775056},{\"thread_id\":1036,\"thread_module_start_address\":140712420179968,\"thread_rip\":140712447511423,\"thread_rsp\":140712447511423,\"thread_start_address\":140712428775056},{\"thread_id\":2252,\"thread_module_start_address\":140712420179968,\"thread_rip\":140712447511423,\"thread_rsp\":140712447511423,\"thread_start_address\":140712428775056},{\"thread_id\":8996,\"thread_module_start_address\":140712420179968,\"thread_rip\":140712447511423,\"thread_rsp\":140712"
Но все эти адреса анти-чит записывает в виде десятичных чисел, если перевести в hex, то получим к примеру

module_base: 140696840306688 -> 00007FF689300000. Базовый адрес SCP:SL
module_size: 9986048 -> 0000000000986000

Также анти-чит после инициализации heartbeating будет неоднократно обращаться к URL
Пожалуйста, авторизуйтесь для просмотра ссылки.
и вызывать GetTickCount64, можете не переходить, потому что провалите проверку на User-Agent и получите 405 ошибку. :roflanEbalo:

Заключение

Первым делом попрошу подписаться на мой бложик:
Пожалуйста, авторизуйтесь для просмотра ссылки.


Благодарю за прочтение данной статьи, я всё ленился делать её, постоянно отвлекался на что-то незначительное и просто ленился, но в один момент мотивация пришла сама ко мне, и так я за пару дней собрал достаточно материала для того, чтобы поведать вам :D
Не прекращу благодарить свою команду Team Enterial: Arting, anarh1st47, Dark_Bull, easton, nelfo57

Надеюсь статья вам понравилась и вы узнали что-то новое для себя!

Блог моей команды:
Пожалуйста, авторизуйтесь для просмотра ссылки.
имбулька от колби
 
Сверху Снизу