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