Ich habe erfahren, dass die dänische Regierung nicht nur
das Programm Digital Exam Monitor, das wir im
vorherigen Artikel analysiert und vollständig umgangen haben,
ausgesetzt hat , sondern dieses System möglicherweise eine Woche, nachdem wir sie über die Hacking-Methode informiert hatten,
vollständig heruntergefahren hat . Ich möchte nicht glauben, dass die dänische Regierung die Idee der Überwachung von Prüfungen nur wegen uns aufgegeben hat, aber unsere Arbeit wurde deutlich wahrgenommen.
In diesem Artikel werden die technischen Details der Funktionsweise eines anderen Tools zur Verfolgung von Schulkindern beschrieben: ExamCookie. Wenn Sie nur das System umgehen möchten, scrollen Sie zum entsprechenden Abschnitt.
ExamCookie
Vor kurzem kam dieses Tool aufgrund einer Untersuchung des Verstoßes gegen die DSGVO in die Nachrichten. Wir haben uns entschlossen, während der Prüfungen einen Blick auf den zweitgrößten Konkurrenten des oben genannten Schulverfolgungssystems zu
werfen :
ExamCookie . Dies ist ein kommerzielles Tracking-System, das von mehr als 20 dänischen Schulen verwendet wird. Auf der Website gibt es keine andere Dokumentation als die folgende Beschreibung:
ExamCookie ist eine einfache Software, die die Computeraktivität des Schülers während der Prüfung überwacht, um sicherzustellen, dass die Regeln eingehalten werden. Das Programm verbietet Studenten, illegale Hilfe in Anspruch zu nehmen.
ExamCookie speichert alle Aktivitäten auf dem Computer: aktive URLs, Netzwerkverbindungen, Prozesse, Zwischenablage und Screenshots beim Ändern der Fenstergröße.
Das Programm funktioniert einfach: Wenn Sie an der Prüfung teilnehmen, führen Sie sie auf Ihrem Computer aus und überwachen Ihre Aktivitäten. Wenn die Prüfung abgeschlossen ist, wird das Programm geschlossen und Sie können es vom Computer entfernen.
Um mit der Nachverfolgung zu beginnen, müssen Sie Ihr UNI-Login verwenden, das auf verschiedenen Bildungsseiten funktioniert, oder Anmeldeinformationen manuell eingeben. Wir haben das Tool nicht verwendet, daher können wir nicht sagen, in welchen Fällen die manuelle Eingabe verwendet wird. Möglicherweise wird dies für Studenten durchgeführt, die kein UNI-Login haben, was wir nicht für möglich halten.
Binäre Informationen
Das Programm kann von der ExamCookie-Homepage heruntergeladen werden. Es ist eine x86 .NET-Anwendung. Als Referenz hat der analysierte binäre MD5-Hash
63AFD8A8EC26C1DC368D8FF8710E337D
Signatur EXAMCOOKIE APS vom 24. April 2019. Wie der
letzte Artikel gezeigt hat, kann die Analyse der .NET-Binärdatei kaum als Reverse Engineering bezeichnet werden, da die Kombination aus einfach zu lesendem IL-Code und Metadaten einen perfekten Quellcode liefert.
Im Gegensatz zum vorherigen Überwachungsprogramm haben die Entwickler dieses Tools es nicht nur aus dem Debug-Protokoll gelöscht, sondern auch verschleiert. Zumindest haben sie es versucht :-)
Verschleierung (Lachen zu Tränen)
Als wir die Anwendung in dnSpy öffneten, bemerkten wir schnell einen fehlenden Einstiegspunkt:
Seltsamerweise wird normalerweise eine Art Wrapper angenommen, der die Körper der Methoden vom Konstruktor des Moduls ändert, der zum eigentlichen Einstiegspunkt läuft. Mal sehen:
Cool. Es ist 2019 und die Leute benutzen immer noch Confuser (Ex).
Wir haben diesen Dekomprimierungscode sofort erkannt und die Assembler-Header überprüft:
[Modul: ConfusedBy ("Confuser.Core 1.1.0 + a36320377a")]
Im Moment dachten wir, dass der Code tatsächlich verschleiert sein würde, da der obige Konstruktor die Körper und Ressourcen der Methode entschlüsselt. Zu unserer Überraschung entschied sich der Entwickler der Verschleierung jedoch, die Metadaten nicht umzubenennen:
Dies tötet das ganze Summen des Reverse Engineering. Wie bereits in einem
früheren Artikel erwähnt , möchte ich auf das eigentliche Problem eines ordnungsgemäß geschützten und qualitativ hochwertigen Überwachungsinstruments stoßen, dessen Analyse mehr als fünf Minuten dauern wird.
In jedem Fall ist das Entpacken einer durch confuser (ex) geschützten Binärdatei sehr einfach: Verwenden Sie den Dumper für .NET-Binärdateien oder den Haltepunkt für die break-Anweisung in <MODULE> .ctor und sichern Sie sich. Der Vorgang dauert 30 Sekunden, und dieser Packer bleibt immer mein Favorit, da der Schutz vor dem Debuggen überhaupt
nicht funktioniert .
Wir haben uns für MegaDumper entschieden: Dies ist etwas schneller als manuelles Dumping:
Nach dem Speichern der ExamCookie-Binärdatei sollte die folgende Meldung angezeigt werden:
Jetzt haben Sie ein Verzeichnis mit allen Assembler-Fragmenten, die in den entsprechenden Prozess geladen werden, diesmal mit entschlüsselten Methodenkörpern.
Wer auch immer diese Verschleierung implementiert hat, Gott sei Dank, hat zumindest die Zeilen verschlüsselt:
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]); }
Ja, der gute alte String-Verschlüsselungs-Confuser (Ex), die beste Pseudo-Sicherheit in der Welt von .NET. Es ist gut, dass Confuser (Ex) so oft gehackt wurde, dass im Internet für jeden Mechanismus Deobfuscation-Tools verfügbar sind, sodass wir nichts mit .NET zu tun haben. Führen Sie den ConfuserExStringDecryptor von
CodeCracker auf dem Binärdump aus:
Das vorherige Snippet wird folgendermaßen konvertiert:
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]); }
Das ist der gesamte Schutz der Anwendung, der in weniger als einer Minute unterbrochen wurde. Wir werden unsere Tools hier nicht veröffentlichen, da wir sie nicht entwickelt haben und keinen Quellcode haben. Aber jeder, der den Job wiederholen möchte, kann sie auf
Tuts4You finden . Wir haben kein tuts4you-Konto mehr, daher können wir keine Verknüpfung zu den Spiegeln herstellen.
Funktionalität
Überraschenderweise wurde keine echte "versteckte Funktionalität" gefunden. Wie auf der Website angegeben, werden die folgenden Informationen regelmäßig an den Server gesendet:
- Prozessliste (alle 5000 ms)
- Aktive Anwendung (alle 1000 ms)
- Zwischenablage (alle 500 ms)
- Screenshot (alle 5000 ms)
- Liste der Netzwerkadapter (alle 20.000 ms)
Der Rest der Anwendung ist sehr langweilig, daher haben wir beschlossen, den gesamten Initialisierungsvorgang zu überspringen und direkt zu den Funktionen zu wechseln, die für die Erfassung von Informationen verantwortlich sind.
Adapter
Netzwerkadapter werden von der Funktion .NET
NetworkInterface.GetAllNetworkInterfaces()
genau wie im
vorherigen Artikel zusammengestellt :
NetworkInterface[] allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); foreach (NetworkInterface networkInterface in allNetworkInterfaces) { try {
Aktive Anwendung
Das wird interessant. Anstatt alle geöffneten Fenster zu registrieren, steuert das Dienstprogramm nur die aktive Anwendung. Die Implementierung ist übertrieben, daher präsentieren wir einen schönen Pseudocode:
var whiteList = { "devenv", "ExamCookie.WinClient", "ExamCookie.WinClient.vshost", "wermgr", "ShellExperienceHost" };
Großartig ... Leute verwenden immer noch Prozessnamen, um sie zu unterscheiden. Sie hören nie auf und denken nicht: „Moment mal, Sie können die Namen der Prozesse nach Belieben ändern“, damit wir diesen Schutz sicher umgehen können.
Wenn Sie einen vorherigen Artikel über ein anderes Prüfungsverfolgungsprogramm lesen, werden Sie diese unterdurchschnittliche Implementierung für Browser wahrscheinlich erkennen:
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 ""; }
Und die Kirsche auf dem Kuchen:
private static string GetBrowserUrlById(object processId, string name) {
Dies ist buchstäblich die gleiche Implementierung wie im vorherigen Artikel. Es ist schwer zu verstehen, dass die Entwickler immer noch nicht erkannt haben, wie schlimm es ist. Jeder kann die URL im Browser bearbeiten, dies ist nicht einmal eine Demonstration wert.
Erkennung virtueller Maschinen
Im Gegensatz zu den Aussagen der Website setzt das Starten in einer virtuellen Maschine ein Flag. Die Implementierung ist ... interessant.
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"; } }
Aus irgendeinem Grund schreiben sie eine externe Binärdatei auf die Festplatte und führen sie aus. Dann verlassen sie sich vollständig auf die E / A-Ergebnisse. Das passiert wirklich ziemlich oft, aber die Übertragung solch wichtiger Arbeiten auf einen anderen ungeschützten Prozess ist mittelmäßig. Mal sehen, mit welcher Datei wir es zu tun haben:
Also verwenden wir jetzt C ++? Nun, Interoperabilität ist eigentlich nicht unbedingt schlecht. Und das kann bedeuten, dass wir jetzt wirklich am Reverse Engineering arbeiten müssen (!!). Schauen wir uns die IDA an:
int __cdecl main(int argc, const char **argv, const char **envp) { int v3;
Dadurch wird das Vorhandensein des VMWare-E / A-Ports 'VX' überprüft:
int __fastcall vm_detect::vmware_port() { int result;
Als nächstes wird die Ausführung der Anweisung zur
Erweiterung des
virtuellen PCs überprüft, die nur beim Start in einer virtualisierten Umgebung funktionieren sollte, wenn sie bei falscher Verarbeitung nicht zu einem Maschinenabsturz führt;):
char vm_detect::vpcext() { char result;
... kein echtes Reverse Engineering, nur 30 Sekunden, um zwei Funktionen umzubenennen :(
Dieses Programm liest einfach den Registrierungsschlüssel und führt zwei Hypervisor-Überprüfungen durch, die im Vergleich zu ihrem anderen Programm seltsam aussehen. Ich frage mich, wo sie es kopiert haben? Oh, schauen Sie, ein Artikel mit dem Titel
"Methoden zum Erkennen virtueller (sic) Maschinen" , der diese Methoden erklärt :). In jedem Fall können diese Erkennungsvektoren umgangen werden, indem Sie die .vmx-Datei bearbeiten oder eine erweiterte Version eines Hypervisors Ihrer Wahl verwenden.
Datenschutz
Wie bereits erwähnt, läuft derzeit eine Untersuchung wegen Nichteinhaltung der DSGVO. Auf der
Website heißt es:
Die Daten werden verschlüsselt und an einen sicheren Microsoft Azure-Server gesendet, auf den nur mit den richtigen Anmeldeinformationen zugegriffen werden kann. Nach der Prüfung werden die Daten bis zu drei Monate gespeichert.
Wir sind uns nicht ganz sicher, wie sie die "Sicherheit" des Servers bestimmen, da die Anmeldeinformationen in der Anwendung fest codiert und in den Metadatenressourcen in vollständig klarem Text gespeichert sind:
Endpunkt: https://examcookiewinapidk.azurewebsites.net
Benutzername: VfUtTaNUEQ
Passwort: AwWE9PHjVc
Wir haben den Inhalt des Servers nicht untersucht (dies ist illegal), können jedoch davon ausgehen, dass dort vollständiger Zugriff gewährt wird. Da das Konto in der Anwendung fest codiert ist, besteht keine Isolation zwischen den Schülerdatencontainern.
Rechtliche Hinweise: Wir behalten uns das Recht vor, API-Anmeldeinformationen zu veröffentlichen, da diese in einer öffentlichen Binärdatei gespeichert sind und daher nicht illegal abgerufen werden. Die Verwendung mit böswilliger Absicht verstößt jedoch eindeutig gegen das Gesetz. Wir empfehlen daher dringend, dass die Leser die oben genannten Anmeldeinformationen in keiner Weise verwenden und nicht für mögliche Handlungen verantwortlich sind.Umgehen
Da diese Anwendung unglaublich an Digital Exam Monitor erinnert, haben wir gerade den
Ayyxam- Code aktualisiert, um ExamCookie zu unterstützen.
Prozessliste
Die .NET-Prozessschnittstelle speichert Prozessdaten intern mithilfe des
ntdll!NtQuerySystemInformation
. Das Ausblenden von Prozessen erfordert einige Arbeit, da an vielen Stellen Informationen über den Prozess angezeigt werden. Glücklicherweise ruft .NET nur einen bestimmten Informationstyp ab, sodass Sie nicht alle
Latebros- Methoden verwenden müssen.
Code zur Umgehung der Validierung aktiver Prozesse.
NTSTATUS WINAPI ayyxam::hooks::nt_query_system_information( SYSTEM_INFORMATION_CLASS system_information_class, PVOID system_information, ULONG system_information_length, PULONG return_length) {
Puffer
ole32.dll!OleGetClipboard
, das sehr anfällig für Hooks ist, ist für die interne Implementierung von Puffern in .NET verantwortlich. Anstatt viel Zeit mit der Analyse interner Strukturen zu verbringen, können Sie einfach
S_OK
, und die .NET-Fehlerbehandlung
S_OK
den Rest:
std::int32_t __stdcall ayyxam::hooks::get_clipboard(void* data_object[[maybe_unused]]) {
Dadurch wird der gesamte Puffer vor dem ExamCookie-Überwachungstool ausgeblendet, ohne die Funktionalität des Programms zu beeinträchtigen.
Screenshots
Wie immer nehmen die Leute eine fertige .NET-Implementierung der gewünschten Funktion. Um diese Funktion zu umgehen, mussten wir im vorherigen Code nicht einmal etwas ändern. Screenshots werden von der
Graphics.CopyFromScreen
.NET-Funktion gesteuert. Es ist im Wesentlichen ein Wrapper zum Übertragen von
gdi32!BitBlt
, der
gdi32!BitBlt
. Wie in Videospielen zur Bekämpfung von Anti-Cheat-Systemen, die Screenshots machen, können wir den BitBlt-Hook verwenden und unerwünschte Informationen ausblenden, bevor wir einen Screenshot machen.
Websites öffnen
Die Grabber-URL wird vollständig aus dem vorherigen Programm kopiert, sodass wir unseren Code erneut verwenden können, um den Schutz zu umgehen. Im
letzten Artikel haben wir die AutomationElement-Struktur dokumentiert, wodurch der folgende Hook gestartet wird:
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)
Erkennung virtueller Maschinen
Die verzögerte Erkennung einer virtuellen Maschine kann auf zwei Arten umgangen werden: 1) ein Patch eines Programms, das auf die Festplatte geschrieben wird; oder 2) eine Umleitung des Prozesserstellungsprozesses zu einer Dummy-Anwendung. Letzteres scheint deutlich einfacher :).
Process.Start()
ruft
Process.Start()
CreateProcess
es einfach ein und leiten Sie es an eine beliebige Dummy-Anwendung weiter, die das Zeichen '0' druckt.
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 ) {
Herunterladen
Das gesamte Projekt ist im
Github-Repository verfügbar. Das Programm fügt die x86-Binärdatei in den entsprechenden Prozess ein.