Malicious Code: Würmer, Viren und Co Proseminar im Sommer
Transcription
Malicious Code: Würmer, Viren und Co Proseminar im Sommer
Malicious Code: Würmer, Viren und Co Proseminar im Sommer Semester 09 Disassembling Philipp Willmertinger Technische Universität München 25.5.2009 Zusammenfassung Disassembling ist eine Technik, mit der der binär (bzw. hexadezimal) codierte Maschinencode eines ausführbaren Programms zurück in Assemblercode portiert werden kann. Assemblercode ist für Menschen lesbar. Deshalb kann der genaue Ablauf eines Programms nachvollzogen oder sogar geändert werden. In dieser Seminararbeit werden das Disassembling und die dafür nötigen Grundlagen erklärt und Anwendungsgebiete dieser Technik, vor allem in Bezug auf das Erkennen von bösartigem Code, aufgezeigt. 1 Inhaltsverzeichnis 1 Einleitung 3 2 Ein Programm erzeugen 2.1 Compiling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Assembling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 4 3 Assemblercode auf einer x86-Systemstruktur 3.1 Maschinenstruktur . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Assemblercode . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 4 5 4 Programm ausführen 6 5 Disassembling 5.1 Definition . . . . . . . . . . . . . . . . . . 5.2 Softwarelösungen . . . . . . . . . . . . . . 5.2.1 OllyDbg . . . . . . . . . . . . . . . 5.2.2 IDA Pro . . . . . . . . . . . . . . . 5.3 Codebeispiele . . . . . . . . . . . . . . . . 5.3.1 Einfache IF-Anweisung . . . . . . 5.3.2 Loginabfrage als C++ - Programm 5.3.3 Conficker . . . . . . . . . . . . . . 5.4 Anwendungsgebiete . . . . . . . . . . . . . 5.5 Schutz vor Disassembling . . . . . . . . . 5.6 Rechtliche Grundlagen . . . . . . . . . . . 6 Schluss . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 6 7 7 11 12 12 12 15 16 17 17 18 2 1 Einleitung Was macht dieses Programm eigentlich genau? Werden vielleicht private Daten ausgespäht und gesammelt? Das sind heute für viele Programmierer und Endnutzer entscheidende Fragen. In der Benutzeroberfläche bekommt man zwar alles zu sehen, was man sehen soll, aber eben meistens nicht viel mehr. Man spricht hier auch von WYSINWYX (What you see is not what you execute); in der Maschinensprache können noch andere Prozesse ablaufen, die in der Benutzeroberfläche nicht zu erkennen sind. Eine Lösung für dieses Problem ist Disassembling. Dadurch können ausführbare Programme aus dem Maschinencode zurück in Assemblercode übersetzt werden. In dieser Arbeit wird erklärt, wie die Technik des Disassembling funktioniert und welche Vorteile dadurch entstehen. Dazu wird zunächst erörtert, wie ein ausführbares Programm entsteht, um ein grundlegendes Verständnis für den Arbeitsbereich eines Disassemblers zu geben. Anschließend werden grundlegende Elemente von Assembler und Systemarchitekturen erklärt, um den im Beispiel verwendeten Code verständlich zu machen. Des Weiteren wird ein Überblick über Softwarelösungen und Anwendungsgebiete des Disassemblings gegeben und die rechtlichen Aspekte dieses Gebiets aufgezeigt. 2 Ein Programm erzeugen Programme werden meist in einer sogenannten höheren Programmiersprache (Hochsprachen) geschrieben. Diese vereinfacht die Programmierung für den Menschen enorm, da sie eine für ihn äußerst verständliche Sprache ist, da die Syntax oft an menschliche Denkgewohnheiten angepasst ist. Den Hochsprachen ist gemeinsam, dass sie nicht direkt auf der Maschine ausgeführt werden können, sondern zunächst von einem sogenannten Compiler in eine maschinenlesbare Form übersetzt werden müssen. 2.1 Compiling Aufgabe von Compilern ist die Übersetzung von Programmen, die in einer Hochsprache geschrieben sind, in maschinenlesbaren Code. Die Zielsprache ist von der Zielplattform abhängig. Oft übersetzen sie die Hochsprache zunächst in Assemblercode. Es gibt spezielle Compiler, die ein Programm erst kompilieren, kurz bevor es ausgeführt wird. Diese Just-in-Time-Compiler (werden zum Beispiel für .NET-Applikationen verwendet) übersetzen das Programm während es läuft, mit Hilfe eines Zwischencodes in Maschinensprache. 3 2.2 Assembling Ein Assembler ist im Grunde ein spezieller Compiler: Sein Quellcode ist immer Assemblercode und sein Zielcode Maschinencode. Voraussetzung für das Assembling ist, dass der Compiler ein Zielprogramm in Assemblercode ausgegeben hat oder das Programm direkt in Assemblerclde geschrieben ist. Durch ein Assemblerprogramm wird dieser in binären Maschinencode übersetzt, welcher direkt vom Prozessor ausführbar ist. Zunächst werden jedoch einige Merkmale von Assemblercode erklärt. 3 Assemblercode auf einer x86-Systemstruktur Da die Assemblersprache die Maschinensprache direkt repräsentiert, ist sie direkt von der Maschinenarchitektur abhängig. Jede Computerarchitektur hat folglich ihre eigene Assemblersprache. In diesem Kapitel werden nun einige Befehle der Assemblersprache und grundlegende Maschinenstrukturen erklärt, die notwendig sind, um das Beispiel im Kapitel 5 zu verstehen. Als Maschinenarchitektur wird ein x86-Prozessor (eine von Intel entwickelte Architektur von Mikroprozessoren. [6]) gewählt. 3.1 Maschinenstruktur Ein aktueller Intel x86-Prozessor hat neun 32-bit Register. Abbildung 1: Register eines Intel x86-Prozessors 4 Die Register verschiedener Größen (z. B. 8-, 16- oder 32-Bit-Register) können theoretisch für jeden Zweck verwendet werden. In der Regel werden dort Daten oder Befehle mit den vorgegebenen Längen gespeichert werden. Das Register EIP nimmt dabei eine besondere Rolle ein: In ihm ist der aktuelle Stand des Befehlszählers gespeichert. So kann das laufende Programm jederzeit auslesen, welcher Befehl als nächstes zu verarbeiten ist. Die grundlegende Funktionsweise einer Maschinenarchitektur, wie wir sie in aktuellen Intel-Maschinen finden, wurde bereits 1946 von Von Neumann beschrieben : 1. Ein Rechner besteht aus vier Werken: -Haupt- bzw. Arbeitsspeicher (speichert Programme und Daten) -Leitwerk (interpretiert Programm) -Rechenwerk (führt arithmetische Operationen aus) -Ein/Ausgabewerk incl. Sekundärspeicher (kommuniziert mit Umwelt, Langfristspeicher) 2. Die Struktur des Rechners ist unabhängig vom bearbeiteten Problem (Programmsteuerung!) 3. Programm und Daten stehen in demselben Speicher (Hauptspeicher) und können durch die Maschine verändert werden. 4. Der Hauptspeicher ist in Zellen gleicher Größe geteilt, die durch fortlaufende Nummern (Adressen) bezeichnet werden. 5. Das Programm besteht aus einer Folge von Befehlen (Instruktionen), die i.a. nacheinander ausgeführt werden (sequentiell). 6. Von der Folge kann durch bedingte oder unbedingte Sprungbefehle abgewichen werden, die die Programmfortsetzung aus einer anderen Zelle bewirken. Bedingte Sprünge sind von gespeicherten Werten abhängig. 7. Die Maschine benutzt Binärcodes, Zahlen werden dual dargestellt. Bei aktuelleren Maschinenarchitekturen ist der Speicher für den Code schreibgeschützt. 3.2 Assemblercode Ein Assemblerprogramm besteht aus einzelnen Assembleranweisungen. Diese bestehen jeweils aus einem Opcode in symbolischer Schreibweise und den zugehörigen Operanden. Beispiele für Befehle: 5 MOV AL, 62h XOR EAX, EAX ADD EAX, [EBP] INC EBX MOV ist der Opcode für den Move-Befehl, der hier den hexadezimalen Wert 62 (98 dezimal) ins Register ’AL’ lädt. Verknüpft die Operanden durch ein logisches exklusives Oder (Antivalenz). (Das Register EAX ist danach immer 0) Addiert zum Register EAX den Wert einer über das Register EBP bestimmten Speicherzelle. Addiert 1 zum Zieloperanden EBX. Der Assembler übersetzt nun z.B. den Befehl XOR EAX, EAX in den hexadezimalen Maschinencode 33C0. Wenn ein Programm in Abhängigkeit von Bedingungen verschiedene Wege einschlagen kann, werden sogenannte Jumps verwendet. Diese verändern, je nachdem ob eine Bedingung gegeben ist oder nicht, den EIP. Für die Jumps spielen einige Flags eine große Rolle. Der wichtigste ist wohl der Z-Flag (Zero-Flag). Er zeigt an, ob die aktuelle Operation das Ergebnis 0 hat. In diesem Fall hat er den Wert 1, bei jedem anderen Ergebnis 0. Der Jump JNZ (’JUMP NOT ZERO’) wird zum Beispiel nur ausgeführt, wenn der Z-Flag nicht gesetzt ist, also momentan den Wert 0 hat. (Siehe Listing 4) Ein weiterer interessanter Jump ist der JMP-Jump, der den EIP in jedem Fall verändert. Der Befehl NOP (’No Operation’) hingegen macht gar nichts und das Programm läuft auf dem normalen Weg weiter. 4 Programm ausführen Der fertige Maschinencode muss in der für ihn bestimmten Systemumgebung ausgeführt werden. Dazu benötigt er verschiedene Bibliotheken. In diesen Bibliotheken finden sich oft verwendete, vordefinierte Funktionen, die in das Programm eingebunden werden können. Der Linker verbindet diese und erzeugt eine ausführbare Datei. Wird eine solche Datei gestartet, reserviert der Lader die benötigten Speicherbereiche, lädt den Maschinencode in den Speicher und bewirkt die Ausführung des Programms. 5 5.1 Disassembling Definition Die Technik, mit der ausführbare Programme, die in Maschinencode verfasst sind, wieder zurück in Assemblersprache übersetzt werden, nennt man Disassembling. Die Software, die diesen Vorgang ausführt, muss wissen, auf welcher Maschinenarchitektur sie arbeitet. Im Rahmen dieser Arbeit wird ein x86Prozessor verwendet. [7] 6 Ein großer Anteil von Malware ist direkt in Assemblersprache geschrieben. Durch Disassembling lässt sich Schadsoftware identifizieren und klassifizieren. 5.2 Softwarelösungen Als dafür mögliche Software wird eine Vielfalt an Programmen angeboten. Meistens ist der Disassembler in Entwicklerumgebungen integriert, damit der Hochsprachencode zum Testen ausgeführt werden kann und im Falle von Fehlern der Maschinencode ausgelesen werden kann. (Kapitel 5.4). Im Folgenden sollen zwei Disassembler betrachtet werden, die zum Disassembling und zum anschließenden Reassembling verwendet werden können. 5.2.1 OllyDbg OllyDbg [12] (Abbildung 2) ist ein Disassembler für Microsoft Windows. Es wird als Shareware angeboten, hat aber keine Beschränkungen. Die Benutzeroberfläche ist in sechs Bereiche aufgeteilt. In der Steuerungsleiste, die sich im oberen Bereich des Disassemblers befindet, finden sich Schaltflächen, die es dem Anwender ermöglichen, ein ausführbares Programm einzulesen, schrittweise durch dieses zu navigieren und den aktuellen Status des Programms zu erkennen. Die restlichen Bereiche, welche alle den aktuellen Status des laufenden, zu analysierenden Programms anzeigen, und deren Bedeutung werden erklärt: 7 8 Abbildung 2: Benutzeroberfläche von OllyDbg Abbildung 3: OllyDbg - CPU-Fenster Das erste Fenster, das CPU-Fenster, ist in in vier Spalten unterteilt. Die erste Spalte zeigt die virtuellen Adressen des Speichers in Hexadezimalschreibweise, in die Windows das Programm geladen hat. Die zweite zeigt die darin enthaltenen Opcodes (auch hexadezimal) und die dritte den dafür von OllyDbg ermittelten Assemblercode. In der letzten Spalte wird ein Kommentar zur Funktion, die durch diese Adresse angesteuert wird, und deren Parameter in lesbarer Form angezeigt. Abbildung 4: OllyDbg - nächste Operation Im nächsten Fenster sehen wir weitere Informationen zu der Operation, die im nächsten Schritt ausgeführt wird. 9 Abbildung 5: OllyDbg - DUMP-Fenster Im dritten sehen wir das sogenannte DUMP-Fenster, in dem der Inhalt der betroffenen Speicheradressen im Maschinencode angezeigt wird. Abbildung 6: OllyDbg - System-Stack Im vierten Fenster sehen wir die aktuelle Belegung des System-Stacks. 10 Abbildung 7: OllyDbg - Register und Flags Im letzten Fenster sehen wir die aktuelle Belegung der Register und Flags. In diesem Beispiel ist das Zero(Z)-Flag gesetzt, da es den Wert 1 hat. 5.2.2 IDA Pro Die deutlich professionellere und kommerziellere Lösung für einen Disassembler heißt IDA Pro [5]. Die Anwendung wird frei nur in einer veralteten Version angeboten, für die aktuelle gibt es nur eine zeit- und funktionsbeschränkte Demoversion. Aufgrund des gleichen Anwendungsgebietes weist dieses Programm starke Parallelen zu OllyDbg auf. Allerdings sticht sofort der Kontrollflussgraph ins Auge, der den Verlauf des Programms in Form eines Ablaufdiagramms graphisch darstellt. So lässt sich schnell ein Überblick über den gesamten Ablauf des Programms erkennen. Des Weiteren unterstützt IDA Pro viel mehr Maschinenstrukturen: Währen Olly nur auf einem x86-System mit Microsoft Windows (32-bit) arbeitet, unterstützt IDA Pro in der Vollversion mehr als 50 Prozessorfamilien, darunter auch PDAs, Mobiltelefone und Spielkonsolen [5]. 11 Abbildung 8: Beispielprogramm im Programmplotter von IDA Pro 12 5.3 5.3.1 Codebeispiele Einfache IF-Anweisung Es wird gezeigt, wie eine einfache If-Anweisung (Programmiersprache C) in Assemblercode umgesetzt wird. 1 2 3 int main ( ) { int x , y , z ; z = ( x>y ) ? 2 : 3 ; 4 5 } Listing 1: Beispielprogramm ’if-Anweisung’ in C 1 2 3 4 5 6 7 8 8048355: 8048358: 804835 b : 804835 d : 8048364: 8048366: 804836 d : 8048370: 8b 3b 7e c7 eb c7 8b 89 45 45 09 45 07 45 45 45 f8 f4 e8 02 00 00 00 e8 03 00 00 00 e8 f0 mov cmp jle movl jmp movl mov mov −0x8(%ebp ) ,% eax −0xc(%ebp ) ,% eax 8048366 <main+0x22> $0x2 ,−0 x18(%ebp ) 804836 d <main+0x29> $0x3 ,−0 x18(%ebp ) −0x18(%ebp ) ,% eax %eax ,−0 x10(%ebp ) Listing 2: Beispielprogramm ’if-Anweisung’ in Assemblercode Im Listing 1 wird der C-Code der If-Anweisung, im Listing 2 wird der disassemblierte Code des ausführbaren If-Programms gezeigt. In diesem Assemblercode ist in Zeile 1 die Zuweisung des Werts von x (= 0x8(%ebp)) auf das Register EAX zu sehen. In Zeile 2 wird der Wert von y (= -0xc(%ebp)) mit EAX verglichen. Liefert der Vergleich die Bedingung kleiner oder gleich, wird das Programm an der Adresse 8048366 fortgesetzt und dort (Zeile 6) wird der Wert 3 in die temporäre Speicherzelle mit Adresse 0x18(%ebp) geschrieben. Im anderen Vergleichsfall wird das Programm in Zeile 4 fortgesetzt, wo der Wert 2 in die gleiche temporäre Speicherzelle geschrieben wird, und in Zeile 5 wird der Jump über die andere Zuweisung (Zeile 6) ausgeführt, damit die Speicherzelle nicht erneut belegt wird. In Zeile 7 wird der Wert der temporären Speicherzelle ins Register EAX geladen und in Zeile 8 wird der Wert von EAX in die für z (= -0x10(%ebp)) bestimmte Speicherzelle geschoben. 5.3.2 Loginabfrage als C++ - Programm Um die Möglichkeiten der beiden Disassembler (siehe Kapitel 5.2.2 und 5.2.1) besser zu verstehen, wird nun eine kleine C++-Anwendung (Win-32) mit Microsoft Visual Studio 2008 erstellt, die u.a. auch eine Schleife beinhaltet. 1 2 #include ” s t d a f x . h” #include ” s t r i n g . h” 13 3 4 5 int { tmain ( int argc , char argv [ ] ) char pwd [ 1 0 0 ] ; p r i n t f ( ” Passwort e i n g e b e n : ” ) ; f g e t s (pwd , 1 0 0 , s t d i n ) ; pwd [ s t r l e n (pwd) −1] = ’ \0 ’ ; 6 7 8 9 10 i f ( strcmp (pwd , ”admin” ) == 0 ) p r i n t f ( ” Login e r f o l g r e i c h \n” ) ; else p r i n t f ( ” Login f e h l g e s c h l a g e n \n” ) ; return 0 ; 11 12 13 14 15 16 } Listing 3: Beispielprogramm ’LoginWindow’ in C++ Dieses Beispiel beinhaltet nur eine Passwortabfrage und -überprüfung und könnte zum Beispiel am Anfang eines zugriffsbeschränkten Programms stehen, welches es nur nach Eingabe des richtigen Passworts öffnet. Beim Öffnen des Programms wird die Ausgabe ’Passwort eingeben:’ angezeigt und der Befehl fgets in Zeile 8 liest die Eingabe in die Variable pwd ein. Anschließend wird in Zeile 9 das letzte Zeichen - das bei der Eingabe ein Return ist - durch einen Nullbyte ersetzt. Danach wird das Passwort überprüft. Das fertige Programm lässt sich nun mit Disassemblern betrachten: In IDA pro lässt sich der Programmverlauf im graphischem Ablaufdiagramm (siehe Abbildung 8) erkennen, das in Assembler letztendlich der Jump JNZ für die Überprüfung des Passworts verantwortlich ist. Wenn wir dieses Programm in OllyDbg öffnen, können wir es Schritt für Schritt durchlaufen lassen, so kann hier der Programmverlauf in Assemblersprache nachvollzogen werden. Zwischen den virtuellen Adressen 00091040 und 00091045 (erste und zweite rote Markierung, Abbildung 3) ist die strlen-Funktion in Assemblercode zu sehen, die die Länge des eingegebenen Strings ermittelt. Am Beispiel dieser Funktion wird hier nun die Aufgabe des JNZ-Jumps in der Schleife dieser Funktion erklärt. 1 2 3 4 5 00DF1040 00DF1042 00DF1043 00DF1045 00DF1047 MOV CL,BYTE PTR DS : [ EAX] INC EAX TEST CL, CL JNZ SHORT LoginWin . 0 0 DF1040 SUB EAX, EDX Listing 4: strlen-Funktion in Assemblercode In Zeile 1 wird das Zeichen an der Position EAX (beim ersten Durchlauf 0 und damit das erste Zeichen) aus der Adresse DS in CL verschoben. Danach wird 14 der Wert von EAX um 1 erhöht. In Zeile 3 wird dann getestet, ob CL einen Inhalt hat. Wenn dies der Fall ist, bleibt das Zero-Flag 0, da das Ergebnis dieser Operation true bzw. 1 war. Der JNZ-Jump lässt das Programm zurück zu Zeile 00DF1040 (siehe Zeile 4) springen. Wenn aber TEST CL,CL false ergibt, also kein Zeichen mehr übrig ist, läuft das Programm weiter (Zeile 5). Von EAX wird nun der Wert des Registers EDX abgezogen, der vor der Funktion mit EAX gleichgesetzt wurde. Zwischen den nächsten beiden roten Markierungen (Adressen 00091056 und 00091070) ist die Entsprechung der strcmp-Funktion. Hier wird das Passwort überprüft, und wenn es richtig ist, wird das ZeroFlag gesetzt. Wenn das Zero-Flag nicht gesetzt ist, wird durch den JNZ-Jump (009107D) zur Ausgabe des Strings ’Login fehlgeschlagen’ gesprungen. Ansonsten wird der String ’Login erfolgreich’ ausgegeben. Außerdem lässt sich unschwer erkennen, mit welchem String das Passwort verglichen wird, so dass wir das Programm erfolgreich ausführen können: ’admin’ (siehe Abbildung 3) Da wir in OllyDbg auch Schreibzugriff haben, besteht auch die Möglichkeit, den Befehl JNZ durch den Befehl NOP zu ersetzen, was zur Folge hat, dass unabhängig von der Eingabe immer der String ’Login erfolgreich’ ausgegeben wird. So kann ein nicht berechtigter Anwender Zugang zum Programm erhalten. Dieses Beispiels spiegelt nun einige grundliegende Anwendungsmöglichkeiten von Disassembling wieder, gibt aber dennoch nur einen kleinen Einblick in die zahlreichen Anwendungsgebiete für Disassembling. 5.3.3 Conficker Um Schwachstellen in einem Systemprozess zu finden, können diese disassembliert werden. So kann ein Angreifer herausfinden, wie die Schwachstelle genau aussieht, und in auf welche Weise schadhafter Code in diese Prozesse injiziert werden kann. Der Conficker-Wurm, der seit November 2008 ca. 3 Millionen Rechner befallen hat, produziert in der Systemfunktion MS08-067 des Microsoft-Server-Dienstes einen Pufferüberlauf (buffer overflow ). Dieses ist für das Canonicalizing eines aufgerufenen Serverstrings zuständig. Das bedeutet, dass aufgerufene Adressen in eine vorschriftsmäßige Form gebracht werden. So wird zum Beispiel die Adresse \\server1\freigabe\dir1\..\dir2\ umgewandelt in \\server1\freigabe\dir2\. Durch einen Fehler in dieser Routine kann, wenn die Anweisung \..\ über das root-Verzeichnis hinausgeht (z.B. \\server1\freigabe\..\..\) in Speicherbereiche, die von svchost.exe als Dienst ausgeführt werden, geschrieben werden. Die Grundidee des Conficker-Wurms ist es, Code, der von einem Systemdienst mit allen Privilegien ausgeführt werden kann, auf die Maschine zu bringen. Also ruft Conficker nun auf der zu infizierenden Maschine eine Adresse wie \\server1\freigabe\.. \..\urlmon(http://114.44.XX.XX:2363/wkpqz) auf. Der Befehl urlmon(http://114.44.XX.XX:2363/wkpqz) wird in Speicherbereiche geschrieben, die dann von dem Serverprogramm svchost.exe im 15 Dienstmodus ausgeführt werden. Dem Serverprogramm svchost.exe ist standardmäßig erlaubt, eine Verbindung ins Internet herzustellen. Auch Firewalls sind meist so konfiguriert, dass Dienste, die durch svchost.exe ausgeführt werden, nicht geblockt werden. Dadurch wird dann die angegebene URL in eine Datei auf der Maschine geladen. Von dort aus wird sie ausgeführt und installiert Conficker. Dieser kann sich nun von der infizierten Maschine aus weiter verbreiten. 5.4 Anwendungsgebiete Die Technik des Disassembling wird in verschiedenen Bereichen angewendet, die in diesem Kapitel voneinander abgegrenzt werden. Anti-Viren-Programme Ein Anwendungsgebiet ist die Virenerkennung in ausführbaren Dateien. Damit man Schadmuster im Maschinencode entdecken kann, kann der Hersteller schädlichen Maschinencode durch Disassemblierung identifizieren. Gefährliche Muster, wie zum Beispiel Endlosschleifen, werden (in Maschinencode) in sogenannten Viral definition files gespeichert und als Update für die Endnutzer zur Verfügung gestellt. Wird ein Programm dann verdächtigt, bösartige Ziele zu verfolgen, da es mit Mustern aus dem Viral definition file übereinstimmt, kann es sofort gestoppt werden und der Benutzer über eine mögliche Infektion seines Systems gewarnt werden. Überprüfung von Software Ein weiteres Gebiet ist die Überprüfung von Software, deren Code nicht einsehbar ist (Closed Code). Es stellt sich die Frage, ob Programme ohne Bedenken verwendet werden können. Dieses Gebiet lässt sich am Beispiel ’The Silver Needle in the Skype’ erklären [3]: Im Auftrag von EADS wurde überprüft, ob Skype, das ein seltsames Trafficverhalten (auch im unbenutzten Modus) aufweist, an Firmenrechnern verwendet werden kann oder ob dadurch Daten ausgespäht werden können und Angreifer Zugang zum Firmennetzwerk haben. So konnte herausgefunden werden, dass Skype zwar eine gute Verschlüsselung benutzt, aber Mitarbeiter von Skype jederzeit Konversationen mithören könnten. Skype lässt sich auf keine generelle Sicherheitspolice ein, es werden jedoch nur vertrauenswürdige Server verwendet. Durch das Aufbauen eines eigenen Netzwerks könnten Nutzer und deren Traffic ausgespäht werden. Aus diesem Grund muss auch jeder Kontakt vertrauenswürdig sein. Jedoch konnte in dieser Studie nicht herausgefunden werden, ob man sich vor Angriffen schützen oder herauszufinden kann, ob eine Backdoor existiert. 16 Bösartiger Code (Malware) Wie in Listing 5.3.2 gezeigt und am Beispiel des Conficker-Wurms wurde, ist Disassembling auch eine Technik, die Virenschreiber verwenden. Ein Programmierer kann versuchen, entsprechende Passwörter oder Codes ausfindig zu machen, die Passwortabfrage in einem Programm zu umgehen, nicht aktivierte, kostenpflichtige Teile eines Programms zu aktivieren oder gewisse Teile des Programms zu ändern. Hierbei spricht man auch vom Cracken oder Patchen von Software. Natürlich kann auch Schadcode in eine ansonsten vertrauenswürdige Datei eingeschleust werden (Beispiel: Conficker, Kapitel 5.3.3). Konkurrenten haben auch die Möglichkeit, benötigte Funktionen aus Programmen zu kopieren und für eigene Software wiederzuverwenden. Entwicklung von Software Auch für Entwickler von Software ist das Disassembling relevant: Um zu testen ob geschriebene Software so funktioniert, wie man erwartet, beinhalten Entwicklungstools Disassembler (Reverse Engineering). So können Fehler im Code schnell gefunden werden. Des Weiteren besteht für Entwickler die Möglichkeit, Schnittstellen von Sekundärsoftware zu verstehen. So kann das eigene Programm kompatibel zu anderen Programmen gemacht werden. 5.5 Schutz vor Disassembling Im Bereich der Softwareentwicklung verbreiten sich inzwischen aber schon Softwarelösungen, die in Programme integriert werden, um deren Assemblercode möglichst kompliziert zu gestalten. Hierbei werden zum Beispiel eigene Lader in die Datei integriert, die sich erst nach Programmstart selbst entpacken und anders agieren als der Standard-Windows-Lader. Sie führen während der Laufzeit des Programms Hash-Überprüfungen durch, die das laufende Programm verifizieren, verschlüsseln Daten und gestalten den Programmverlauf deutlich komplizierter. Dadurch wird ein Disassembling zwar stark erschwert, aber nicht unmöglich. Als konkretes Beispiel für ein solches Schutzsystem sei hier SoftwarePassportArmadillo der Firma Silicon Realms [10] genannt, das unter anderem den kompletten Maschinencode des zu schützenden Programms verschlüsselt abspeichert. Zur Laufzeit wird der Code im Systemspeicher entschlüsselt und erst dann ausgeführt. 5.6 Rechtliche Grundlagen Häufig wird bei Software in der EULA, dem End-User-Lizenz-Vertrag [2], das Disassemblieren der Software vom Hersteller untersagt. Dies ist jedoch in Deutschland in den meisten Fällen nicht rechtlich bindend, da §69 des Gesetzes über Urheberrecht und verwandte Schutzrechte das Disassemblieren zu bestimmten Zwecken ausdrücklich erlaubt. Dort heißt es: 17 (1) Die Zustimmung des Rechtsinhabers ist nicht erforderlich, wenn die Vervielfältigung des Codes oder die Übersetzung der Codeform im Sinne des § 69c Nr. 1 und 2 unerlässlich ist, um die erforderlichen Informationen zur Herstellung der Interoperabilität eines unabhängig geschaffenen Computerprogramms mit anderen Programmen zu erhalten, sofern folgende Bedingungen erfüllt sind: 1. Die Handlungen werden von dem Lizenznehmer oder von einer anderen zur Verwendung eines Vervielfältigungsstücks des Programms berechtigten Person oder in deren Namen von einer hierzu ermächtigten Person vorgenommen; 2. die für die Herstellung der Interoperabilität notwendigen Informationen sind für die in Nummer 1 genannten Personen noch nicht ohne weiteres zugänglich gemacht; 3. die Handlungen beschränken sich auf die Teile des ursprünglichen Programms, die zur Herstellung der Interoperabilität notwendig sind. Das Disassemblieren von Fremdsoftware ist aus gesetzlicher Sicht nur für die Herstellung von Kompatibilität der eigenen Software gedacht. Dies wird in den Absätzen (2) und (3) dieses Paragraphen genauer geregelt [2]. 6 Schluss Die Technik Disassembling eignet sich zum Erkennen und Debuggen von • Malware, • Sicherheitslücken in Benutzer- und Systemprogrammen und • fehlerhaftem oder unverständlichem Code in entwickelter Software. Oft spielt Disassembling auch eine Rolle beim Nachvollziehen von Programmen mit nicht öffentlich zugänglichem Code. Neben dem Cracken von Software sind das Stehlen von Code aus Fremdsoftware und das Einschleusen von schadhaftem Code in Software gängige Vorgehensweisen. Disassembling ist eine spannende und nützliche Technik, die aus vielen Bereichen der Informatik nicht weg zu denken ist. 18 Literatur [1] Günter Born. Assembler enträtselt. Systhema Verlag GmbH, 1991. [2] Bundesministerium der Justiz. Gesetz über Urheberrecht und verwandte Schutzrechte. http://bundesrecht.juris.de/urhg/ 69e.html. [3] Philippe Biondi & Fabrice Desclaux. Silver Needle in the Skype. EADS, 2006. http://www.blackhat.com/presentations/bh-europe-06/bheu-06-biondi/bh-eu-06-biondi-up.pdf. [4] Otmar Feger. MC-Tools 2 - Die 8051-Mikrocontroller-Familie - Einführung in die Software mit Assembler und Disassembler. Feger & Co. Hardware + Software Verlag OHG, 1990. [5] Hex-Rays. IDA Pro. http://www.hex-rays.com/idapro/. [6] Intel. Intel Architecture Software Developer’s nual Volume 2: Instruction Set Reference, http://www.intel.com/design/pentium/manuals/24319101.PDF. Ma1997. [7] Wolfgang Link. Assembler Programmierung. Franzis, 2004. [8] Trutz Eyke Podschun. Das Assembler-Buch. Addison-Wesley Verlag, 2002. [9] Klaus Schmaranz. Software-Entwicklung in C++. Springer-Verlag, 2003. [10] Silicon Realms. http://www.siliconrealms.com/. SoftwarePassport/Armadillo. [11] Peter Szor. Virus Research and Defense. Linda McCarthy (Symantec orporation), 2005. [12] Oleh Yuschuk. OllyDbg, 2000-2009. http://ollydbg.de/quickst.htm. 19