Suivi des examens: ExamCookie

J'ai appris que le gouvernement danois n'a pas simplement suspendu le programme Digital Exam Monitor, que nous avons analysé et complètement contourné dans l' article précédent , mais a peut-être complètement arrêté ce système une semaine après que nous les ayons informés de la méthode de piratage. Je ne veux pas penser que c'est uniquement à cause de nous que le gouvernement danois a rejeté l'idée de contrôler les examens, mais notre travail a été clairement remarqué.

Dans cet article, nous décrirons les détails techniques du fonctionnement d'un autre outil de suivi des écoliers: ExamCookie. Si vous souhaitez uniquement contourner le système, faites défiler la page jusqu'à la section appropriée.

ExamCookie


Récemment, cet outil a fait la une des journaux suite à une enquête sur la violation du RGPD. Nous avons décidé de jeter un œil au deuxième plus grand concurrent du système de suivi scolaire susmentionné lors des examens: ExamCookie . Il s'agit d'un système de suivi commercial utilisé par plus de 20 écoles danoises. Il n'y a pas de documentation sur le site autre que la description suivante:

ExamCookie est un logiciel simple qui surveille l'activité informatique de l'élève pendant l'examen pour s'assurer que les règles sont respectées. Le programme interdit aux étudiants d'utiliser toute forme d'aide illégale.

ExamCookie enregistre toutes les activités sur l'ordinateur: URL actives, connexions réseau, processus, presse-papiers et captures d'écran lors du redimensionnement de la fenêtre.

Le programme fonctionne simplement: en entrant l'examen, vous l'exécutez sur votre ordinateur et il surveille votre activité. Une fois l'examen terminé, le programme se ferme et vous pouvez le supprimer de l'ordinateur.

Pour commencer le suivi, vous devez utiliser votre identifiant UNI, qui fonctionne sur divers sites éducatifs, ou saisir manuellement les informations d'identification. Nous n'avons pas utilisé l'outil, nous ne pouvons donc pas dire dans quels cas la saisie manuelle est utilisée. Peut-être que cela est fait pour les étudiants qui n'ont pas de connexion UNI, ce que nous ne considérons pas comme possible.



Information binaire


Le programme peut être téléchargé à partir de la page d'accueil d'ExamCookie. Il s'agit d'une application x86 .NET. Pour référence, le hachage MD5 binaire analysé 63AFD8A8EC26C1DC368D8FF8710E337D signature EXAMCOOKIE APS datée du 24 avril 2019. Comme le dernier article l'a montré, l'analyse du binaire .NET peut difficilement être appelée rétro-ingénierie, car la combinaison de code IL et de métadonnées faciles à lire fournit un code source parfait.

Contrairement au programme de surveillance précédent, les développeurs de cet outil l'ont non seulement supprimé du journal de débogage, mais l'ont également obscurci. Au moins, ils ont essayé :-)

Obfuscation (rire aux larmes)


Lorsque nous avons ouvert l'application dans dnSpy, nous avons rapidement remarqué un point d'entrée manquant:

 // Token: 0x0600003D RID: 61 RVA: 0x00047BB0 File Offset: 0x00045FB0 [STAThread] [DebuggerHidden] [EditorBrowsable(EditorBrowsableState.Advanced)] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] internal static void Main(string[] Args) { } 

Étrange, une sorte de wrapper est généralement supposée, elle modifie les corps des méthodes du constructeur du module, qui s'exécute jusqu'au point d'entrée réel, voyons:

 // Token: 0x06000001 RID: 1 RVA: 0x00058048 File Offset: 0x00055048 static <Module>() { <Module>.\u206B\u202B\u200B\u206F\u206C\u202D\u200D\u200E\u202D\u206B\u206F\u206F\u202C\u202A\u206B\u202E\u202A\u206C\u202A\u206C\u200B\u206A\u202D\u206C\u202C\u206C\u200F\u202C\u206C\u202C\u200C\u206A\u200C\u206C\u200B\u206B\u202B\u206E\u202C\u202B\u202E(); <Module>.\u206C\u200D\u200F\u200E\u200C\u200C\u200F\u200F\u206E\u206A\u206A\u200B\u202C\u206A\u206B\u200D\u206E\u200E\u202D\u206B\u202C\u206C\u202D\u206D\u200C\u200F\u206E\u200F\u206E\u206A\u202B\u206B\u200E\u206B\u202E\u206F\u206A\u202E\u202C\u202A\u202E(); <Module>.\u200B\u202D\u200F\u200F\u202A\u206D\u202C\u206B\u206E\u202A\u206F\u206C\u200D\u200C\u202D\u200F\u202B\u202C\u202B\u206D\u206D\u202D\u206E\u200D\u206D\u206A\u202A\u202C\u200C\u206F\u206B\u206E\u200D\u202E\u206F\u200C\u206B\u200E\u206D\u206A\u202E(); } 

Cool. Nous sommes en 2019 et les gens utilisent toujours Confuser (Ex).

Nous avons instantanément reconnu ce code de décompression et vérifié les en-têtes d'assembleur:

  [module: ConfusedBy ("Confuser.Core 1.1.0 + a36320377a")] 

Pour le moment, nous pensions que le code serait en fait obscurci, car le constructeur susmentionné décrypte les corps et les ressources de la méthode. Mais, à notre grande surprise, le développeur de l'obfuscation a décidé ... de ne pas renommer les métadonnées:



Cela tue tout le buzz de la rétro-ingénierie. Comme nous l'avons dit dans un article précédent , je voudrais rencontrer le vrai problème d'un outil de surveillance de haute qualité et correctement protégé, dont l'analyse prendra plus de cinq minutes.

Dans tous les cas, décompresser tout binaire protégé par confuser (ex) est très simple: utilisez le dumper de binaires .NET ou le point d'arrêt de l'instruction break dans <MODULE> .ctor et videz-vous. Le processus prend 30 secondes, et ce packer restera toujours mon préféré, car la protection contre le débogage ne fonctionne jamais du tout .

Nous avons décidé d'utiliser MegaDumper: c'est un peu plus rapide que le dumping manuel:



Après avoir vidé le binaire ExamCookie, le message suivant devrait apparaître:



Vous avez maintenant un répertoire avec tous les fragments d'assembleur qui sont chargés dans le processus correspondant, cette fois avec les corps de méthode décryptés.

Celui qui a mis en œuvre cet obscurcissement, Dieu merci, au moins il a chiffré les lignes:

 else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.SymbolicLink)) { Module1.DebugPrint(<Module>.smethod_5<string>(1582642794u), new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.Tiff)) { Module1.DebugPrint(<Module>.smethod_2<string>(4207351461u), new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.UnicodeText)) { Module1.DebugPrint(<Module>.smethod_5<string>(3536903244u), new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.WaveAudio)) { Module1.DebugPrint(<Module>.smethod_2<string>(2091555364u), new object[0]); } 

Oui, le bon vieux chiffreur de chaîne Confuser (Ex), la meilleure pseudo-sécurité dans le monde de .NET. Il est bon que Confuser (Ex) ait été piraté si souvent que des outils de désobfuscation soient disponibles sur Internet pour chaque mécanisme, donc nous ne toucherons à rien lié à .NET. Exécutez ConfuserExStringDecryptor à partir de CodeCracker sur le vidage binaire:



Il convertit l'extrait précédent en ceci:

 else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.SymbolicLink)) { Module1.DebugPrint("ContainsData.SymbolicLink", new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.Tiff)) { Module1.DebugPrint("ContainsData.Tiff", new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.UnicodeText)) { Module1.DebugPrint("ContainsData.UnicodeText", new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.WaveAudio)) { Module1.DebugPrint("ContainsData.WaveAudio", new object[0]); } 

C'est toute la protection de l'application, cassée en moins d'une minute ... Nous ne publierons pas nos outils ici, car nous ne les avons pas développés et nous n'avons pas de code source. Mais quiconque veut répéter le travail peut les trouver sur Tuts4You . Nous n'avons plus de compte tuts4you, nous ne pouvons donc pas établir de lien avec les miroirs.

Fonctionnalité


Étonnamment, aucune véritable "fonctionnalité cachée" n'a été trouvée. Comme indiqué sur le site Internet, les informations suivantes sont périodiquement envoyées au serveur:

  • Liste des processus (toutes les 5000 ms)
  • Application active (toutes les 1000 ms)
  • Presse-papiers (toutes les 500 ms)
  • Capture d'écran (toutes les 5000 ms)
  • Liste des adaptateurs réseau (toutes les 20 000 ms)

Le reste de l'application est très ennuyeux, nous avons donc décidé de sauter toute la procédure d'initialisation et d'aller directement aux fonctions chargées de capturer les informations.

Adaptateur


Les adaptateurs réseau sont assemblés par la fonction .NET NetworkInterface.GetAllNetworkInterfaces() , exactement comme dans l' article précédent :

 NetworkInterface[] allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); foreach (NetworkInterface networkInterface in allNetworkInterfaces) { try { // ... // TEXT FORMATTING OMITTED // ... dictionary.Add(networkInterface.Id, stringBuilder.ToString()); stringBuilder.Clear(); } catch (Exception ex) { AdapterThread.OnExceptionEventHandler onExceptionEvent = this.OnExceptionEvent; if (onExceptionEvent != null) { onExceptionEvent(ex); } } } result = dictionary; 

Application active


Cela devient intéressant. Au lieu d'enregistrer toutes les fenêtres ouvertes, l'utilitaire contrôle uniquement l'application active. L'implémentation est exagérée, nous présentons donc un pseudocode magnétisé:

 var whiteList = { "devenv", "ExamCookie.WinClient", "ExamCookie.WinClient.vshost", "wermgr", "ShellExperienceHost" }; // GET WINDOW INFORMATION var foregroundWindow = ApplicationThread.GetForegroundWindow(); ApplicationThread.GetWindowRect(foregroundWindow, ref rect); ApplicationThread.GetWindowThreadProcessId(foregroundWindow, ref processId); var process = Process.GetProcessById(processId); if (process == null) return; // LOG BROWSER URL if (IsBrowser(process)) { var browserUrl = UiAutomation32.GetBrowserUrl(process.Id, process.ProcessName); // SEND BROWSER URL TO SERVER if (ValidBrowserUrl(browserUrl)) { ReportToServer(browserUrl); } } else if (!whiteList.contains(process.ProcessName, StringComparer.OrdinalIgnoreCase)) { ReportToServer(process.MainWindowTitle); } 

Génial ... les gens utilisent toujours des noms de processus pour les différencier. Ils ne s'arrêtent jamais et ne pensent pas: «Attendez une minute, vous pouvez changer les noms des processus comme vous le souhaitez», afin que nous puissions contourner cette protection en toute sécurité.

Si vous lisez un article précédent sur un autre programme de suivi des examens, vous reconnaîtrez probablement cette implémentation inférieure aux navigateurs:

 private bool IsBrowser(System.Diagnostics.Process proc) { bool result; try { string left = proc.ProcessName.ToLower(); if (Operators.CompareString(left, "iexplore", false) != 0 && Operators.CompareString(left, "chrome", false) != 0 && Operators.CompareString(left, "firefox", false) != 0 && Operators.CompareString(left, "opera", false) != 0 && Operators.CompareString(left, "cliqz", false) != 0) { if (Operators.CompareString(left, "applicationframehost", false) != 0) { result = false; } else { result = proc.MainWindowTitle.Containing("Microsoft Edge"); } } else { result = true; } } catch (Exception ex) { result = false; } return result; } 

 private string GetBrowserName(string name) { if (Operators.CompareString(name.ToLower(), "iexplore", false) == 0) { return "IE-Explorer"; } else if (Operators.CompareString(name.ToLower(), "chrome", false) == 0) { return "Chrome"; } else if (Operators.CompareString(name.ToLower(), "firefox", false) == 0) { return "Firefox"; } else if (Operators.CompareString(name.ToLower(), "opera", false) == 0) { return "Opera"; } else if (Operators.CompareString(name.ToLower(), "cliqz", false) == 0) { return "Cliqz"; } else if (Operators.CompareString(name.ToLower(), "applicationframehost", false) == 0) { return "Microsoft Edge"; } return ""; } 

Et la cerise sur le gâteau:

 private static string GetBrowserUrlById(object processId, string name) { // ... automationElement.GetCurrentPropertyValue(/*...*/); return url; } 

Il s'agit littéralement de la même implémentation que dans l'article précédent. Il est difficile de comprendre comment les développeurs n'ont toujours pas réalisé à quel point c'est mauvais. Tout le monde peut modifier l'URL dans le navigateur, cela ne vaut même pas la peine d'être démontré.

Découverte de machine virtuelle


Contrairement à ce que dit le site Web, le démarrage dans une machine virtuelle définit un indicateur. La mise en œuvre est ... intéressante.

 File.WriteAllBytes("ecvmd.exe", Resources.VmDetect); using (Process process = new Process()) { process.StartInfo = new ProcessStartInfo("ecvmd.exe", "-d") { CreateNoWindow = true, UseShellExecute = false, RedirectStandardOutput = true }; process.Start(); try { using (StreamReader standardOutput = process.StandardOutput) { result = standardOutput.ReadToEnd().Replace("\r\n", ""); } } catch (Exception ex3) { result = "-5"; } } 

Eh bien, pour une raison quelconque, ils écrivent un binaire externe sur le disque et l'exécutent, puis s'appuient entièrement sur les résultats d'E / S. Cela se produit vraiment assez souvent, mais le transfert d'un travail aussi important vers un autre processus non protégé est moyen. Voyons quel fichier nous traitons:



Alors maintenant, nous utilisons C ++? Eh bien, l'interopérabilité n'est pas nécessairement mauvaise. Et cela peut signifier que nous devons maintenant vraiment travailler sur la rétro-ingénierie (!!). Regardons l'IDA:

 int __cdecl main(int argc, const char **argv, const char **envp) { int v3; // ecx BOOL v4; // ebx int v5; // ebx int *v6; // eax int detect; // eax bool vbox_key_exists; // bl char vpcext; // bh char vmware_port; // al char *vmware_port_exists; // ecx char *vbox_detected; // edi char *vpcext_exists; // esi int v14; // eax int v15; // eax int v16; // eax int v17; // eax int v18; // eax int v20; // [esp+0h] [ebp-18h] HKEY result; // [esp+Ch] [ebp-Ch] HKEY phkResult; // [esp+10h] [ebp-8h] if ( argc != 2 ) goto LABEL_20; v3 = strcmp(argv[1], "-d"); if ( v3 ) v3 = -(v3 < 0) | 1; if ( !v3 ) { v4 = (unsigned __int8)vm_detect::vmware_port() != 0; result = 0; v5 = (vm_detect::vpcext() != 0 ? 2 : 0) + v4; RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\ACPI\\DSDT\\VBOX__", 0, 0x20019u, &result); v6 = sub_402340(); LABEL_16: sub_404BC0((int)v6, v20); return 0; } detect = strcmp(argv[1], "-s"); if ( detect ) detect = -(detect < 0) | 1; if ( !detect ) { LABEL_20: phkResult = 0; vbox_key_exists = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\ACPI\\DSDT\\VBOX__", 0, 0x20019u, &phkResult) == 0; vpcext = vm_detect::vpcext(); vmware_port = vm_detect::vmware_port(); vmware_port_exists = "1"; vbox_detected = "1"; if ( !vbox_key_exists ) vbox_detected = "0"; vpcext_exists = "1"; if ( !vpcext ) vpcext_exists = "0"; if ( !vmware_port ) vmware_port_exists = "0"; result = (HKEY)vmware_port_exists; v14 = std::print((int)&dword_433310, "VMW="); v15 = std::print(v14, (const char *)result); v16 = std::print(v15, ",VPC="); v17 = std::print(v16, vpcext_exists); v18 = std::print(v17, ",VIB="); v6 = (int *)std::print(v18, vbox_detected); goto LABEL_16; } return 0; } 

Cela vérifie la présence du port d'E / S VMWare 'VX':

 int __fastcall vm_detect::vmware_port() { int result; // eax result = __indword('VX'); LOBYTE(result) = 0; return result; } 

Ensuite, l'exécution de l'instruction d' extension de PC virtuel est vérifiée, ce qui ne devrait fonctionner que lorsqu'elle est lancée dans un environnement virtualisé, si elle ne conduit pas à un plantage de la machine si elle est traitée incorrectement;):

 char vm_detect::vpcext() { char result; // al result = 1; __asm { vpcext 7, 0Bh } return result; } 

... pas de véritable ingénierie inverse, seulement 30 secondes pour renommer deux fonctions :(

Ce programme lit simplement la clé de registre et exécute deux vérifications d'hyperviseur qui semblent étranges par rapport à leur autre programme. Je me demande où ils l'ont copié? Oh, regardez, un article intitulé "Méthodes pour découvrir des machines virtuelles (sic)" qui explique ces méthodes :). Dans tous les cas, ces vecteurs de détection peuvent être contournés en modifiant le fichier .vmx ou en utilisant une version améliorée de n'importe quel hyperviseur de votre choix.

Protection des données


Comme mentionné précédemment, une enquête est en cours pour non-respect du RGPD, et leur site Web indique:

Les données sont chiffrées et envoyées à un serveur Microsoft Azure sécurisé, accessible uniquement avec les informations d'identification correctes. Après l'examen, les données sont conservées jusqu'à trois mois.

Nous ne savons pas exactement comment ils déterminent la «sécurité» du serveur, car les informations d'identification sont codées en dur dans l'application et stockées en texte complètement clair dans les ressources de métadonnées:

  Point de terminaison: https://examcookiewinapidk.azurewebsites.net
 Nom d'utilisateur: VfUtTaNUEQ
 Mot de passe: AwWE9PHjVc 

Nous n'avons pas examiné le contenu du serveur (c'est illégal), mais nous pouvons supposer qu'un accès complet y est fourni. Étant donné que le compte est codé en dur dans l'application, il n'y a pas d'isolement entre les conteneurs de données des étudiants.

Mentions légales: Nous nous réservons le droit de publier les informations d'identification de l'API car elles sont stockées dans un fichier binaire public et ne sont donc pas obtenues illégalement. Cependant, leur utilisation avec une intention malveillante viole clairement la loi, par conséquent, nous recommandons fortement aux lecteurs de ne pas utiliser les informations d'identification susmentionnées de quelque manière que ce soit et ne sont pas responsables des actions potentielles.

Contourner


Étant donné que cette application rappelle incroyablement le moniteur d'examen numérique, nous venons de mettre à jour le code ayyxam pour prendre en charge ExamCookie.

Liste des processus


L'interface de processus .NET met en cache en interne les données de processus à l'aide de l'appel système ntdll!NtQuerySystemInformation . Pour lui cacher des processus nécessite un certain travail, car les informations sur le processus sont indiquées à de nombreux endroits. Heureusement, .NET ne récupère qu'un type spécifique d'informations, vous n'avez donc pas à utiliser toutes les méthodes de Latebros .

Code pour contourner la validation des processus actifs.

 NTSTATUS WINAPI ayyxam::hooks::nt_query_system_information( SYSTEM_INFORMATION_CLASS system_information_class, PVOID system_information, ULONG system_information_length, PULONG return_length) { // DONT HANDLE OTHER CLASSES if (system_information_class != SystemProcessInformation) return ayyxam::hooks::original_nt_query_system_information( system_information_class, system_information, system_information_length, return_length); // HIDE PROCESSES const auto value = ayyxam::hooks::original_nt_query_system_information( system_information_class, system_information, system_information_length, return_length); // DONT HANDLE UNSUCCESSFUL CALLS if (!NT_SUCCESS(value)) return value; // DEFINE STRUCTURE FOR LIST struct SYSTEM_PROCESS_INFO { ULONG NextEntryOffset; ULONG NumberOfThreads; LARGE_INTEGER Reserved[3]; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ImageName; ULONG BasePriority; HANDLE ProcessId; HANDLE InheritedFromProcessId; }; // HELPER FUNCTION: GET NEXT ENTRY IN LINKED LIST auto get_next_entry = [](SYSTEM_PROCESS_INFO* entry) { return reinterpret_cast<SYSTEM_PROCESS_INFO*>( reinterpret_cast<std::uintptr_t>(entry) + entry->NextEntryOffset); }; // ITERATE AND HIDE PROCESS auto entry = reinterpret_cast<SYSTEM_PROCESS_INFO*>(system_information); SYSTEM_PROCESS_INFO* previous_entry = nullptr; for (; entry->NextEntryOffset > 0x00; entry = get_next_entry(entry)) { constexpr auto protected_id = 7488; if (entry->ProcessId == reinterpret_cast<HANDLE>(protected_id) && previous_entry != nullptr) { // SKIP ENTRY previous_entry->NextEntryOffset += entry->NextEntryOffset; } // SAVE PREVIOUS ENTRY FOR SKIPPING previous_entry = entry; } return value; } 

Tampon


ole32.dll!OleGetClipboard , très sensible aux hooks, est responsable de l'implémentation interne des tampons dans .NET. Au lieu de passer beaucoup de temps à analyser les structures internes, vous pouvez simplement retourner S_OK , et la gestion des erreurs .NET fera le reste:

 std::int32_t __stdcall ayyxam::hooks::get_clipboard(void* data_object[[maybe_unused]]) { // LOL return S_OK; } 

Cela masquera la totalité du tampon de l'outil de surveillance ExamCookie sans perturber la fonctionnalité du programme.

Captures d'écran


Comme toujours, les utilisateurs prennent une implémentation .NET prête à l'emploi de la fonction souhaitée. Pour contourner cette fonction, nous n'avons même pas eu à changer quoi que ce soit dans le code précédent. Les captures d'écran sont contrôlées par la fonction Graphics.CopyFromScreen .NET. Il s'agit essentiellement d'un wrapper pour la transmission de blocs de bits, qui appelle gdi32!BitBlt . Comme dans les jeux vidéo pour lutter contre les systèmes anti-triche qui prennent des captures d'écran, nous pouvons utiliser le crochet BitBlt et masquer toutes les informations indésirables avant de prendre une capture d'écran.


Ouverture de sites


L'URL de saisie est entièrement copiée du programme précédent, nous pouvons donc à nouveau réutiliser notre code pour contourner la protection. Dans le dernier article, nous avons documenté la structure AutomationElement, à la suite de laquelle le hook suivant est lancé:

 std::int32_t __stdcall ayyxam::hooks::get_property_value(void* handle, std::int32_t property_id, void* value) { constexpr auto value_value_id = 0x755D; if (property_id != value_value_id) return ayyxam::hooks::original_get_property_value(handle, property_id, value); auto result = ayyxam::hooks::original_get_property_value(handle, property_id, value); if (result != S_OK) // SUCCESS? return result; // VALUE URL IS STORED AT 0x08 FROM VALUE STRUCTURE class value_structure { public: char pad_0000[8]; //0x0000 wchar_t* value; //0x0008 }; auto value_object = reinterpret_cast<value_structure*>(value); // ZERO OUT OLD URL std::memset(value_object->value, 0x00, std::wcslen(value_object->value) * 2); // CHANGE TO GOOGLE.COM constexpr wchar_t spoofed_url[] = L"https://google.com"; std::memcpy(value_object->value, spoofed_url, sizeof(spoofed_url)); return result; } 

Découverte de machine virtuelle


La détection paresseuse d'une machine virtuelle peut être contournée de deux manières: 1) un patch d'un programme qui est vidé sur le disque; ou 2) une redirection du processus de création de processus vers une application factice. Ce dernier semble clairement plus simple :). Donc, en interne, Process.Start() appelle CreateProcess , alors accrochez-le et redirigez-le vers n'importe quelle application factice qui imprime le caractère «0».

 BOOL WINAPI ayyxam::hooks::create_process( LPCWSTR application_name, LPWSTR command_line, LPSECURITY_ATTRIBUTES process_attributes, LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD creation_flags, LPVOID environment, LPCWSTR current_directory, LPSTARTUPINFOW startup_information, LPPROCESS_INFORMATION process_information ) { // REDIRECT PATH OF VMDETECT TO DUMMY APPLICATION constexpr auto vm_detect = L"ecvmd.exe"; if (std::wcsstr(application_name, vm_detect)) { application_name = L"dummy.exe"; } return ayyxam::hooks::original_create_process( application_name, command_line, process_attributes, thread_attributes, inherit_handles, creation_flags, environment, current_directory, startup_information, process_information); } 

Téléchargez


L'ensemble du projet est disponible dans le référentiel Github . Le programme fonctionne en injectant le binaire x86 dans le processus correspondant.

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


All Articles