Changements dans l'antichita BattlEye populaire et moyens de les contourner


Mises à jour majeures du code shell BattlEye


Le temps passe, les anti-cheats changent, et pour augmenter l'efficacité du produit, des fonctions y apparaissent et disparaissent. Il y a un an, j'ai préparé une description détaillée du shellcode BattlEye dans mon blog , et cette partie de l'article sera un simple reflet des changements apportés au shellcode.

Horodatages sur liste noire


Dans une récente analyse de BattlEye, il n'y avait que deux horodatages de compilation dans la liste des interdictions fantômes, et il semble que les développeurs aient décidé d'en ajouter beaucoup plus:

0x5B12C900 (action_x64.dll)
0x5A180C35 (TerSafe.dll, Epic Games)
0xFC9B9325 (?)
0x456CED13 (d3dx9_32.dll)
0x46495AD9 (d3dx9_34.dll)
0x47CDEE2B (d3dx9_32.dll)
0x469FF22E (d3dx9_35.dll)
0x48EC3AD7 (D3DCompiler_40.dll)
0x5A8E6020 (?)
0x55C85371 (d3dx9_32.dll)
0x456CED13 (?)
0x46495AD9 (D3DCompiler_40.dll)
0x47CDEE2B (D3DX9_37.dll)
0x469FF22E (?)
0x48EC3AD7 (?)
0xFC9B9325 (?)
0x5A8E6020 (?)
0x55C85371 (?)


Je n'ai pas pu identifier les horodatages restants, et les deux 0xF ******* sont les hachages créés par les assemblys déterministes Visual Studio. Merci à @mottikraus et T0B1 d'avoir identifié des horodatages.

Vérifications des modules


Comme l'analyse principale l'a montré, la caractéristique clé de BattlEye est l'énumération des modules, et à partir du moment de la dernière analyse, un autre module a été ajouté à la liste:

 void battleye::misc::module_unknown1() { if (!GetProcAddress(current_module, "NSPStartup")) return; if (optional_header.data_directory[4].size == 0x1B20 || optional_header.data_directory[4].size == 0xE70 || optional_header.data_directory[4].size == 0x1A38 || timestamp >= 0x5C600000 && timestamp < 0x5C700000) { report_module_unknown report = {}; report.unknown = 0; report.report_id = 0x35; report.val1 = 0x5C0; report.timestamp = timestamp; report.image_size = optional_header.size_of_image; report.entrypoint = optional_header.address_of_entry_point; report.directory_size = optional_header.data_directory[4].size; battleye::report(&report, sizeof(report), false); } } 

Il s'agit probablement de la détection de certaines DLL de proxy, car la taille de la table de redirection est vérifiée ici.

Titres de fenêtres


Dans l'analyse précédente, divers fournisseurs de triche ont été marqués avec des noms de fenêtre, mais depuis lors, le shellcode a cessé de vérifier ces en-têtes de fenêtre. La liste des titres des fenêtres a été complètement remplacée par:

Chod's
Satan5


Noms des images


BattlEye est connu pour utiliser des méthodes de détection très primitives, et l'une d'entre elles est une liste noire de noms d'images. Chaque année, la liste des noms d'images interdits s'allonge, et au cours des 11 derniers mois, cinq nouveaux ont été ajoutés:

frAQBc8W.dll
C:\\Windows\\mscorlib.ni.dll
DxtoryMM_x64.dll
Project1.dll
OWClient.dll

Il est à noter que la présence d'un module avec un nom correspondant à l'un des éléments de la liste ne signifie pas que vous serez immédiatement banni. Le moteur de génération de rapports transmet également des informations de module de base, qui sont très probablement utilisées pour distinguer les tricheurs des collisions sur le serveur BattlEye.

7 zips


7-Zip a été largement utilisé et continue d'être utilisé par les participants à la scène de triche comme remplissage de mémoire pour les vides de code (codes-caves). BattlEye essaie de résoudre ce problème en effectuant un très mauvais contrôle d'intégrité, qui a changé depuis mon article précédent:

 void module::check_7zip() { const auto module_handle = GetModuleHandleA("..\\..\\Plugins\\ZipUtility\\ThirdParty\\7zpp\\dll\\Win64\\7z.dll"); // --- REMOVED --- // if (module_handle && *(int*)(module_handle + 0x1000) != 0xFF1441C7) // --- ADDED --- if (module_handle && *(int*)(module_handle + 0x1008) != 0x83485348) { 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), false); } } 

Il semble que les développeurs de BattlEye aient deviné que mon article précédent a conduit de nombreux utilisateurs à contourner cette vérification en copiant simplement les octets souhaités à l'emplacement vérifié par BattlEye. Comment ont-ils réglé la situation? Nous avons décalé la vérification de huit octets et avons continué à utiliser la même mauvaise méthode de vérification de l'intégrité. La partition exécutable en lecture seule, et tout ce que vous avez à faire est de télécharger 7-Zip à partir du disque et de comparer les partitions déplacées entre elles; s'il y a des écarts, alors quelque chose ne va pas. Sérieusement, les gars, effectuer des vérifications d'intégrité n'est pas si difficile.

Vérification du réseau


Énumérer la table TCP fonctionne toujours, mais après avoir publié une analyse précédente qui a critiqué les développeurs pour avoir signalé les adresses IP Cloudflare, ils ont toujours supprimé cette vérification. Anti-cheat rapporte toujours le port que xera.ph utilise pour la connexion, mais les développeurs ont ajouté une nouvelle vérification pour déterminer si le processus avec la connexion a une protection active (probablement cela se fait en utilisant le gestionnaire).

 void network::scan_tcp_table { memset(local_port_buffer, 0, sizeof(local_port_buffer); for (iteration_index = 0; iteration_index; < 500 ++iteration_index) { // GET NECESSARY SIZE OF TCP TABLE auto table_size = 0; GetExtendedTcpTable(0, &table_size, false, AF_INET, TCP_TABLE_OWNER_MODULE_ALL, 0); // ALLOCATE BUFFER OF PROPER SIZE FOR TCP TABLE auto allocated_ip_table = (MIB_TCPTABLE_OWNER_MODULE*)malloc(table_size); if (GetExtendedTcpTable(allocated_ip_table, &table_size, false, AF_INET, TCP_TABLE_OWNER_MODULE_ALL, 0) != NO_ERROR) goto cleanup; for (entry_index = 0; entry_index < allocated_ip_table->dwNumEntries; ++entry_index) { // --- REMOVED --- // const auto ip_address_match_1 = // allocated_ip_table->table[entry_index].dwRemoteAddr == 0x656B1468; // 104.20.107.101 // // const auto ip_address_match_2 = // allocated_ip_table->table[entry_index].dwRemoteAddr == 0x656C1468; // 104.20.108.101 // +++ ADDED +++ const auto target_process = OpenProcess(QueryLimitedInformation, 0, ip_table->table[entry_index].dwOwningPid); const auto protected = target_process == INVALID_HANDLE && GetLastError() == 0x57; if (!protected) { CloseHandle(target_process); return; } const auto port_match = allocated_ip_table->table[entry_index].dwRemotePort == 20480; for (port_index = 0; port_index < 10 && allocated_ip_table->table[entry_index].dwLocalPort != local_port_buffer[port_index]; ++port_index) { if (local_port_buffer[port_index]) continue tcp_table_report.unknown = 0; tcp_table_report.report_id = 0x48; tcp_table_report.module_id = 0x5B9; tcp_table_report.data = BYTE1(allocated_ip_table->table[entry_index].dwLocalPort) | (LOBYTE(allocated_ip_table->table[entry_index.dwLocalPort) << 8; battleye::report(&tcp_table_report, sizeof(tcp_table_report), false); local_port_buffer[port_index] = allocated_ip_table->table[entry_index].dwLocalPort; break } } cleanup: // FREE TABLE AND SLEEP free(allocated_ip_table); Sleep(10 } } 

Merci IChooseYou et résumé

BattlEye Stack Bypass


Les jeux de piratage sont un jeu constant de chat et de souris, donc les rumeurs de nouveaux tours se propagent comme un feu. Dans cette partie, nous examinerons les nouvelles techniques heuristiques qui ont été récemment ajoutées à notre arsenal par un grand fournisseur d'anti-triche BattlEye. Le plus souvent, ces techniques sont appelées marche en pile. Habituellement, ils sont implémentés en traitant une fonction et en parcourant la pile pour savoir qui a spécifiquement appelé cette fonction. Pourquoi avez-vous besoin de faire ça? Comme tout autre programme, les hacks de jeux vidéo ont un ensemble de fonctions bien connues qu'ils utilisent pour obtenir des informations à partir du clavier, les afficher sur la console ou calculer certaines expressions mathématiques. De plus, les hacks de jeux vidéo adorent cacher leur existence, que ce soit en mémoire ou sur disque, afin que les logiciels anti-triche ne les trouvent pas. Mais ce que les programmes de triche oublient, c'est qu'ils appellent régulièrement des fonctions d'autres bibliothèques, et cela peut être utilisé pour détecter heuristiquement des tricheurs inconnus. En implémentant le moteur de traversée de pile pour des fonctions telles que std::print , nous pouvons trouver ces tricheurs même s'ils sont masqués.

BattlEye a mis en place un «contournement de pile», malgré le fait que cela n'a pas été annoncé publiquement et au moment de la publication de l'article, il n'y avait que des rumeurs. Faites attention aux guillemets - ce que vous verrez ici n'est pas vraiment un vrai tour de pile, mais juste une combinaison de vérification de l'adresse de retour et du vidage du programme appelant. Une véritable implémentation de traversée de pile traverserait la pile et générerait une vraie pile d'appels.

Comme je l'ai expliqué dans un article précédent sur BattlEye, le système anti-triche diffuse dynamiquement le shellcode dans le jeu lorsqu'il est en cours d'exécution. Ces codes shell ont des tailles et des tâches différentes et ne sont pas transmis simultanément. Une propriété remarquable d'un tel système est que les chercheurs doivent analyser dynamiquement l'anti-triche pendant le match multijoueur, ce qui complique la détermination des caractéristiques de cet anti-triche. Il permet également à l'anti-triche d'appliquer diverses mesures à différents utilisateurs, par exemple, pour transférer un module plus profondément invasif uniquement à une personne qui a un taux inhabituellement élevé de meurtres et de décès, etc.

L'un de ces codes shell, BattlEye, est chargé d'effectuer cette analyse de pile; nous l'appellerons shellcode8kb car il est légèrement plus petit que shellcodemain , que j'ai documenté ici . Ce petit code shell utilisant la fonction AddVectoredExceptionHandler prépare un gestionnaire d'exceptions vectorisé, puis définit des interruptions d'interruption sur les fonctions suivantes:

GetAsyncKeyState
GetCursorPos
IsBadReadPtr
NtUserGetAsyncKeyState
GetForegroundWindow
CallWindowProcW
NtUserPeekMessage
NtSetEvent
sqrtf
__stdio_common_vsprintf_s
CDXGIFactory::TakeLock
TppTimerpExecuteCallback


Pour ce faire, il parcourt simplement la liste des fonctions standard, en définissant la première instruction de la fonction correspondante sur int3 , qui est utilisée comme point d'arrêt. Après avoir défini un point d'arrêt, tous les appels à la fonction correspondante passent par le gestionnaire d'exceptions, qui a un accès complet aux registres et à la pile. Ayant cet accès, le gestionnaire d'exceptions vide l'adresse du programme appelant depuis le haut de la pile, et si l'une des conditions heuristiques est remplie, 32 octets de la fonction appelante sont vidés et envoyés au serveur BattlEye avec l'identifiant de rapport 0x31 :

 __int64 battleye::exception_handler(_EXCEPTION_POINTERS *exception) { if (exception->ExceptionRecord->ExceptionCode != STATUS_BREAKPOINT) return 0; const auto caller_function = *(__int64 **)exception->ContextRecord->Rsp; MEMORY_BASIC_INFORMATION caller_memory_information = {}; auto desired_size = 0; // QUERY THE MEMORY PAGE OF THE CALLER const auto call_failed = NtQueryVirtualMemory( GetCurrentProcess(), caller_function, MemoryBasicInformation, &caller_memory_information, sizeof(caller_memory_information), &desired_size) < 0; // IS THE MEMORY SOMEHOW NOT COMMITTED? (WOULD SUGGEST VAD MANIPULATIUON) const auto non_commit = caller_memory_information.State != MEM_COMMIT; // IS THE PAGE EXECUTABLE BUT DOES NOT BELONG TO A PROPERLY LOADED MODULE? const auto foreign_image = caller_memory_information.Type != MEM_IMAGE && caller_memory_information.RegionSize > 0x2000; // IS THE CALL BEING SPOOFED BY NAMAZSO? const auto spoof = *(_WORD *)caller_function == 0x23FF; // jmp qword ptr [rbx] // FLAG ALL ANBORMALITIES if (call_failed || non_commit || foreign_image || spoof) { report_stack.unknown = 0; report_stack.report_id = 0x31; report_stack.hook_id = hook_id; report_stack.caller = (__int64)caller_function; report_stack.function_dump[0] = *caller_function; report_stack.function_dump[1] = caller_function[1]; report_stack.function_dump[2] = caller_function[2]; report_stack.function_dump[3] = caller_function[3]; if (!call_failed) { report_stack.allocation_base = caller_memory_information.AllocationBase; report_stack.base_address = caller_memory_information.BaseAddress; report_stack.region_size = caller_memory_information.RegionSize; report_stack.type_protect_state = caller_memory_information.Type | caller_memory_information.Protect | caller_memory_information.State; } battleye::report(&report_stack, sizeof(report_stack), false); return -1; } } 

Comme nous pouvons le voir, le gestionnaire d'exceptions vide toutes les fonctions appelantes en cas de changement non cérémonieux dans la page mémoire ou lorsque la fonction n'appartient pas à un module de processus connu (le type de page mémoire MEM_IMAGE n'a pas été défini par les mappeurs manuels). Il décharge également les fonctions d'appel lorsqu'il ne parvient pas à appeler NtQueryVirtualMemory afin que les tricheurs ne se lient pas à cet appel système et masquent leur module du vidage de la pile. La dernière condition est en fait assez intéressante, elle marque toutes les fonctions d'appel qui utilisent le gadget jmp qword ptr [rbx] - la méthode utilisée pour "usurper l'adresse de retour". Il a été publié par mon surnom de membre co-secrétaire namazso. Il semble que les développeurs de BattlEye ont vu que les gens utilisent cette méthode d'usurpation d'identité dans leurs jeux et ont décidé de viser directement. Il convient de mentionner ici que la méthode décrite par namazsos fonctionne bien, utilisez simplement un gadget différent, ou complètement différent, ou simplement un registre différent - cela n'a pas d'importance.

Conseil du développeur BattlEye: Le CDXGIFactory::TakeLock dans votre mémoire est incorrect car vous avez (accidentellement ou intentionnellement) activé le remplissage CC, qui est très différent à chaque compilation. Pour une compatibilité maximale, vous devez supprimer le rembourrage (le premier octet de la signature) et vous aurez donc probablement plus de tricheurs :)

La structure complète envoyée au serveur BattlEye ressemble à ceci:

 struct __unaligned battleye_stack_report { __int8 unknown; __int8 report_id; __int8 val0; __int64 caller; __int64 function_dump[4]; __int64 allocation_base; __int64 base_address; __int32 region_size; __int32 type_protect_state; }; 

Reconnaissance de l'hyperviseur dans BattlEye


Le jeu du chat et de la souris dans le domaine des jeux de piratage continue d'être une source d'innovation dans les exploits et la lutte contre les tricheurs. L'utilisation de la technologie de virtualisation dans les jeux de piratage a commencé à se développer activement après l'avènement d'hyperviseurs aussi faciles à utiliser que DdiMon Satoshi Tanda et hvpp Peter Benes. Ces deux projets sont utilisés par la plupart des tricheurs rémunérés de la scène des hackers souterrains en raison du seuil d'entrée bas et de la documentation détaillée. Ces versions devraient accélérer la course aux armements dans le domaine des hyperviseurs, qui commence maintenant à se manifester dans la communauté des hackers de jeux. Voici ce que l'administrateur de l'une des plus grandes communautés de piratage de jeux avec le surnom wlan dit à propos de cette situation:

Avec l'avènement des systèmes d'hyperviseur prêts à l'emploi pour les jeux de piratage, il est devenu inévitable que les anti-tricheurs comme BattlEye se concentrent sur la reconnaissance généralisée de la virtualisation.

L'utilisation répandue d'hyperviseurs est due aux récentes améliorations de l'anti-triche, qui ont laissé aux pirates très peu d'opportunités de modifier les jeux de manière traditionnelle. La popularité des hyperviseurs peut s'expliquer par la simplicité d'éviter l'anti-triche, car la virtualisation simplifie le masquage des informations à l'aide de mécanismes tels que les hooks d'appel système et la virtualisation MMU .

Récemment, BattlEye a implémenté la reconnaissance des hyperviseurs courants comme les plates-formes mentionnées ci-dessus (DdiMon, hvpp) en utilisant la détection basée sur le temps. Cette reconnaissance tente de détecter des valeurs de temps d'instruction CPUID non standard. CPUID est une instruction relativement peu coûteuse sur un équipement réel, ne nécessitant généralement que deux cents cycles, et dans un environnement virtuel, son exécution peut prendre dix fois plus longtemps en raison d'opérations inutiles causées par le moteur d'introspection. Le moteur d'introspection est différent d'un équipement réel, qui effectue simplement l'opération de la manière attendue, car sur la base d'un critère arbitraire, il suit et modifie conditionnellement les données renvoyées à l'invité.

Fait amusant: CPUID est activement utilisé dans ces procédures de reconnaissance temporaire car il s'agit d'une instruction avec une sortie inconditionnelle, ainsi que d'une instruction avec une sérialisation non privilégiée. Cela signifie que le CPUID est utilisé comme une barrière et garantit le respect des instructions avant et après; en même temps, les horaires deviennent indépendants de la réorganisation habituelle des instructions. Vous pouvez également utiliser des instructions telles que XSETBV , qui effectuent également une sortie inconditionnelle, mais pour garantir une synchronisation indépendante, cela nécessitera une sorte d'instruction de barrière afin qu'aucune réorganisation ne se produise avant ou après, ce qui affecte la fiabilité des temporisations.

La reconnaissance


Voici la procédure de reconnaissance du module BattlEye «BEClient2»; J'ai effectué sa rétro-ingénierie et recréé le code en pseudo-C, puis je l'ai posté sur Twitter . Le lendemain de mon tweet, les développeurs de BattlEye ont changé de façon inattendue l'obscurcissement de BEClient2, espérant apparemment que cela m'empêcherait d'analyser le module. L'obfuscation précédente n'a pas changé depuis plus d'un an, mais a changé le lendemain de mon tweet à ce sujet - une vitesse impressionnante.

 void battleye::take_time() { // SET THREAD PRIORITY TO THE HIGHEST const auto old_priority = SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); // CALCULATE CYCLES FOR 1000MS const auto timestamp_calibrator = __rdtsc(); Sleep(1000); const auto timestamp_calibration = __rdtsc() - timestamp_calibrator; // TIME CPUID auto total_time = 0; for (std::size_t count = 0; count < 0x6694; count++) { // SAVE PRE CPUID TIME const auto timestamp_pre = __rdtsc(); std::uint32_t cpuid_data[4] = {}; __cpuid(cpuid_data, 0); // SAVE THE DELTA total_time += __rdtsc() - timestamp_pre; } // SAVE THE RESULT IN THE GLOBAL REPORT TABLE battleye::report_table[0x1A8] = 10000000 * total_time / timestamp_calibration / 0x65; // RESTORE THREAD PRIORITY SetThreadPriority(GetCurrentThread(), old_priority); } 

Comme je l'ai dit ci-dessus, c'est la technique de reconnaissance la plus courante utilisant des instructions interceptées inconditionnellement. Cependant, il est vulnérable au faux temps, et nous en parlerons en détail dans la section suivante.

Contournement de reconnaissance


Cette méthode de reconnaissance a des problèmes. Premièrement, il est sujet à un faux temps, ce qui se fait généralement de deux manières: en décalant le TSC dans VMCS ou en diminuant le TSC à chaque exécution du CPUID. Il existe de nombreuses autres façons de gérer les attaques basées sur le temps, mais ces dernières sont beaucoup plus faciles à implémenter, car vous pouvez garantir que le temps d'exécution de l'instruction se situera dans un ou deux cycles d'horloge de synchronisation d'exécution sur un équipement réel. La difficulté de découvrir cette technique de contrefaçon temporelle dépend de l'expérience du développeur. Dans la section suivante, nous examinerons la contrefaçon temporelle et l'amélioration de l'implémentation créée dans BattlEye. La deuxième raison de ce défaut de méthode de reconnaissance est que le délai CPUID (runtime) dans différents processeurs est très différent selon la valeur de la feuille. Cela peut prendre jusqu'à 70 à 300 cycles d'horloge pour se terminer. Le troisième problème avec cette procédure de reconnaissance est d'utiliser SetThreadPriority. Cette fonction Windows est utilisée pour définir la valeur de priorité d'un descripteur de flux donné, cependant, le système d'exploitation n'écoute pas toujours la demande. Cette fonction est simplement une suggestion pour augmenter la priorité du thread, et il n'y a aucune garantie que cela se produira. Ainsi, il est possible que cette méthode soit affectée par des interruptions ou d'autres processus.

Dans ce cas, il est facile de contourner la reconnaissance, et la technique décrite de contrefaçon de temps déjoue efficacement cette méthode de reconnaissance. Si les développeurs de BattlEye souhaitent améliorer cette méthode, la section suivante fournit quelques recommandations.

Amélioration


Cette fonctionnalité peut être améliorée de plusieurs manières. Tout d'abord, vous pouvez désactiver intentionnellement les interruptions et forcer la priorité d'un thread en modifiant CR8 en IRQL le plus élevé. Il serait également idéal d'isoler cette vérification dans un cœur de processeur. Autre amélioration: vous devez utiliser différents temporisateurs, mais beaucoup d'entre eux ne sont pas aussi précis que TSC, mais il existe un tel temporisateur appelé APERF, ou Actual Performance Clock. Je recommande ce temporisateur car il est plus difficile de tricher avec lui et il n'accumule un compteur que lorsque le processeur logique est à l'état d'alimentation C0. Il s'agit d'une excellente alternative à l'utilisation de TSC. Vous pouvez également utiliser l'ACPI, HPET, le minuteur PIT, le minuteur GPU, le minuteur NTP ou le minuteur PPERF, qui est similaire à APERF, mais compte les mesures perçues comme des instructions d'exécution. L'inconvénient de ceci est que vous devez activer HWP, qui peut être désactivé par l'opérateur intermédiaire, et donc il est inutile.

Voici une version améliorée de la procédure de reconnaissance qui devrait être effectuée dans le noyau:

 void battleye::take_time() { std::uint32_t cpuid_regs[4] = {}; _disable(); const auto aperf_pre = __readmsr(IA32_APERF_MSR) << 32; __cpuid(&cpuid_regs, 1); const auto aperf_post = __readmsr(IA32_APERF_MSR) << 32; const auto aperf_diff = aperf_post - aperf_pre; // CPUID IET ARRAY STORE // BATTLEYE REPORT TABLE STORE _enable(); } 

Remarque: IET signifie Instruction Execution Time.

Cependant, la procédure peut toujours être très peu fiable pour détecter les hyperviseurs courants, car les temps d'exécution CPUID peuvent varier considérablement.Il serait préférable de comparer l'IET des deux instructions. L'un d'eux devrait avoir un délai d'exécution plus long que le CPUID. Par exemple, il peut s'agir de FYL2XP1 - une instruction arithmétique qui prend un peu plus de temps à compléter que l'IET moyen de l'instruction CPUID. De plus, il ne provoque aucun piège dans l'hyperviseur et son temps peut être mesuré de manière fiable. En utilisant ces deux fonctions, la fonction de profilage pourrait créer un tableau pour stocker les instructions IET CPUID et FYL2XP1. En utilisant le temporisateur APERF, il serait possible d'obtenir l'horloge initiale d'une instruction arithmétique, d'exécuter l'instruction et de calculer le delta de l'horloge pour celle-ci. Les résultats pourraient être stockés dans la matrice IET pour N cycles de profilage, obtenant la valeur moyenne et répétant le processus pour le CPUID. Si le temps d'exécution de l'instruction CPUID est plus long que l'instruction arithmétique,il s'agit alors d'un signe fiable que le système est virtuel, car une instruction arithmétique ne peut en aucun cas passer plus de temps que l'exécution du CPUID pour obtenir des informations sur le fabricant ou la version. Une telle procédure de reconnaissance pourra également détecter ceux utilisant le décalage / mise à l'échelle TSC.

Je répète, les développeurs devraient forcer l'activation de la liaison au cœur de calcul pour effectuer cette vérification sur un seul cœur, désactiver les interruptions et forcer IRQL à définir la valeur maximale pour garantir des données cohérentes et fiables. Il serait surprenant que les développeurs de BattlEye décident de l'implémenter, car cela nécessite beaucoup plus d'efforts. Dans le pilote du noyau, BattlEye mange deux autres routines de reconnaissance de machine virtuelle, mais c'est un sujet pour un autre article.

Source: https://habr.com/ru/post/fr484420/


All Articles