Viren. Viren? Viren! Teil 1



    Sprechen Sie über Computerviren? Nein, nicht dass dein Antivirus gestern gefangen hat. Nicht, dass Sie unter dem Deckmantel eines anderen Photoshop-Installationsprogramms heruntergeladen hätten. Nicht über rootkit-e, das auf Ihrem Server steht und sich als Systemprozess tarnt. Nicht über Suchleisten, Downloader und andere malvari. Nicht über Code, der schlechte Dinge in Ihrem Namen tut und Ihr Geld will. Nein, das alles ist Handel, keine Romantik ...

    Wir werden von Computerviren als einem Code sprechen, der seine eigenen Kopien erzeugen kann und sich von Generation zu Generation ändert. Welches, wie seine biologischen Gegenstücke, einen Datenträger benötigt, der funktionsfähig ist und funktionsfähig bleibt, um neue Generationen des Virus zum Leben zu erwecken. Was für die Zucht eine fruchtbare Umgebung, viele leckere ausführbare Dateien und auch viele dumme und aktive Benutzer erfordert, um sie auszuführen. Der Name „Virus“ ist also nicht nur ein schönes Etikett für die Beschreibung eines Schadprogramms, ein Computervirus ist im klassischen Sinne eine Entität, die seinem biologischen Gegenstück sehr nahe steht. Die Menschheit ist, wie mehr als einmal bewiesen, in der Lage, sehr raffinierte Lösungen zu schaffen, insbesondere wenn es darum geht, etwas zu schaffen, das anderen Menschen schadet.

    Vor langer Zeit, als DOS zu den Leuten kam und jeder Programmierer sein eigenes kleines Universum hatte, in dem der Adressraum gleich war und die Rechte an den Dateien immer rwx waren, kam der Gedanke auf, ob sich das Programm selbst kopieren könnte. "Natürlich kann es!", Sagte der Programmierer und schrieb Code, der seine eigene ausführbare Datei kopiert. Der nächste Gedanke war: "Können zwei Programme zu einem verschmelzen?" "Natürlich können sie das!", Sagte der Programmierer und schrieb den ersten Infektor. "Aber warum?", Dachte er und dies war der Beginn der Ära der Computerviren. Es stellte sich heraus, dass das Verwöhnen eines Computers und das Vermeiden von Erkennungen auf jede erdenkliche Weise sehr viel Spaß macht und das Erstellen von Viren aus Sicht eines Systemprogrammierers sehr interessant ist. Außerdem,

    Im Allgemeinen sind die Texte für den Artikel völlig ausreichend. Kommen wir zur Sache. Ich möchte über den klassischen Virus, seine Struktur, grundlegende Konzepte, Erkennungsmethoden und Algorithmen sprechen, mit denen beide Seiten gewinnen.

    Virus Anatomie

    Wir werden über Viren sprechen, die in ausführbaren Dateien der Formate PE und ELF leben, dh Viren, deren Hauptteil ausführbarer Code für die x86-Plattform ist. Darüber hinaus sollte unser Virus die Quelldatei nicht zerstören, so dass ihre Funktionsfähigkeit erhalten bleibt und alle geeigneten ausführbaren Dateien ordnungsgemäß infiziert werden. Ja, das Brechen ist viel einfacher, aber wir haben uns darauf geeinigt, über die richtigen Viren zu sprechen, oder? Um das Material auf dem neuesten Stand zu halten, werde ich keine Zeit damit verschwenden, Infektoren des alten COM-Formats zu überprüfen, obwohl darauf die ersten fortgeschrittenen Techniken für die Arbeit mit ausführbarem Code ausgeführt wurden.

    Die Hauptbestandteile des Virencodes sind Infector und Payload. Infector ist ein Code, der nach für eine Infektion geeigneten Dateien sucht und diese mit einem Virus infiziert. Dabei wird versucht, die Tatsache der Implementierung so weit wie möglich zu verbergen und die Funktionalität der Datei nicht zu beeinträchtigen. Payload ist ein Code, der die für den Virmeiker tatsächlich erforderlichen Aktionen ausführt, z. B. Spam oder DoS-it an jemanden sendet oder einfach die Textdatei „Virya war hier“ auf dem Computer belässt. Es ist für uns völlig unbegründet, dass sich eine Nutzlast darin befindet. Hauptsache, der Virmaker tut sein Bestes, um seinen Inhalt zu verbergen.

    Beginnen wir mit den Eigenschaften des Virencodes. Um die Implementierung des Codes zu vereinfachen, möchten Sie den Code und die Daten nicht trennen. Daher wird in der Regel die Integration von Daten direkt in den ausführbaren Code verwendet. Nun, zum Beispiel so:
        jmp message
    the_back:
        mov eax, 0x4
        mov ebx, 0x1
        pop ecx		; со стека будет взят адрес «Hello, World»
        mov edx, 0xF
        int 0x80
    ...
    message:
        call the_back       ; после исполнения на стеке будет лежать адрес «возврата», т.е. адрес «Hello, World\n»
        db "Hello, World!", 0Dh, 0Ah
    

    Oder so:
    push 0x68732f2f   ; “hs//”
    push 0x6e69622f   ; “nib/”
    mov ebx, esp ; в ESP теперь адрес строки «/bin/sh»
    mov al, 11
    int 0x80
    


    Unter bestimmten Umständen können alle diese Codevarianten einfach in den Speicher kopiert und bei der ersten Anweisung zu JMP gemacht werden. Nachdem dieser Code korrekt geschrieben wurde, die korrekten Offsets, Systemaufrufe, die Sauberkeit des Stacks vor und nach der Ausführung usw. berücksichtigt wurden, kann er mit dem Code eines anderen in einen Puffer eingebettet werden.

    Angenommen, ein Virmaker kann einen Viruscode in diesem Stil schreiben und muss ihn jetzt in eine vorhandene ausführbare Datei einbetten. Er muss sich um zwei Dinge kümmern:
    • Wohin mit dem Virus? Es ist erforderlich, genügend Speicherplatz für den Virus zu finden, ihn dort zu schreiben, wenn möglich, ohne die Datei zu beschädigen, damit in dem Bereich, in dem der Virus auftritt, die Codeausführung zulässig ist.
    • Wie übertrage ich die Kontrolle auf einen Virus? Das Einfügen des Virus in die Datei reicht nicht aus, Sie müssen den Übergang zu seinem Körper dennoch durchführen und nach Abschluss der Arbeit die Kontrolle an das Opferprogramm zurückgeben. Oder in einer anderen Reihenfolge, aber auf jeden Fall haben wir uns darauf geeinigt, nichts zu brechen, oder?

    Wir werden also die Implementierung der Datei verstehen. Moderne ausführbare Formate für die x86-Plattform unter Windows und Linux sind PE (Portable Executable) und ELF (Executable and Linkable Format). Sie können ihre Spezifikationen leicht in der Systemdokumentation finden, und wenn Sie Probleme mit dem Schutz von ausführbarem Code haben, sollten Sie sie auf keinen Fall verpassen. Ausführbare Formate und der Systemlader (Betriebssystemcode, der die ausführbare Datei ausführt) gehören zu den „Elefanten“, auf denen das Betriebssystem basiert. Die Prozedur zum Starten einer EXE-Datei ist ein sehr komplexer algorithmischer Prozess mit einer Reihe von Nuancen. Sie können darüber in einem Dutzend Artikeln sprechen, die Sie auf jeden Fall selbst finden, wenn das Thema Sie interessiert. Ich werde mich auf eine einfache Überlegung beschränken, die für ein grundlegendes Verständnis des Startvorgangs ausreicht. Damit sie mich nicht mit Tomaten bewerfen,

    Eine ausführbare Datei (PE oder ELF) besteht aus einem Header und einer Reihe von Abschnitten. Abschnitte sind ausgerichtete Puffer mit Code oder Daten (siehe unten). Wenn die Datei gestartet wird, werden Abschnitte in den Speicher kopiert, und ihnen wird Speicher zugewiesen, und es ist nicht erforderlich, dass sie den Datenträger belegen. Die Kopfzeile enthält das Layout der Abschnitte und teilt dem Ladeprogramm mit, wie sich die Abschnitte in der Datei befinden, wenn sie sich auf der Festplatte befinden, und wie sie im Speicher angeordnet werden, bevor die Steuerung auf den Code in der Datei übertragen wird. Wir sind an drei Schlüsselparametern für jeden Abschnitt interessiert: psize, vsize und flags. Psize (physische Größe) ist die Größe der Partition auf der Festplatte. Vsize (virtuelle Größe) - Die Größe des Abschnitts im Speicher nach dem Laden der Datei. Flags - Abschnittsattribute (rwx). Psize und Vsize können sich erheblich unterscheiden, z. B.

    Den Speicherseiten, auf denen der Abschnitt angezeigt wird, werden Flags (Zugriffsattribute) zugewiesen. Beispielsweise hat ein Abschnitt mit ausführbarem Code die Attribute r_x (Lesen, Ausführen) und ein Datenabschnitt die Attribute rw_ (Lesen, Schreiben). Der Prozessor, der versucht, Code auf der Seite ohne das Ausführungsflag auszuführen, löst eine Ausnahme aus. Dasselbe gilt für den Versuch, auf die Seite ohne das Attribut w zu schreiben. Daher sollte der Virmaker beim Platzieren des Viruscodes die Attribute der Speicherseiten berücksichtigen, auf denen sich der Viruscode befindet. Bis vor kurzem hatten Standardabschnitte nicht initialisierter Daten (z. B. der Programmstapelbereich) rwx-Attribute (Lesen, Schreiben, Ausführen), mit denen Code direkt auf den Stapel kopiert und dort ausgeführt werden konnte. Jetzt wird es als unmodern und unsicher angesehen, und in neueren Betriebssystemen ist der Stapelbereich nur für Daten vorgesehen. Natürlich

    Im Header befindet sich auch der Einstiegspunkt - die Adresse des ersten Befehls, von dem aus die Datei beginnt.

    Es ist notwendig, eine so wichtige Eigenschaft von ausführbaren Dateien wie die Ausrichtung für Virmeaker zu erwähnen. Damit die Datei optimal von der Festplatte gelesen und im Speicher angezeigt werden kann, werden Abschnitte in den ausführbaren Dateien mit dem Vielfachen von Zweierpotenzen ausgerichtet, und der freie Platz, der von der Ausrichtung (Auffüllung) übrig bleibt, wird nach Ermessen des Compilers mit etwas gefüllt. Zum Beispiel ist es logisch, Abschnitte an der Größe der Speicherseite auszurichten - dann ist es bequem, sie vollständig in den Speicher zu kopieren und Attribute zuzuweisen. Ich werde mich nicht einmal an all diese Ausrichtungen erinnern, wenn es ein bisschen Standarddaten oder Code gibt, der ausgerichtet ist (jeder Programmierer weiß, dass es genau 1.024 Meter pro Kilometer gibt). Nun, die Beschreibung der Standards Portable Executable (PE) und Executable Linux Format (ELF) für die Arbeit mit Sicherheitsmethoden für ausführbaren Code sind Desktop-Bücher.

    Da die Adressen in all diesen Abschnitten miteinander verbunden sind, wird die Quelldatei beschädigt, wenn Sie einfach einen Teil des Codes in die Mitte des Abschnitts legen und mit JMPs verknüpfen. Beliebte Orte für die Implementierung des Virencodes sind daher:
    • Hauptcodeabschnitt (Virus überschreibt den Anfang des ausführbaren Codes, der mit Entry Point beginnt).
    • Abstand zwischen dem Ende des Headers und dem ersten Abschnitt. Es gibt dort nichts und es ist durchaus möglich, einen kleinen Virus (oder seinen Loader) dort unterzubringen, ohne die Datei zu beschädigen.
    • Ein neuer Abschnitt, der dem Header hinzugefügt und nach allen anderen in die Datei eingefügt werden kann. In diesem Fall wird keine interne Vorspannung unterbrochen, und es gibt auch keine Probleme mit dem Ort. Richtig, der letzte Abschnitt in der Datei, in dem die Ausführung zulässig ist, wird natürlich die Heuristik interessieren.
    • Abstand zwischen dem Ende des Inhalts eines Abschnitts und seinem ausgerichteten Ende. Dies ist viel schwieriger, da Sie zuerst genau dieses „Ende“ finden müssen und nicht die Tatsache, dass wir Glück haben und es genug Platz gibt. Für einige Compiler kann dieser Ort jedoch einfach durch charakteristische Bytes gefunden werden.

    Es gibt Wege und Tricks, von denen ich einige im zweiten Artikel beschreiben werde.

    Nun zur Übertragung der Kontrolle. Damit der Virus funktioniert, muss sein Code die Kontrolle übernehmen. Der naheliegendste Weg: Zuerst bekommt der Virus die Kontrolle und dann, nachdem es funktioniert, das Host-Programm. Dies ist der einfachste Weg, aber sie haben auch das Recht auf Leben und Optionen, wenn der Virus die Kontrolle erlangt, z. B. nachdem der Host fertig ist oder mitten in der Ausführung die Ausführung einer Funktion "ersetzt". Hier sind einige Techniken zum Übertragen der Steuerung (der Begriff Einstiegspunkt oder EP, der im Folgenden verwendet wird, ist der Einstiegspunkt, dh die Adresse, an die der Systemlader die Steuerung überträgt, nachdem er die ausführbare Datei für den Start vorbereitet):
    1. JMP ersetzt die ersten Bytes in der Entry Point-Datei durch den Viruskörper. Der Virus speichert Bytes in seinem Körper und stellt sie am Ende seiner Arbeit wieder her und überträgt die Kontrolle an den Anfang des wiederhergestellten Puffers.
    2. Eine ähnliche Methode wie die vorherige, jedoch speichert der Virus anstelle von Bytes mehrere vollständige Maschinenanweisungen in Entry Point. Dann kann er nichts wiederherstellen (nur nach der korrekten Bereinigung des Stapels), sie nach Beendigung seiner eigenen Arbeit ausführen und die Steuerung an die Adresse der folgenden Anweisung übertragen "Gestohlen."
    3. Wie bei der Implementierung gibt es auch hier schlauere Methoden, aber wir werden sie auch weiter unten betrachten oder auf den nächsten Artikel verschieben.

    All dies sind Möglichkeiten, um den Puffer mit dem Code korrekt in eine ausführbare Datei einzufügen. Darüber hinaus S. 2 und S. 3. Sie bedeuten eine Funktionalität, mit der Sie verstehen können, welche Bytes Anweisungen sind und wo die Grenzen zwischen Anweisungen liegen. Schließlich können wir den Befehl nicht in zwei Hälften "brechen", in diesem Fall wird alles brechen. So kommen wir nahtlos zur Betrachtung von Disassemblern in Viren. Wir werden das Konzept des Funktionsprinzips von Disassemblern benötigen, um alle normalen Techniken für die Arbeit mit ausführbarem Code zu berücksichtigen. Es ist also in Ordnung, wenn ich es jetzt ein wenig beschreibe.

    Wenn wir unseren Code genau zwischen den Anweisungen einfügen, können wir den Kontext (Stapel, Flags) speichern und nach Ausführung des Viruscodes alles wieder herstellen und die Kontrolle an das Host-Programm zurückgeben. Dies kann natürlich auch ein Problem sein, wenn Sie Code-Integritätskontrollen, Anti-Debugging usw. verwenden, aber mehr dazu im zweiten Artikel. Um nach einer solchen Stelle zu suchen, benötigen wir Folgendes:
    • Platzieren Sie den Zeiger genau am Anfang eines Befehls (Sie können nur nicht zufällig einen Platz im ausführbaren Abschnitt einnehmen und mit dem Zerlegen beginnen, da dasselbe Byte sowohl den Befehls-Opcode als auch die Daten enthalten kann.)
    • Bestimmen der Länge des Befehls (für x86-Architekturen haben Befehle unterschiedliche Längen)
    • Bewegen Sie den Zeiger vorwärts auf diese Länge. Wir werden am Anfang der nächsten Anweisung sein.
    • Wiederholen, bis wir uns entschließen aufzuhören

    Dies ist die Mindestfunktionalität, die erforderlich ist, um nicht in die Mitte des Befehls zu fallen. Eine Funktion, die einen Zeiger auf eine Byte-Zeichenfolge nimmt und als Antwort die Länge des Befehls zurückgibt, wird als Längendisassembler bezeichnet. Beispielsweise kann der Infektionsalgorithmus wie folgt lauten:
    1. Wir wählen eine köstliche ausführbare Datei (dick genug, um dem Viruskörper mit der gewünschten Verteilung von Abschnitten usw. zu entsprechen).
    2. Lesen Sie Ihren Code (Virus Body Code).
    3. Die ersten Anweisungen entnehmen wir der Opferakte.
    4. Wir hängen sie an den Virencode an (wir speichern die Informationen, die zur Wiederherstellung der Arbeitskapazität erforderlich sind).
    5. Wir fügen dem Virencode den Übergang zu der Anweisung hinzu, die die Ausführung des Opfercodes fortsetzt. Nachdem der Virus seinen eigenen Code ausgeführt hat, führt er den Prolog des Opfercodes korrekt aus.
    6. Erstellen Sie einen neuen Abschnitt, schreiben Sie dort den Virencode und bearbeiten Sie den Header.
    7. Anstelle dieser ersten Anweisungen stellen wir den Schalter auf den Virencode.

    Dies ist eine Option für einen vollständig korrekten Virus, der eine ausführbare Datei infiltrieren, nichts zerstören, den Code geheim ausführen und die Ausführung an das Host-Programm zurückgeben kann. Jetzt fangen wir ihn.

    Detektoranatomie

    Plötzlich taucht aus dem Nichts ein Ritter auf einem weißen Computer auf, in der linken Hand einen Debugger und in der rechten einen Disassembler, einen Antiviren-Programmierer. Woher kam er? Sie haben es natürlich erraten. Mit hoher Wahrscheinlichkeit erschien er dort aus dem "angrenzenden Gebiet". Der Bereich Antivirus in Bezug auf die Programmierung wird von den Fachleuten sehr geschätzt, da diese Leute mit sehr ausgefeilten Algorithmen und unter beengten Bedingungen arbeiten müssen. Überzeugen Sie sich selbst: Sie haben hunderttausende Kopien einer Infektion und eine ausführbare Datei zur Verfügung. Sie sollten fast in Echtzeit arbeiten, und die Kosten für den Fehler sind sehr hoch.

    Für ein Antivirus-Programm sowie für alle Zustandsautomaten, die eine binäre Ja / Nein-Entscheidung treffen (infiziert / fehlerfrei), gibt es zwei Arten von Fehlern: falsch positiv und falsch negativ (die Datei wurde fälschlicherweise als infiziert erkannt, die infizierte Datei fälschlicherweise übersprungen). Es ist klar, dass die Gesamtzahl der Fehler in jedem Szenario reduziert werden muss, aber falsch-negativ für das Virenschutzprogramm ist viel unangenehmer als falsch-positiv. "Schalten Sie nach dem Herunterladen des Torrents das Virenschutzprogramm aus, bevor Sie das Spiel installieren" - ist das bekannt? Dies ist "falsch positiv" - crack.exe, das etwas in die ausführbare EXE-Datei für einen ausreichend intelligenten heuristischen Analysator schreibt (siehe unten), sieht aus wie ein Virus. Wie das Sprichwort sagt: "Es ist besser zu überholen als nicht zu beenden."

    Ich denke, Sie müssen die Komponenten eines modernen Antivirenprogramms nicht beschreiben, sie drehen sich alle um eine Funktion - einen Antiviren-Detektor. Ein Monitor, der Dateien im laufenden Betrieb überprüft, Datenträger überprüft, E-Mail-Anhänge überprüft, bereits gescannte Dateien in Quarantäne stellt und speichert - all dies ist die Bindung des Haupterkennungskerns. Die zweite Schlüsselkomponente des Antivirus ist die nachgeladene Feature-Datenbank, ohne die es nicht möglich ist, das Antivirus auf dem neuesten Stand zu halten. Die dritte Komponente, die sehr wichtig ist, aber eine separate Artikelserie verdient, überwacht das System auf verdächtige Aktivitäten.

    Also (wir betrachten klassische Viren), am Eingang haben wir eine ausführbare Datei und eine von hunderttausenden potenziellen Viren. Lassen Sie uns erkennen. Dies sei ein Teil des ausführbaren Virencodes:
    XX XX XX XX XX XX	; начало вируса длиной N байт . . . 
    68 2F 2F 73 68		push 0x68732f2f   ; “hs//”
    68 2F 62 69 6E		push 0x6e69622f   ; “nib/”
    8B DC			mov ebx, esp ; в ESP теперь адрес строки «/bin/sh»
    B0 11			mov al, 11
    CD 80			int 0x80
    XX XX XX XX		; конец вируса длиной M байт . . .  
    

    Ich möchte nur ein paar Opcodes nehmen (68 2F 2F 73 68 68 2F 62 69 6E 8B DC B0 11 CD 80) und nach dieser Byte-Zeichenfolge in der Datei suchen. Wenn du es findest, bin ich erwischt, du Bastard. Aber leider stellt sich heraus, dass das gleiche Paket von Bytes auch in anderen Dateien gefunden wird (Sie wissen ja nie, wer die Shell aufruft), und selbst solche Zeilen, um nach "stotych" zu suchen, wenn Sie jeweils suchen, hilft keine Optimierung. Die einzige schnelle und korrekte Möglichkeit, nach einer solchen Zeile in einer Datei zu suchen, besteht darin, ihre Existenz mit einem festen Versatz zu überprüfen. Woher bekommen Sie es?

    Wir erinnern uns an den „angrenzenden Bereich“ - insbesondere an die Orte, an denen sich das Virus befindet und wie es die Kontrolle auf sich selbst überträgt:
    • Der Virus ist in den Abstand zwischen dem Header und dem Anfang des ersten Abschnitts eingebettet. In diesem Fall können Sie das Vorhandensein dieser Byte-Zeichenfolge durch den Versatz
      "Header-Länge" + N überprüfen (wobei N die Anzahl der Bytes vom Beginn des Virus bis zur Byte-Zeichenfolge ist).
    • Das Virus liegt in einem neuen, separaten Abschnitt. In diesem Fall können Sie die Existenz einer Byte-Zeichenfolge ab dem Beginn aller Abschnitte mit Code überprüfen
    • Der Virus infiltrierte den Abstand zwischen dem Ende des Codes und dem Ende des Codeabschnitts. Sie können einen negativen Versatz vom Ende des Abschnitts verwenden, z. B. "Ende des Codeabschnitts" - M (wobei M die Anzahl der Bytes vom Ende der Bytefolge bis zum Ende des Viruscodes ist) - "Länge der Bytefolge"

    Nun von dort über die Übertragung der Kontrolle:
    • Der Virus hat seine Anweisungen direkt über die Anweisungen in Entry Point geschrieben. In diesem Fall suchen wir nach einer Byte-Zeichenfolge, die sich einfach aus dem Versatz „Einstiegspunkt“ + N zusammensetzt (wobei N die Anzahl der Bytes vom Beginn des Virus bis zur Byte-Zeichenfolge ist).
    • Der in Entry Point JMP aufgezeichnete Virus befindet sich auf seinem Körper. In diesem Fall müssen Sie zuerst berechnen, wo dieser JMP sucht, und dann nach der Byte-Zeichenfolge am Offset "JMP-Übergangsadresse" + N suchen (wobei N die Anzahl der Bytes vom Beginn des Virus bis zur Byte-Zeichenfolge ist).

    Ich habe es satt, "Byte-String" zu schreiben, es ist von variabler Länge, es ist unpraktisch, es in der Datenbank zu speichern, und es ist absolut optional, daher verwenden wir anstelle des Byte-Strings dessen Länge plus CRC32. Ein solcher Datensatz ist sehr kurz und der Vergleich ist schnell, da der CRC32-Algorithmus nicht langsam ist. Es macht keinen Sinn, Widerstand gegen die Kollision von Prüfsummen zu betreiben, da die Wahrscheinlichkeit einer Kollision über feste Verschiebungen gering ist. Außerdem ist der Fehler selbst im Falle einer Kollision vom Typ "falsch positiv", was nicht so schlimm ist. Wir fassen all das zusammen. Hier ist ein Beispiel für eine Datensatzstruktur in unserer Antiviren-Datenbank:
    1. Virus ID
    2. Flags, die angeben, wo der Offset vom EP gelesen werden soll, vom Ende des Headers, vom Ende des ersten Abschnitts, vom Anfang aller Abschnitte, von der Adresse des JMP-Befehls zum EP usw.)
    3. Offset
    4. Signaturlänge (Lsig)
    5. CRC32-Signaturen (CRCsig)

    Wir optimieren die Eingabe (wir lassen nur die Signaturen, die in die gegebene Datei "passen", und bereiten sofort aus dem Header die Menge der erforderlichen Offsets vor) und dann:
    { # для всех подходящих записей 
    -	на основании флагов вычисляем базовое смещение в файле (начало кодовой секции, entry point и т.п.)
    -	прибавляем к нему offset
    -	читаем Lsig байт
    -	считаем от них CRC32
    -	если совпало – мы поймали вирус
    }
    

    Hurra, hier ist unser erstes Virenschutzprogramm. Es ist ziemlich cool, denn mit Hilfe einer ziemlich vollständigen Datenbank von Signaturen, normalerweise ausgewählten Flags und einer guten Optimierung kann dieser Detektor sehr schnell 95% aller Infektionen abfangen (die überwiegende Mehrheit der modernen Malware sind einfach ausführbare Dateien, ohne jegliche Veränderbarkeit). Dann beginnt das Spiel "Wer wird die Signaturdatenbank schneller aktualisieren" und "Wem werden sie früher eine neue Instanz von etwas Mist senden".

    Das Sammeln und Katalogisieren dieser "Gemeinheit" ist eine sehr nicht triviale Aufgabe, aber für eine qualitativ hochwertige Prüfung des Detektors unbedingt erforderlich. Das Sammeln einer Referenzdatenbank mit ausführbaren Dateien ist keine leichte Aufgabe: Versuchen Sie, alle Instanzen infizierter Dateien zu finden (in komplexen Fällen in mehreren Fällen), katalogisieren Sie sie, mischen Sie sie mit „sauberen“ Dateien und führen Sie regelmäßig einen Detektor aus, um Erkennungsfehler zu erkennen. Eine solche Datenbank existiert seit Jahren und ist ein sehr wertvolles Kapital von Antiviren-Unternehmen. Vielleicht irre ich mich und verstehe es wirklich (alle Arten von Online-Virensuchdiensten sind durchaus in der Lage, ein Analogon zu liefern), aber als ich mich mit diesem Problem befasste, war es unmöglich, so etwas zu bekommen (zumindest unter Linux).

    Heuristischer Analysator

    Was für ein schreckliches Wort - "heuristischer Analysator", jetzt werden Sie es nicht einmal in den Antivirus-Benutzeroberflächen sehen (es macht den Benutzern wahrscheinlich Angst). Dies ist einer der interessantesten Teile des Antivirus, da alles enthalten ist, was nicht in eine der Engines passt (weder Signatur noch Emulator), und es aussieht wie ein Arzt, der sieht, dass der Patient hustet und niest, aber eine bestimmte Krankheit identifiziert kann nicht. Dies ist der Code, der die Datei auf einige charakteristische Anzeichen einer Infektion überprüft. Beispiele für solche Zeichen:
    • Falscher (durch den Virus beschädigter, aber funktionsfähiger) Dateiheader
    • JMP direkt am Einstiegspunkt
    • "Rwx" im Codeabschnitt

    Nun und so weiter. Eine Heuristik gibt nicht nur Aufschluss darüber, ob eine Infektion vorliegt, sondern kann Ihnen auch bei der Entscheidung helfen, ob eine umfassendere Dateianalyse durchgeführt werden soll. Jedes Symptom hat ein anderes Gewicht, von "verdächtig" bis "Ich weiß nicht, was, aber die Datei ist genau infiziert". Es sind diese Zeichen, die die Mehrzahl der falsch positiven Fehler ergeben. Vergessen Sie nicht, dass Heuristiken Virenschutzunternehmen Kopien potenzieller Viren zur Verfügung stellen können. Eine Heuristik hat funktioniert, aber es wurde nichts Konkretes gefunden? Die Datei ist also definitiv ein Kandidat für den Versand an ein Antiviren-Unternehmen.

    Interspezifische Interaktion und Evolution

    Wie wir gesehen haben, benötigt der Detektor für einen schnellen und genauen Vergleich die Signatur-Bytes selbst und ihren Offset. Oder in einer anderen Sprache den Inhalt des Codes und die Adresse seines Speicherorts in der Hostdatei. Daher ist klar, wie sich die Idee, den ausführbaren Code von Viren zu verbergen, auf zwei Arten entwickelt hat:
    • den Code des Virus selbst verstecken;
    • versteckte seinen Einstiegspunkt.

    Das Verbergen des Viruscodes führte zum Auftreten polymorpher Engines. Dies sind Engines, mit denen der Virus seinen Code in jeder neuen Generation ändern kann. In jeder neu infizierten Datei mutiert der Viruskörper und versucht, die Erkennung zu erschweren. Daher ist es schwierig, den Inhalt der Signatur zu erstellen.

    Das Verschleiern eines Einstiegspunkts (Entry Point Obscuring) diente als Anreiz für das Auftreten von automatischen Disassemblern in Virus-Engines, um zumindest Übergangsanweisungen zu ermitteln. Der Virus versucht, die Stelle zu verbergen, an der der Übergang zu seinem Code stattfindet, und verwendet dabei die Datei, die letztendlich zum Übergang führt: JMP, CALL, alle Arten von RET, Adresstabellen usw. Das Virus macht es daher schwierig, die Signaturverzerrung anzuzeigen.

    Wir werden uns im zweiten Artikel, den ich in naher Zukunft schreiben werde, einige Algorithmen solcher Engines und des Detektors genauer ansehen.

    Parallel zur Entwicklung der Viren-Engines und ihrer Detektoren wurde der kommerzielle Schutz von ausführbaren Dateien aktiv weiterentwickelt. Eine große Anzahl kleiner kommerzieller Programme erschien, und die Entwickler benötigten Engines, die es ihnen ermöglichten, eine EXE-Datei zu nehmen und sie in einen „Umschlag“ zu „wickeln“, mit dem eine gültige Seriennummer sicher generiert werden kann. Und wer kann den ausführbaren Code verstecken und in ausführbare Dateien implementieren, ohne dass die Funktionalität verloren geht? Das ist richtig, die gleichen Entwickler aus dem "angrenzenden Feld". Daher ist das Schreiben eines guten polymorphen Virus und der Schutz der ausführbaren Datei durch Scharniere eine sehr ähnliche Aufgabe, bei der dieselben Algorithmen und Tools verwendet werden. Ähnlich verhält es sich auch mit der Analyse von Viren, dem Erstellen von Signaturen und dem Hacken von kommerzieller Software.

    Im Internet gibt es mehrere Seiten zum Thema "Klassifizierung von Computerviren". Wir waren uns jedoch einig, dass ein Virus sich im System selbst reproduzieren kann und wofür ein Datenträger erforderlich ist. Daher sind alle Arten von Malware-Trojaner-Rootkits keine Viren, sondern die Art des Payload-Codes, den ein Virus selbst tragen kann. Es kann nur eine Klassifizierung von Computerviren für die im Artikel beschriebenen Technologien geben: polymorphe und nicht polymorphe Viren. Das heißt, von Generation zu Generation wechseln oder nicht.

    Der in dem Artikel betrachtete Detektor erkennt leicht nicht polymorphe (als monomorphe Viren bezeichnete) Viren. Nun, der Übergang zu polymorphen Viren ist eine hervorragende Gelegenheit, um diesen Artikel zu Ende zu bringen, und verspricht, im zweiten Teil auf interessantere Methoden zum Verbergen von ausführbarem Code zurückzukommen .

    Jetzt auch beliebt: