BattlEye est principalement un anti-triche allemand tiers, principalement développé par
Bastian Heiko Suter, 32 ans. Il fournit (ou tente de fournir) aux éditeurs de jeux un système anti-triche facile à utiliser qui utilise des mécanismes de protection généraux, ainsi que la détection des tricheurs pour des jeux spécifiques afin d'optimiser la sécurité. Comme indiqué sur le site Web du produit, il reste toujours à la pointe de la technologie moderne et utilise des méthodes innovantes de protection et de détection; évidemment, c'est une conséquence de la nationalité du développeur:
QUALITY MADE IN GERMANY
. BattlEye se compose de nombreux éléments qui travaillent ensemble pour trouver des tricheurs dans les jeux qui ont payé pour l'utilisation du produit. Les quatre éléments principaux sont:
- BEService
- Un service système Windows qui communique avec le serveur BattlEye BEServer , qui fournit une communication client-serveur avec BEDaisy et BEClient .
- BEDaisy
- Un pilote de noyau Windows qui enregistre les mécanismes de traitement préventif des événements et les mini-filtres pour empêcher les tricheurs de modifier illégalement le jeu
- Becient
- Une bibliothèque Windows connectée dynamiquement qui est responsable de la plupart des vecteurs de détection, y compris ceux décrits dans cet article. Après l'initialisation, il s'attache au processus de jeu.
- Beserver
- Serveur backend propriétaire, chargé de collecter des informations et de prendre des mesures spécifiques contre les tricheurs.
Shellcode
Récemment, un vidage du code shell BattlEye a fait surface sur Internet, et nous avons décidé d'écrire sur ce que la version actuelle de BattlEye recherche exactement. Nous n'avons pas analysé BattlEye pendant six mois, donc notre dernier vidage du shellcode est très probablement obsolète. Diverses parties du code ont été récupérées uniquement de la mémoire de ce dernier vidage, en supposant que BattlEye n'a terminé que le code shell et n'a pas supprimé les procédures de détection précédentes.
Comment?
BattlEye aurait diffusé le shellcode de son serveur vers un service Windows appelé BEService. Ce service communique avec le module BEClient situé à l'intérieur du jeu. L'échange de données est effectué via le
\.namedpipeBattleye
et jusqu'en 2018 n'était pas chiffré. Maintenant, toutes les données transmises sont cryptées avec un xor-encryptor avec de très petites clés, ce qui rend extrêmement facile la réalisation d'attaques en clair bien connues. Lorsque le code shell est transmis au client, il est localisé et exécuté en dehors de tous les modules connus, ce qui le rend facile à déterminer. Pour créer un vidage shellcode, vous pouvez soit traiter les fonctions standard de l'API Windows telles que CreateFile, ReadFile, etc., et vider la zone mémoire correspondante de tous les modules appelants (demandant des informations sur la mémoire pour l'adresse renvoyée) qui se trouvent en dehors de tous les modules connus, ou analyser périodiquement l'espace de mémoire virtuelle du jeu à la recherche de mémoire exécutable en dehors de tous les modules connus et la vider sur le disque. Dans le même temps, vous devez garder une trace des zones qui ont déjà été vidées, de sorte que vous n'obtiendrez pas de nombreux vidages identiques.
Explication
Les fragments du pseudocode présentés dans l'article sont
fortement modifiés par souci de beauté. Vous ne pourrez pas vider le code shell BattlEye et reconnaître immédiatement ces parties; le shellcode ne contient pas d'appels de fonction et de nombreux algorithmes de l'article sont déployés. Mais en fait, ce n'est pas important, car lorsque vous aurez fini de lire sur cette terrible antiquité, vous aurez l'occasion de la contourner (:
Tri de la mémoire
Le mécanisme de détection le plus courant dans les anti-tricheurs est l'énumération de la mémoire et l'analyse de la mémoire pour rechercher des images de triche
connues . Il est facile à mettre en œuvre et, comme le montre le passé, avec la bonne approche, est assez efficace si vous n'avez pas oublié les bases de l'assembleur et mis sur liste noire le prologue d'une fonction commune.
Battleye parcourt tout l'espace d'adressage du processus de jeu (le processus actuel dans ce contexte) et effectue diverses vérifications sur les performances de la page et recherche le code shell en dehors de l'espace mémoire correspondant.
Voici comment il est implémenté dans Battleye:
Anomalies de mémoire
BattlEye marque toutes les anomalies dans l'espace d'adressage mémoire, principalement la mémoire des modules exécutables qui ne correspondent pas à l'image chargée:
void memory::anomaly_check(MEMORY_BASIC_INFORMATION memory_information) {
Recherche de motifs
Comme mentionné ci-dessus, BattlEye analyse également la mémoire des processus locaux pour la présence de divers modèles clairement définis, comme le montre la mise en œuvre illustrée ci-dessous.
Lors de la lecture de ce pseudo-code, vous pouvez deviner que
ces contrôles peuvent être contournés en réécrivant la zone de code de chaque module chargé, car ils ne numériseront pas à la recherche de motifs dans les images connues. Afin de ne pas tomber dans les contrôles d'intégrité, vous devez télécharger toutes les zones compressées et sur liste blanche et réécrire les zones de code marquées comme
RWX , car nous ne pouvons pas effectuer de contrôles d'intégrité sans émuler le packer. Dans la version actuelle du shellcode BattlEye, ces modèles de mémoire sont codés en dur:
[05 18] ojectsPUBGChinese [05 17] BattleGroundsPrivate_CheatESP [05 17] [%.0fm] %s [05 3E] 0000Neck0000Chest0000000Mouse 10 [05 3F] PlayerESPColor [05 40] Aimbot: %d02D3E2041 [05 36] HackMachine [05 4A] VisualHacks.net [05 50] 3E232F653E31314E4E563D4276282A3A2E463F757523286752552E6F30584748 [05 4F] DLLInjection-master\x64\Release\ [05 52] NameESP [05 48] Skullhack [05 55] .rdata$zzzdbg [05 39] AimBot [05 39] EB4941803C123F755C623FEB388D41D0FBEC93C977583E930EB683E1DF [05 5F] 55E9 [05 5F] 57E9 [05 5F] 60E9 [05 68] D3D11Present initialised [05 6E] [ %.0fM ] [05 74] [hp:%d]%dm [05 36] 48836424380488D4C2458488B5424504C8BC848894C24304C8BC7488D4C2460 [05 36] 741FBA80000FF15607E0085C07510F2F1087801008B8788100EB [05 36] 40F2AA156F8D2894E9AB4489535D34F9CPOSITION0000COL [05 7A] FFE090 [05 79] %s00%d00POSITION0000COLOR0000000 [05 36] 8E85765DCDDA452E75BA12B4C7B94872116DB948A1DAA6B948A7676BB948902C [05 8A] n<assembly xmlsn='urn:schemas-mi
Ces modèles de mémoire contiennent également un en-tête à deux octets, à savoir la valeur statique inconnue
05
et un identifiant unique.
Ce que nous ne verrons pas, c'est que BattlEye diffuse également de manière dynamique des modèles depuis
BEServer et les envoie à
BEClient , mais nous n'en discuterons pas dans l'article.
Ils sont analysés de manière itérative par l'algorithme suivant:
void memory::pattern_check(void* current_address, MEMORY_BASIC_INFORMATION memory_information) { const auto is_user32 = memory_information.allocation_base == GetModuleHandleA("user32.dll"
Validation de modules spécifiques (Microsoft)
La vérification des modules signale la présence de modules spécifiques chargés dans le jeu:
void memory::module_specific_check_microsoft(MEMORY_BASIC_INFORMATION memory_information) { auto executable = memory_information.protect == PAGE_EXECUTE || memory_information.protect == PAGE_EXECUTE_READ || memory_information.protect == PAGE_EXECUTE_READWRITE auto allocated = memory_information.state == MEM_COMMIT if (!allocated || !executable) continue auto mmres_handle = GetModuleHandleA("mmres.dll" auto mshtml_handle = GetModuleHandleA("mshtml.dll" if (mmres_handle && mmres_handle == memory_information.allocation_base) { battleye_module_anomaly_report module_anomaly_report module_anomaly_report.unknown = 0 module_anomaly_report.report_id = 0x5B module_anomaly_report.identifier = 0x3480 module_anomaly_report.region_size = memory_information.region_size battleye::report(&module_anomaly_report, sizeof(module_anomaly_report), 0 } else if (mshtml_handle && mshtml_handle == memory_information.allocation_base) { battleye_module_anomaly_report module_anomaly_report module_anomaly_report.unknown = 0 module_anomaly_report.report_id = 0x5B module_anomaly_report.identifier = 0xB480 module_anomaly_report.region_size = memory_information.region_size battleye::report(&module_anomaly_report, sizeof(module_anomaly_report), 0 } }
Vérification de modules spécifiques (inconnu)
Une vérification de modules spécifiques a été ajoutée au système, ce qui signale au serveur que vous avez chargé des modules qui répondent à l'
un de ces critères:
void memory::module_specific_check_unknown(MEMORY_BASIC_INFORMATION memory_information) { const auto dos_header = (DOS_HEADER*)module_handle const auto pe_header = (PE_HEADER*)(module_handle + dos_header->e_lfanew const auto is_image = memory_information.state == MEM_COMMIT && memory_information.type == MEM_IMAGE if (!is_image) return const auto is_base = memory_information.base_address == memory_information.allocation_base if (!is_base) return const auto match_1 = time_date_stamp == 0x5B12C900 && *(__int8*)(memory_information.base_address + 0x1000) == 0x00 && *(__int32*)(memory_information.base_address + 0x501000) != 0x353E900 const auto match_2 = time_date_stamp == 0x5A180C35 && *(__int8*)(memory_information.base_address + 0x1000) != 0x00 const auto match_2 = time_date_stamp == 0xFC9B9325 && *(__int8*)(memory_information.base_address + 0x6D3000) != 0x00 if (!match_1 && !match_2 && !match_3) return const auto buffer_offset = 0x00
Nous ne savons pas quels modules répondent à ces critères, mais nous soupçonnons qu'il s'agit d'une tentative de détection d'un ensemble très limité de modules de triche spécifiques.
Addendum: @ how02 nous a informés que le module
action_x64.dll
a un
0x5B12C900
et contient une zone de code dans laquelle vous pouvez écrire; comme mentionné précédemment, cela peut être utilisé pour exploiter.
Protection de la mémoire
BattlEye implémente également une procédure de détection très douteuse, qui, à notre avis, recherche de la mémoire avec le drapeau
PAGE_GUARD défini , sans réellement vérifier si le drapeau
PAGE_GUARD est
défini :
void memory::guard_check(void* current_address, MEMORY_BASIC_INFORMATION memory_information) { if (memory_information.protect != PAGE_NOACCESS) { auto bad_ptr = IsBadReadPtr(current_address, sizeof(temporary_buffer auto read = NtReadVirtualMemory( GetCurrentProcess(), current_address, temporary_buffer, sizeof(temporary_buffer), 0 if (read < 0 || bad_ptr) { auto query = NtQueryVirtualMemory( GetCurrentProcess(), current_address, 0, &new_memory_information, sizeof(new_memory_information), &return_length memory_guard_report.guard = query < 0 || new_memory_information.state != memory_information.state || new_memory_information.protect != memory_information.protect if (memory_guard_report.guard) { memory_guard_report.unknown = 0 memory_guard_report.report_id = 0x21 memory_guard_report.base_address = memory_information.base_address memory_guard_report.region_size = (int)memory_information.region_size memory_guard_report.memory_info = memory_information.type | memory_information.protect | memory_information.state battleye::report(&memory_guard_report, sizeof(memory_guard_report), 0 } } } }
Tri des fenêtres
Le code shell BattlEye itère sur chacune des fenêtres actuellement visibles pendant le jeu, en contournant les fenêtres de haut en bas (par valeur z). Les
GetWindowThreadProcessId
fenêtre à l'intérieur du jeu sont exclus de cette énumération, et cela est déterminé en appelant
GetWindowThreadProcessId
. Par conséquent, vous pouvez lier la fonction correspondante au faux propriétaire de la fenêtre afin que
BattlEye ne vérifie pas votre fenêtre .
void window_handler::enumerate() { for (auto window_handle = GetTopWindow window_handle window_handle = GetWindow(window_handle, GW_HWNDNEXT),
Rechercher une anomalie
Si moins de deux fenêtres sont cochées, une notification est envoyée au serveur. Ceci est probablement fait afin d'empêcher l'application de correctifs aux fonctions correspondantes qui ne permettent pas au code shell BattlEye d'examiner les fenêtres:
void window_handler::check_count() { if (window_handler::windows_enumerated > 1) return
Tri des processus
En appelant
CreateToolhelp32Snapshot
itère sur tous les processus en cours d'exécution, mais
ne traite aucune erreur , ce qui facilite le patch et évite les procédures de détection suivantes:
Vérification du chemin
Si l'image se trouve dans au moins deux sous-répertoires (à partir de la racine du disque), le système marquera les processus si le chemin d'accès à l'image correspondante contient au moins une de ces lignes:
Desktop Temp FileRec Documents Downloads Roaming tmp.ex notepad. ...\. cmd.ex
Si le chemin d'accès au fichier exécutable correspond à l'une de ces lignes, le serveur reçoit une notification concernant le chemin d'accès au fichier exécutable, ainsi que des informations indiquant si le processus parent est l'un des suivants (contient le bit indicateur correspondant envoyé au serveur):
steam.exe [0x01] explorer.exe [0x02] lsass.exe [0x08] cmd.exe [0x10]
Si le client ne peut pas ouvrir le descripteur avec les droits
QueryLimitedInformation
appropriés, il définira le bit d'indicateur
0x04
, si la cause de l'erreur lorsque l'appel
OpenProcess
échoue n'est pas
ERROR_ACCESS_DENIED
, ce qui nous donne le dernier conteneur d'énumération pour la valeur d'indicateur correspondante:
enum BATTLEYE_PROCESS_FLAG { STEAM = 0x1, EXPLORER = 0x2, ERROR = 0x4, LSASS = 0x8, CMD = 0x10 }
Si le processus parent est à la vapeur, le drapeau est instantanément défini pour l'utilisateur et le serveur en est informé avec l'ID de notification
0x40
Nom de l'image
Si le processus répond à l'un des nombreux critères présentés ci-dessous, vous définissez instantanément un indicateur et cela est signalé au serveur avec l'ID de notification
0x38
"Loadlibr" "Rng " "A0E7FFFFFF81" "RNG " "90E54355" "2.6.ex" "TempFile.exe"
Superposition de jeu Steam
BattlEye surveille le processus de superposition du jeu Steam, qui est responsable de la superposition dans le jeu, connue de la plupart des utilisateurs de Steam.
gameoverlayui.exe
est le nom d'hôte complet de la superposition Steam Games; il est connu qu'il est souvent utilisé pour le rendu d'exploits, car il est assez facile de pirater et d'effectuer un rendu illégal dans la fenêtre de jeu. La vérification a la condition suivante:
file size != 0 && image name contains (case insensitive) gameoverlayu
Les vérifications supplémentaires spécifiques à la superposition des jeux Steam sont presque identiques aux procédures effectuées pour le processus de jeu lui-même, par conséquent, sont omises dans le pseudo-code.
Analyse de la mémoire de superposition de vapeur
Le processus de superposition des jeux Steam est analysé à la recherche de modèles et d'anomalies. Nous n'avons pas pu pénétrer plus profondément dans le terrier du lapin et découvrir à quoi servent ces modèles, car ils sont très généralisés et très probablement associés aux modules de triche.
void gameoverlay::pattern_scan(MEMORY_BASIC_INFORMATION memory_information) {
La procédure de scan recherche également toute anomalie sous forme de code exécutable en dehors des images téléchargées, suggérant que des crackers ont injecté le code dans le processus de superposition:
void gameoverlay::memory_anomaly_scan(MEMORY_BASIC_INFORMATION memory_information) {
Protection de superposition de jeux Steam
Si le processus de superposition de jeux Steam est protégé par une protection des processus Windows comme
Light (WinTcb) , le serveur recevra une notification à ce sujet.
void gameoverlay::protection_check(HANDLE process_handle) { auto process_protection = 0 NtQueryInformationProcess( process_handle, ProcessProtectionInformation, &process_protection, sizeof(process_protection), nullptr if (process_protection == 0)
De plus, si l'appel
OpenProcess correspondant renvoie
ERROR_ACCESS_DENIED au processus de superposition, une notification est envoyée concernant l'utilisateur avec l'ID
3B
.
Modules de tri
Les modules de processus de superposition de jeux Steam sont également recherchés, en particulier,
vgui2_s.dll
et
vgui2_s.dll
gameoverlayui.dll
. Plusieurs vérifications sont effectuées pour ces modules, en commençant par
gameoverlayui.dll
.
Si cette condition est remplie:
[gameoverlayui.dll+6C779] == 08BE55DC3CCCCB8????????C3CCCCCC
, le
[gameoverlayui.dll+6C779] == 08BE55DC3CCCCB8????????C3CCCCCC
scanne la vtable à l'adresse stockée en octets
????????
. Si l'un de ces éléments vtable se trouve en dehors du module source gameoverlayui.dll ou pointe vers une instruction
int 3
, l'utilisateur est signalé au serveur avec un ID de notification de
3B
.
void gameoverlay::scan_vtable(HANDLE process_handle, char* buffer, MODULEENTRY32 module_entry) { char function_buffer[16 for (vtable_index = 0 vtable_index < 20 vtable_index += 4) { NtReadVirtualMemory( process_handle, *(int*)&buffer[vtable_index], &function_buffer, sizeof(function_buffer), 0 if (*(int*)&buffer[vtable_index] < module_entry.modBaseAddr || *(int*)&buffer[vtable_index] >= module_entry.modBaseAddr + module_entry.modBaseSize || function_buffer[0] == 0xCC )
Une procédure de vérification spécifique est également effectuée pour le module
vgui2_s.dll
:
void vgui::scan() { if (!equals(vgui_buffer, "6A08B31FF561C8BD??????????FF96????????8BD????????8B1FF90")) { auto could_read = NtReadVirtualMemory( process_handle, module_entry.modBaseAddr + 0x48338, vgui_buffer, 8, 0) >= 0 constexpr auto pattern_offset = 0x48378
Cette procédure vérifie les modifications du décalage 48378
, qui correspond à l'emplacement de la zone de code: push 04 push offset aCBuildslaveSte_4 ; "c:\buildslave\steam_rel_client_win32"... push offset aAssertionFaile_7 ; "Assertion Failed: IsValidIndex(elem)"
La procédure vérifie ensuite une modification très spécifique et apparemment ordonnée: push 04 push 00 push 02 push ??
Nous n'avons pas pu trouver une copie de vgui2_s.dll qui ne correspond pas à la première des deux vérifications ci-dessus, nous ne pouvons donc pas savoir quelle table virtuelle il vérifie.Flux de superposition de vapeur
Les flux en cours de superposition de jeux Steam sont également déplacés: void gameoverlay::check_thread(THREADENTRY32 thread_entry) { const auto tread_handle = OpenThread(THREAD_SUSPEND_RESUME|THREAD_GET_CONTEXT, 0, thread_entry.th32ThreadID if (thread_handle) { suspend_count = ResumeThread(thread_handle if (suspend_count > 0) { SuspendThread(thread_handle gameoverlay_thread_report.unknown = 0 gameoverlay_thread_report.report_id = 0x3B gameoverlay_thread_report.suspend_count = suspend_count battleye::report(&gameoverlay_thread_report, sizeof(gameoverlay_thread_report), 0 } if (GetThreadContext(thread_handle, &context) && context.Dr7) { gameoverlay_debug_report.unknown = 0 gameoverlay_debug_report.report_id = 0x3B gameoverlay_debug_report.debug_register = context.Dr0 battleye::report(&gameoverlay_debug_report, sizeof(gameoverlay_debug_report), 0 } } }
LSASS
L'espace d'adressage mémoire du processus Windows lsass.exe , également appelé processus Local Security Authority, est également analysé et toutes les anomalies sont signalées au serveur, comme dans le cas de deux vérifications précédentes: if (equals(process_entry.executable_path, "lsass.exe")) { auto lsass_handle = OpenProcess(QueryInformation, 0, (unsigned int)process_entry.th32ProcessID if (lsass_handle) { for (address = 0 NtQueryVirtualMemory(lsass_handle, address, 0, &lsass_memory_info, 0x30, &bytes_needed) >= 0 address = lsass_memory_info.base_address + lsass_memory_info.region_size) { if (lsass_memory_info.state == MEM_COMMIT && lsass_memory_info.type == MEM_PRIVATE && (lsass_memory_info.protect == PAGE_EXECUTE || lsass_memory_info.protect == PAGE_EXECUTE_READ || lsass_memory_info.protect == PAGE_EXECUTE_READWRITE)) {
LSASS était auparavant utilisé dans des exploits pour effectuer des opérations avec de la mémoire, car tout processus nécessitant une connexion Internet doit lui fournir un accès LSASS. BattlEye traite actuellement ce problème en effaçant manuellement le descripteur de processus des opérations de lecture / écriture, puis en attachant ReadProcessMemory
/ WriteProcessMemory
, en redirigeant les appels vers son pilote BEDaisy. Ensuite, BEDaisy décide si l'opération de mémoire est légale. S'il pense que l'opération est légale, il la poursuit et allume délibérément la machine sur un écran bleu.Notifications diverses
BattlEye collecte diverses informations et les envoie au serveur avec l'ID de notification 3C
. Ces informations se composent des éléments suivants:- Toute fenêtre avec l'indicateur WS_EX_TOPMOST ou ses analogues:
- (Unicode)
- (Unicode)
- Window style
- Window extended style
- -
- -
- (VM_WRITE|VM_READ)
- :
- ….ContentPaksTslGame-WindowsNoEditor_assets_world.pak
- ….ContentPaksTslGame-WindowsNoEditor_ui.pak
- ….ContentPaksTslGame-WindowsNoEditor_sound.pak
- :
- ….BLGameCookedContentScriptBLGame.u
- NtGetContextThread
NoEye
BattlEye implémente une vérification plutôt paresseuse pour détecter un rootkit accessible au public pour contourner cet anti-triche appelé NoEye: le système utilise GetFileAttributesExA pour vérifier la taille du fichier BE_DLL.dll
si cette bibliothèque se trouve sur le disque. void noeye::detect() { WIN32_FILE_ATTRIBUTE_DATA file_information if (GetFileAttributesExA("BE_DLL.dll", 0, &file_information)) { noeye_report.unknown = 0 noeye_report.report_id = 0x3D noeye_report.file_size = file_information.nFileSizeLow battleye::report(&noeye_report, sizeof(noeye_report), 0 } }
Disponibilité des pilotes
Vérifie les appareils Beep et Null; le cas échéant, une notification est générée. Dans l'état normal, ces deux périphériques ne sont pas disponibles dans le système, ce qui peut indiquer que quelqu'un a allumé manuellement le périphérique. Cette technique est appelée détournement de périphérique du pilote. Cela permet de garantir que les données IOCTL sont échangées avec le pilote malveillant sans avoir besoin d'un objet pilote distinct pour ce pilote. void driver::check_beep() { auto handle = CreateFileA("\\.\Beep", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0 if (handle != INVALID_HANDLE_VALUE) { beep_report.unknown = 0 beep_report.report_id = 0x3E battleye::report(&beep_report, sizeof(beep_report), 0 CloseHandle(handle } }
void driver::check_null() { auto handle = CreateFileA("\\.\Null", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0 if (handle != INVALID_HANDLE_VALUE) { null_report.unknown = 0 null_report.report_id = 0x3E battleye::report(&null_report, sizeof(null_report), 0 CloseHandle(handle } }
Delta du sommeil
De plus, BattlEye peut demander une seconde d'inactivité au thread actuel et mesure la différence dans le nombre de cycles avant et après l'inactivité (sommeil): void sleep::check_delta() { const auto tick_count = GetTickCount Sleep(1000 const auto tick_delta = GetTickCount() - tick_count if (tick_delta >= 1200) { sleep_report.unknown = 0 sleep_report.report_id = 0x45 sleep_report.delta = tick_delta battleye::report(&sleep_report, sizeof(sleep_report), 0 } }
7zip
Un contrôle d'intégrité très paresseux a été ajouté à BattlEye afin que les utilisateurs ne puissent pas charger la bibliothèque 7zip dans les processus de jeu et écraser les zones. Cela a été fait par les utilisateurs pour réduire la gravité des analyses de modèle décrites précédemment et la détection des anomalies. BattlEye a simplement décidé d'ajouter des contrôles d'intégrité pour cette bibliothèque 7zip particulière. void module::check_7zip() { constexpr auto sz_7zipdll = "..\..\Plugins\ZipUtility\ThirdParty\7zpp\dll\Win64\7z.dll" const auto module_handle = GetModuleHandleA(sz_7zipdll if (module_handle && *(int*)(module_handle + 0x1000) != 0xFF1441C7) { sevenzip_report.unknown_1 = 0 sevenzip_report.report_id = 0x46 sevenzip_report.unknown_2 = 0 sevenzip_report.data1 = *(__int64*)(module_handle + 0x1000 sevenzip_report.data2 = *(__int64*)(module_handle + 0x1008 battleye::report(&sevenzip_report, sizeof(sevenzip_report), 0 } }
Couche d'abstraction matérielle
BattlEye vérifie la présence d'une bibliothèque de couche d'abstraction matérielle Windows liée dynamiquement (hal.dll) et indique au serveur si elle est chargée à l'intérieur du jeu. void module::check_hal() { const auto module_handle = GetModuleHandleA("hal.dll" if (module_handle) { hal_report.unknown_1 = 0 hal_report.report_id = 0x46 hal_report.unknown_2 = 2 hal_report.data1 = *(__int64*)(module_handle + 0x1000 hal_report.data2 = *(__int64*)(module_handle + 0x1008 battleye::report(&hal_report, sizeof(hal_report), 0 } }
Vérifications d'image
BattlEye vérifie également diverses images chargées dans le jeu. Ces modules sont censés être des images signées qui sont en quelque sorte manipulées, changeant leur comportement en malveillants, mais nous ne pouvons rien dire de plus à leur sujet, seulement sur leur détection:nvToolsExt64_1
void module::check_nvtoolsext64_1 { const auto module_handle = GetModuleHandleA("nvToolsExt64_1.dll" if (module_handle) { nvtools_report.unknown = 0 nvtools_report.report_id = 0x48 nvtools_report.module_id = 0x5A8 nvtools_report.size_of_image = (PE_HEADER*)(module_handle + (DOS_HEADER*)(module_handle)->e_lfanew))->SizeOfImage battleye::report(&nvtools_report, sizeof(nvtools_report), 0 } }
ws2detour_x96
void module::check_ws2detour_x96 { const auto module_handle = GetModuleHandleA("ws2detour_x96.dll" if (module_handle) { ws2detour_report.unknown = 0 ws2detour_report.report_id = 0x48 ws2detour_report.module_id = 0x5B5 ws2detour_report.size_of_image = (PE_HEADER*)(module_handle + (DOS_HEADER*)(module_handle)->e_lfanew))->SizeOfImage battleye::report(&ws2detour_report, sizeof(ws2detour_report), 0 } }
networkdllx64
void module::check_networkdllx64 { const auto module_handle = GetModuleHandleA("networkdllx64.dll" if (module_handle) { const auto dos_header = (DOS_HEADER*)module_handle const auto pe_header = (PE_HEADER*)(module_handle + dos_header->e_lfanew const auto size_of_image = pe_header->SizeOfImage if (size_of_image < 0x200000 || size_of_image >= 0x400000) { if (pe_header->sections[DEBUG_DIRECTORY].size == 0x1B20) { networkdll64_report.unknown = 0 networkdll64_report.report_id = 0x48 networkdll64_report.module_id = 0x5B7 networkdll64_report.data = pe_header->TimeDatestamp battleye::report(&networkdll64_report, sizeof(networkdll64_report), 0 } } else { networkdll64_report.unknown = 0 networkdll64_report.report_id = 0x48 networkdll64_report.module_id = 0x5B7 networkdll64_report.data = pe_header->sections[DEBUG_DIRECTORY].size battleye::report(&networkdll64_report, sizeof(networkdll64_report), 0 } } }
nxdetours_64
void module::check_nxdetours_64 { const auto module_handle = GetModuleHandleA("nxdetours_64.dll" if (module_handle) { nxdetours64_report.unknown = 0 nxdetours64_report.report_id = 0x48 nxdetours64_report.module_id = 0x5B8 nxdetours64_report.size_of_image = (PE_HEADER*)(module_handle + (DOS_HEADER*)(module_handle)->e_lfanew))->SizeOfImage battleye::report(&nxdetours64_report, sizeof(nxdetours64_report), 0 } }
nvcompiler
void module::check_nvcompiler { const auto module_handle = GetModuleHandleA("nvcompiler.dll" if (module_handle) { nvcompiler_report.unknown = 0 nvcompiler_report.report_id = 0x48 nvcompiler_report.module_id = 0x5BC nvcompiler_report.data = *(int*)(module_handle + 0x1000 battleye::report(&nvcompiler_report, sizeof(nvcompiler_report), 0 } }
wmp
void module::check_wmp { const auto module_handle = GetModuleHandleA("wmp.dll" if (module_handle) { wmp_report.unknown = 0 wmp_report.report_id = 0x48 wmp_report.module_id = 0x5BE wmp_report.data = *(int*)(module_handle + 0x1000 battleye::report(&wmp_report, sizeof(wmp_report), 0 } }
Identificateurs d'énumération de module
Pour référence, nous donnons l'ID d'énumération pour les modules: enum module_id { nvtoolsext64 = 0x5A8, ws2detour_x96 = 0x5B5, networkdll64 = 0x5B7, nxdetours_64 = 0x5B8, nvcompiler = 0x5BC, wmp = 0x5BE
Analyser les tables TCP
Le shellcode BattlEye recherche une liste de connexions TCP pour l'ensemble du système (connue sous le nom de table TCP) et signale si l'utilisateur est connecté à au moins une des adresses IP de passerelle Cloudflare appartenant au site Web payant allemand -cheat appelé xera.ph . Ce mécanisme a été ajouté au code shell pour détecter les utilisateurs dont le lanceur est en cours d'exécution lorsque le jeu est en cours d'exécution, ce qui les rend faciles à reconnaître. Le seul problème avec ce mécanisme est que les adresses IP de la passerelle Cloudflare peuvent changer de propriétaire ultérieurement. et si leur nouveau propriétaire distribue un logiciel qui se connecte à ses serveurs via un port spécifique, alors sans aucun doute de faux déclencheurs anti-triche positifs se produiront. Utilisateurs defournisseurs de services xera.ph payantsPendant longtemps, ils ont signalé qu'ils étaient pris et les développeurs ne peuvent en aucun cas y faire face. Nous avons contacté les développeurs de xera.ph pour les informer de leur comportement stupide, mais ils nous ont mal compris et nous ont envoyé une copie gratuite, sans penser que nous pourrions pirater et publier. Nous ne le ferons pas, mais vous ne devriez probablement pas envoyer gratuitement des fichiers binaires propriétaires sous licence aux personnes impliquées dans le reverse engineering et vous attendre à ce qu'elles ne soient pas piratées. void network::scan_tcp_table { memset(local_port_buffer, 0, sizeof(local_port_buffer for (iteration_index = 0 iteration_index < 500 ++iteration_index) {
Types de notifications
Voici pour référence tous les types de notifications connus du code shell: enum BATTLEYE_REPORT_ID { MEMORY_GUARD = 0x21, MEMORY_SUSPICIOUS = 0x2F, WINDOW_TITLE = 0x33, MEMORY = 0x35, PROCESS_ANOMALY = 0x38, DRIVER_BEEP_PRESENCE = 0x3E, DRIVER_NULL_PRESENCE = 0x3F, MISCELLANEOUS_ANOMALY = 0x3B, PROCESS_SUSPICIOUS = 0x40, LSASS_MEMORY = 0x42, SLEEP_ANOMALY = 0x45, MEMORY_MODULE_SPECIFIC = 0x46, GENERIC_ANOMALY = 0x48, MEMORY_MODULE_SPECIFIC2 = 0x5B, }