Praxisarbeit. Entwicklung eines PE Editors. Sebastian Porst. Betreuung: Professor Künkler

Ähnliche Dokumente
Dokumentation IBIS Monitor

Arbeiten mit UMLed und Delphi

Nach der Anmeldung im Backend Bereich landen Sie im Kontrollzentrum, welches so aussieht:

Artikel Schnittstelle über CSV

! " # $ " % & Nicki Wruck worldwidewruck

Suche schlecht beschriftete Bilder mit Eigenen Abfragen

Datensicherung. Beschreibung der Datensicherung

Objektorientierte Programmierung für Anfänger am Beispiel PHP

Datei Erweiterungen Anzeigen!

WordPress. Dokumentation

Er musste so eingerichtet werden, dass das D-Laufwerk auf das E-Laufwerk gespiegelt

Handbuch B4000+ Preset Manager

Qt-Projekte mit Visual Studio 2005

Erstellen von x-y-diagrammen in OpenOffice.calc

WOT Skinsetter. Nun, erstens, was brauchen Sie für dieses Tool zu arbeiten:

Anleitung directcms 5.0 Newsletter

Der Kalender im ipad

S/W mit PhotoLine. Inhaltsverzeichnis. PhotoLine

SANDBOXIE konfigurieren

DOKUMENTATION VOGELZUCHT 2015 PLUS

1 Vom Problem zum Programm

Neue Schriftarten installieren

Jederzeit Ordnung halten

Whitepaper. Produkt: address manager David XL Tobit InfoCenter AddIn für den address manager Zuordnung

Professionelle Seminare im Bereich MS-Office

Kurzfassung der Studienarbeit

.NET Code schützen. Projekt.NET. Version 1.0

Leitfaden zur ersten Nutzung der R FOM Portable-Version für Windows (Version 1.0)

Datenbanken Kapitel 2

Aber mancher braucht diese Funktionalität halt, doch wo ist sie unter Windows 8 zu finden?

Anleitung über den Umgang mit Schildern

Hilfe zur Dokumentenverwaltung

Um ein solches Dokument zu erzeugen, muss eine Serienbriefvorlage in Word erstellt werden, das auf die von BüroWARE erstellte Datei zugreift.

TTS - TinyTimeSystem. Unterrichtsprojekt BIBI

VB.net Programmierung und Beispielprogramm für GSV

Zahlen auf einen Blick

Viele Bilder auf der FA-Homepage

Handbuch Fischertechnik-Einzelteiltabelle V3.7.3

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren

Beispiel Shop-Eintrag Ladenlokal & Online-Shop im Verzeichnis 1

Adobe Photoshop. Lightroom 5 für Einsteiger Bilder verwalten und entwickeln. Sam Jost

TeamSpeak3 Einrichten

Einkaufslisten verwalten. Tipps & Tricks

Kurzanleitung RACE APP

Hilfedatei der Oden$-Börse Stand Juni 2014

Grundlagen von Python

Kapitel 3 Frames Seite 1

Bauteilattribute als Sachdaten anzeigen

Microsoft Access 2013 Navigationsformular (Musterlösung)

Datenübernahme von HKO 5.9 zur. Advolux Kanzleisoftware

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen

.htaccess HOWTO. zum Schutz von Dateien und Verzeichnissen mittels Passwortabfrage

EasyWk DAS Schwimmwettkampfprogramm

Kurzeinführung Excel2App. Version 1.0.0

MORE Profile. Pass- und Lizenzverwaltungssystem. Stand: MORE Projects GmbH

Erzherzog Johann Jahr 2009

Kommunikations-Management

Handbuch. NAFI Online-Spezial. Kunden- / Datenverwaltung. 1. Auflage. (Stand: )

OP-LOG

Web-Kürzel. Krishna Tateneni Yves Arrouye Deutsche Übersetzung: Stefan Winter

Historical Viewer. zu ETC5000 Benutzerhandbuch 312/15

Anleitung zum erfassen von Last Minute Angeboten und Stellenangebote

SICHERN DER FAVORITEN

Stellen Sie bitte den Cursor in die Spalte B2 und rufen die Funktion Sverweis auf. Es öffnet sich folgendes Dialogfenster

Einrichten einer Festplatte mit FDISK unter Windows 95/98/98SE/Me

Kurzanleitung zu. von Daniel Jettka

etutor Benutzerhandbuch XQuery Benutzerhandbuch Georg Nitsche

Anleitung Union Homepage

Mediator 9 - Lernprogramm

L10N-Manager 3. Netzwerktreffen der Hochschulübersetzer/i nnen Mannheim 10. Mai 2016

Anzeige von eingescannten Rechnungen

Dossier: Rechnungen und Lieferscheine in Word

Bedienungsanleitung. Matthias Haasler. Version 0.4. für die Arbeit mit der Gemeinde-Homepage der Paulus-Kirchengemeinde Tempelhof

Handbuch zur Anlage von Turnieren auf der NÖEV-Homepage

Enigmail Konfiguration

Bilder Schärfen und Rauschen entfernen

1 Dokumentenmanagement

Software Engineering Klassendiagramme Assoziationen

In diesem Tutorial lernen Sie, wie Sie einen Termin erfassen und verschiedene Einstellungen zu einem Termin vornehmen können.

Anleitung zur Erstellung einer Batchdatei. - für das automatisierte Verbinden mit Netzlaufwerken beim Systemstart -

Dokumentation. Mindestanforderungen: Das Board

Microsoft PowerPoint 2013 Folien gemeinsam nutzen

Task: Nmap Skripte ausführen

Registrierungsanleitung Informatik-Biber

Virtueller Seminarordner Anleitung für die Dozentinnen und Dozenten

Wie halte ich Ordnung auf meiner Festplatte?

GEORG.NET Anbindung an Ihr ACTIVE-DIRECTORY

Kleines Handbuch zur Fotogalerie der Pixel AG

Speichern. Speichern unter

SEP 114. Design by Contract

Dieser Ablauf soll eine Hilfe für die tägliche Arbeit mit der SMS Bestätigung im Millennium darstellen.

Erstellen eines Wordpress-Blogs

Anton Ochsenkühn. amac BUCH VERLAG. Ecxel für Mac. amac-buch Verlag

4D Server v12 64-bit Version BETA VERSION

Diese Ansicht erhalten Sie nach der erfolgreichen Anmeldung bei Wordpress.

ASA Schnittstelle zu Endian Firewall Hotspot aktivieren. Konfiguration ASA jhotel

Agentur für Werbung & Internet. Schritt für Schritt: Newsletter mit WebEdition versenden

Erweiterung der Aufgabe. Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen:

MSDE 2000 mit Service Pack 3a

Wo möchten Sie die MIZ-Dokumente (aufbereitete Medikamentenlisten) einsehen?

Transkript:

Praxisarbeit Entwicklung eines PE Editors Sebastian Porst Juli 2005 Betreuung: Professor Künkler Fachbereich Design und Informatik Fachhochschule Trier University of Applied Sciences Sebastian Porst - Entwicklung eines PE Editors Seite 1 von 61

FACHHOCHSCHULE TRIER UNIVERSITY OF APPLIED SCIENCES Fachbereich INFORMATIK Autor: Sebastian Porst Titel: Entwicklung eines PE Editors Studiengang: Bachelor of Science Betreuung: Professor Künkler Fachbetreuung: Professor Künkler Juli 05 Es wird hiermit der Fachhochschule Trier (University of Applied Sciences) die Erlaubnis erteilt, die Arbeit zu nicht-kommerziellen Zwecken zu verteilen und zu kopieren. Sebastian Porst, 2005 Unterschrift des Autors Sebastian Porst - Entwicklung eines PE Editors Seite 2 von 61

Kurzfassung Unter Microsoft Windows haben die meisten ausführbaren Dateien wie zum Beispiel EXE, DLL oder OCX Dateien 1 im Grunde das selbe Dateiformat. Dateien dieses Typs werden unter dem Namen Portable Executable (PE) Dateien zusammengefasst. In dieser Arbeit ging es darum einen Editor für diese PE Dateien zu entwickeln, mit dem es möglich sein soll, die internen Strukturen dieser Dateien auf einer möglichst hohen Abstraktionsebene anschauen und editieren zu können. Besonderen Wert sollte bei der Entwicklung des PE Editors darauf gelegt werden, dass das eigentliche Hauptprogramm mit einer strikten Plugin-Architektur arbeitet. Das bedeutet, dass das Hauptprogramm möglichst wenig, die Plugins jedoch möglichst viel Anwendungslogik enthalten. Abstract Most executable files in Microsoft Windows like EXE, DLL or OCX files basically share the same file format. Files of this format are called Portable Executable (PE) files. The goal for this paper was to develop an editor for these PE files that allows the user to view and modify the internal structures of these files at a high abstraction level. Special consideration during the development of the PE Editor was given to the flexible plugin architecture that's supposed to drive most of the program. That means the plugins should contain most of the business logic while the actual main program should not do much more than schedule the plugins and allow them to cooperate in an effective way. 1 Siehe Glossar Sebastian Porst - Entwicklung eines PE Editors Seite 3 von 61

Inhaltsverzeichnis Inhalt 1. Einleitung... 6 2. Aufgabenstellung und Zielsetzung... 6 3. Die Gridkomponente FooGrid... 7 3.1 Übersicht... 7 3.2 Beschreibung... 7 3.3 Eine Übersicht über die einzelnen Klassen... 8 3.3.1 Die Klasse FooGrid... 8 3.3.2 Die TableModels... 9 3.3.3 Der Zellencontainer... 10 3.3.4 Die Zellengrößen... 10 3.3.5 Die Zellen... 10 3.3.6 Die Cell Models... 12 3.3.7 Die Paint Policies... 12 3.3.8 Die Edit Policies... 13 3.3.9 Die Selection Policies... 14 3.3.10 Die RightClick Policies... 14 3.3.11 Die verbleibenden Klassen... 15 3.4 Überlegungen zum Design... 15 3.5 Überlegungen zur Performance... 16 4. Migration von PeLib von C++ nach C++.NET... 17 4.1 Übersicht... 17 4.2 Motivation... 17 4.3 Details zur Migration... 18 4.4 Fazit... 19 5. Eine kurze Übersicht über das PE Dateiformat... 20 5.1 Der MZ Header... 20 5.2 Der PE Header... 21 5.3 Das Export Directory... 23 5.4 Das Import Directory... 24 5.5 Das Resource Directory... 25 5.6 Das BaseReloc Directory... 27 5.7 Das TLS Directory... 27 5.8 Das BoundImport Directory... 28 5.9 Das IAT Directory... 28 5.10 Das COM+ Runtime Header Directory... 29 6. Der Editor... 30 6.1 Das Basissystem... 30 6.1.1 Das Hauptprogramm... 30 6.1.2 Die Plugin Registry... 31 6.1.3 Die Logdatei... 32 6.1.4 Die Einstellungsdatei... 33 6.2 Die Pluginarchitektur... 34 6.3 Die Plugins... 36 6.3.1 Das MZ Header Plugin... 38 6.3.2 Das PE Header Plugin... 40 6.3.3 Das Export Directory Plugin... 42 Sebastian Porst - Entwicklung eines PE Editors Seite 4 von 61

6.3.4 Das ImportDirectory Plugin... 43 6.3.5 Das ResourceDirectory Plugin... 43 6.3.6 Das RelocationsDirectory Plugin... 44 6.3.7 Das DebugDirectory Plugin... 45 6.3.8 Das TlsDirectory Plugin... 45 6.3.9 Das BoundImportDirectory Plugin... 45 6.3.10 Das IATDirectory Plugin... 46 6.3.11 Das COM Header Directory Plugin... 46 6.3.12 Das OffsetConversion Plugin... 47 6.3.13 Das Hex View Plugin... 48 6.3.14 Das File Locator Plugin... 48 6.3.15 Die Standard GUI... 49 6.3.16 Die Kontextmenüs der Grids... 51 6.3.17 Die Menüs des Export Buttons... 52 7. Die Lokalisierungsstrategie... 54 7.1 Übersicht... 54 7.2 Übersicht über das.net Lokalisierungs-/Internationalisierungskonzept... 54 7.3 Anpassen von Strings die in Fenstern sichtbar sind... 55 7.4 Anpassen von Strings, die sonstwo zum Einsatz kommen... 55 7.5 Anpassen von anderen Elementen... 56 7.6 Ausnahmen... 56 8. Die Unit-Testing Strategie... 57 8.1 Übersicht... 57 8.2 Theoretische Grundlagen... 57 8.3 Eine Kurzübersicht über nunit... 57 8.4 Die Unit-Tests für PeLib.NET... 58 9. Fazit... 59 10. Anhang... 60 10.1 Glossar... 60 10.2 Hinweise zu den UML Diagrammen... 60 10.3 Literaturhinweise...61 Sebastian Porst - Entwicklung eines PE Editors Seite 5 von 61

1. Einleitung In vielen Bereichen der Programmierung muss man sich mit der Struktur von PE Dateien auseinander setzen. Beispiele hierfür wären zum Beispiel der Compilerbau, bei dem im letzten Schritt die ausführbare Datei erstellt werden muss. Ein anderes Beispiel ist die Virenbekämpfung, bei der oftmals ausführbare Dateien analysiert werden müssen. Ein erster Schritt ist hierbei die statische Analyse der Interna der Datei. Auch beim Debuggen von Programmen muss man ab und zu tief ins System einsteigen und eine gute Kenntnis des Formats der zu debuggenden Datei ist hier oft hilfreich. Die Untersuchung von Binärdateien auf der niedrigsten Abstraktionsebene, dem Hex Editor, ist jedoch ausgesprochen mühsam, da direkt auf den einzelnen Bytes der Binärdatei gearbeitet wird und der Hex Editor keinerlei interne Strukturen der Binärdateien kennt. Mit Hilfe eines PE Editors kann von der Byte-Ebene abstrahiert werden. Es ist mit solch einem Tool also möglich die internen Strukturen von PE Dateien auf einfach und verständliche Weise einzusehen und zu verändern. Dieses Tool erleichtert also die Arbeit mit PE Dateien ungemein. 2. Aufgabenstellung und Zielsetzung Im Rahmen dieser Arbeit sollte ein PE Editor entwickelt werden, mit dessen Hilfe es möglich sein sollte, Portable Executable Dateien anzusehen und zu editieren. Der Großteil des Programms wurde mit C# 2.0 geschrieben, wobei als IDE das von Microsoft kostenlos bereitgestellte Visual Studio 2005 Beta 2 benutzt wurde. Neben C# kommt auch C++ und C++.NET zum Einsatz, da PeLib 2, die PE Bibliothek in der die meisten Funktionen zum Arbeiten mit PE Dateien gekapselt sind, in ISO-C++ geschrieben wurde. Auch für das Arbeiten mit C++ wurde Visual Studio 2005 Beta 2 verwendet. Im ersten Schritt musste ein Weg gefunden werden PeLib aus C# benutzen zu können. Dazu wurde wie später detailliert beschrieben wird der Umweg über C++.NET gewählt. Als das geschehen war, musste ein Weg gefunden werden die Daten, die PeLib zur Verfügung stellt, für den Benutzer leicht verständlich zugänglich und editierbar zu machen. 2 PeLib ist eine von mir entwickelte Open-Source C++ Bibliothek zur Verarbeitung von PE Dateien. Sie kann auf http://www.pelib.com heruntergeladen werden. Sebastian Porst - Entwicklung eines PE Editors Seite 6 von 61

3. Die Gridkomponente FooGrid 3.1 Übersicht Abb. 1: Sieb des Erasthostenes dargestellt in der FooGrid Komponente Bevor die eigentliche Arbeit am PE Editor begonnen werden konnte, musste zuvor ein.net GridControl Steuerelement ähnlich der aus Java bekannten JTable Komponente entwickelt werden. Dies lag daran, dass die meisten Daten, die vom PE Editor aus PE Dateien ausgelesen und auf der GUI zu sehen sind, am besten in Tabellenform dargestellt werden. Das im.net Framework bereits mitgelieferte GridControl namens DataGridView, welches hauptsächlich zur Visualisierung von Tabellen aus Datenbanken gedacht ist, stellte sich vor allem hinsichtlich der Konfigurierbarkeit der Zellen leider für diesen Zweck als vollkommen ungeeignet heraus. Dieses Control wurde als.net Bibliothek in C# verwirklicht. 3.2 Beschreibung Im weiteren folgt nun eine Übersicht über die grobe Klassenstruktur der Komponente FooGrid. Im ersten UML Diagramm 3 wurde bewusst auf Details wie Elemente, Methoden und unwichtigere Hilfsklassen verzichtet, um das Diagramm nicht zu groß werden zu lassen. Details werden bei der Beschreibung der einzelnen Klassen und Interfaces weiter unten im Text hinzugefügt. 3 Zu allen UML Diagrammen, die im Verlauf dieser Arbeit präsentiert werden, ist unbedingt der Abschnitt über UML im Anhang zu beachten. Sebastian Porst - Entwicklung eines PE Editors Seite 7 von 61

Abb. 2: Die Klassenstruktur der FooGrid Komponente 3.3 Eine Übersicht über die einzelnen Klassen 3.3.1. Die Klasse FooGrid Die zentrale Klasse des Steuerelements ist die von UserControl abgeleitete Klasse FooGrid. Diese Klasse ist das eigentliche GridControl und enthält alle wichtigen Eigenschaften und Methoden, die zur Anzeige des Gitters und der Zellen oder zur Interaktion mit dem Benutzer benötigt werden. Das wichtigste Element der Klasse FooGrid ist sicherlich das TableModel vom Typ AbstractTableModel. Hier kann ein beliebiges TableModel gesetzt werden, um das Verhalten des Grids komplett zu verändern. Die anderen Elemente werden zum Beispiel benötigt, um dem Benutzer das Verändern der Zellgrößen mit der Maus zu ermöglichen oder im Grid, entweder über Scrollbars oder Tastatureingabe, zu navigieren. Sebastian Porst - Entwicklung eines PE Editors Seite 8 von 61

Abb. 3: Die Elemente und Methoden der Klasse FooGrid 3.3.2. Die TableModels Alle TableModels, die vom FooGrid benutzt werden sollen, müssen von der Klasse AbstractTableModel abgeleitet sein. Die Hauptaufgabe eines TableModels besteht in der Verwaltung der im Grid dargestellten Zellen und der Größenangaben für die einzelnen Reihen und Spalten des Grids. Bisher kennt FooGrid nur ein einziges konkretes TableModel, das so genannte DefaultTableModel, welches jedoch allen vom PE Editor gestellten Ansprüchen vollkommen genügt. Sebastian Porst - Entwicklung eines PE Editors Seite 9 von 61

Abb. 4: Klassendiagramm der Klasse AbstractTableModel 3.3.3. Der Zellencontainer Jedes TableModel besitzt einen CellContainer, welcher die Zellen im Grid organisiert. Wie ein CellContainer intern arbeitet wird nicht spezifiziert. Nach außen hin muss jedoch der Eindruck entstehen, dass er über eine Reihe von Zellen in Zeilen und Spalten verfügt, die den Zeilen und Spalten im Grid entsprechen. 3.3.4. Die Zellengrößen Parallel, aber unabhängig von den einzelnen Zellen, müssen vom TableModel die Zeilen- und Spaltengrößen verwaltet werden. Dies geschieht durch Klassen vom Typ AbstractCellDimensions und Klassen, die von dieser Klasse abgeleitet werden. Da alle Zellen in einer Zeile gleich hoch und alle Zellen in einer Spalte gleich breit sein müssen, genügt es pro Zeile und Spalte einen Wert zu speichern. 3.3.5. Die Zellen Abb. 5: Klassendiagramm für den AbstractCellContainer Abb. 6: Klassendiagramm der Klasse AbstractCellDimensions Jede Zelle, die im Grid dargestellt wird, ist vom Typ GridCell. Diese Klasse bietet jedoch keine eigene Funktionalität, die über die logische Bindung eines CellModels und vier Policy Sebastian Porst - Entwicklung eines PE Editors Seite 10 von 61

Typen zu einer zusammengehörigen Einheit hinausgeht. Durch die komplette Entkoppelung der vier Policytypen voneinander können beliebige Policies miteinander kombiniert werden, um neue Zelltypen zu schaffen. So benutzen zum Beispiel fast alle Zelltypen, die bereits im FooGrid enthalten sind, die TextCellPaintPolicy, um sich selbst zu zeichnen. Eine erneute Implementierung z.b. einer ComboBoxCellPaintPolicy entfällt. Die bereits vorhandenen Spezialisierungen von GridCell bieten keine zusätzliche Funktionalität. Sie existieren lediglich zur Erleichterung der Benutzung des Grids. Gäbe es sie nicht, so müsste der Benutzer des Grid alle Zellen als Instanzen der Klasse GridCell anlegen und jedes mal alle 5 Parameter (Model + 4 Policies) explizit angeben. Die spezialisierten Klassen erlauben eine viel bequemere Nutzung durch das Anlegen von Zellen bei denen nur 1 oder 2 Parameter angegeben werden müssen und die anderen Parameter mit Standardwerten initialisiert werden. FooGrid bietet zur Zeit vier verschiedene Zelltypen: - TextCell ist eine ganz normale Zelle. Das Anzeigen der Zelle geschieht durch einfache Ausgabe des Wertes an der richtigen Position. Editiert werden kann die Zelle mit Hilfe einer Textbox die erscheint, sobald der Benutzer durch Doppelklick auf die Zelle die Absicht signalisiert hat, dass er diese editieren möchte. - TextCellValid ist eine Zelle, die ihren aktuellen Wert auf Korrektheit prüfen kann. Je nachdem, ob dieser Wert korrekt oder inkorrekt ist, wird der Wert im Grid in verschiedenen Farben dargestellt. Ist der Wert inkorrekt, erscheint außerdem innerhalb der Zelle ein kleiner Button, über den der Wert der Zelle vom Benutzer auf einfacher Weise korrigiert werden kann. - Zellen vom Typ ComboBoxCell bieten eine Auswahl eines Wertes für die Zelle mit Hilfe einer ComboBox an. Wenn der Benutzer also den Wert der Zelle editieren möchte, so ist er dabei auf die vorgeschlagenen Werte beschränkt. - Zellen vom Typ DialogCell werden mit Hilfe eines Dialogs editiert, der erscheint, sobald der Anwender die Zelle editieren möchte. Dies erlaubt komplexere Editiermöglichkeiten für den Abb. 7: Klassendiagramm der Klasse GridCell Wert der Zelle. Sebastian Porst - Entwicklung eines PE Editors Seite 11 von 61

3.3.6. Die Cell Models Jede Zelle benötigt ein CellModel, welches alle Informationen über die Zelle enthält. Teil dieser Informationen sind zum Beispiel der aktuelle Wert der Zelle, die Schriftart oder die Hintergrund- und Textfarbe, die beim Zeichnen der Zelle verwendet werden soll, die Callback-Funktion, die aufgerufen werden soll, sobald sich der Wert der Zelle ändert und einiges mehr. Obwohl das schon eine ganze Menge an Information ist, wird in einigen Fällen doch noch etwas mehr benötigt. Hier kommen die spezialisierten CellModel zum Einsatz, die von der Klasse AbstractCellModel ableiten. Das ComboBoxCellModel benötigt zum Beispiel noch weitergehende Informationen über alle Elemente, die in der ComboBox beim Editieren einer Zelle anzeigt werden. 3.3.7. Die Paint Policies Abb. 8: Klassendiagramm der CellModels Mit Hilfe der Paint Policies wird das Aussehen von Zellen im Grid festgelegt. Jedes mal, wenn eine Zelle auf dem Bildschirm gezeichnet werden muss, ruft das Grid die Paint Methode der zur Zelle gehörigen PaintPolicy auf und bittet damit quasi diese Zelle sich selbst an den übergebenen Koordinaten zu zeichnen. Der erste der drei Parameter der Paint Methode ist eine Referenz auf das Zellenmodell der Zelle zu der auch die PaintPolicy gehört. Aus diesem Modell benötigt die PaintPolicy zumindest den Wert der Zelle, um diesen auf dem Bildschirm darzustellen. Der zweite Parameter ist der Grafikkontext des Grid Controls auf den die Zelle gezeichnet werden muß. Der dritte und letzte Parameter gibt die Koordinaten der Zelle auf dem Grafikkontext an. Hierhin muss die Zelle von der Paint Policy gezeichnet werden. Sebastian Porst - Entwicklung eines PE Editors Seite 12 von 61

3.3.8. Die Edit Policies Abb. 9: Klassendiagramm der PaintPolicies Die Edit Policies spezifizieren das Verhalten einer Zelle sobald der Benutzer durch einen Doppelklick signalisiert, dass er diese editieren möchte. In diesem Falle ruft das Grid die Edit Funktion der Edit Policy der Zelle auf. Im Parameter args werden eine ganze Menge von Informationen über den aktuellen Status des Grids oder den Editiervorgang übergeben, die für die Policy von Wichtigkeit sein könnten. Drei Edit Policies wurden bisher definiert: - Die TextCellEditPolicy zeigt in der zu editierenden Zelle ein TextBox Steuerelement an, in dem der Benutzer den neuen Wert der Zelle eingeben kann. - Die DialogCellEditPolicy zeigt einen Dialog an, in dem der Benutzer einen Wert wählen kann. Wie dieser Dialog aussieht und wie und aus welchen Werten der Benutzer wählen kann, ist egal, solange die Dialogklasse von CellDialog abgeleitet ist und somit bestimmte Schnittstellen zum Schreiben und Lesen dieses Wertes bereitstellt. Die ComboBoxCellEditPolicy erlaubt dem Benutzer die Auswahl eines Wertes aus einer vorgegebenen Menge aus Werten mit Hilfe einer Combobox. Abb. 10: Klassendiagramm der Edit Policies Sebastian Porst - Entwicklung eines PE Editors Seite 13 von 61

3.3.9. Die Selection Policies Die dritte der vier Cell Policies legt das Verhalten einer Zelle im Falle der Markierung durch den Anwender fest. Select, die einzige Methode, die das Interface ICellSelectionPolicy definiert, hat zwei Parameter: Die Zelle, die selektiert wurde, und ein Flag, das anzeigt, ob die Zelle gerade selektiert wurde oder ob die eventuelle vorherige Selektierung aufgehoben wurde. Aufgerufen wird diese Funktion vom Grid selbst, sobald sich hinsichtlich der Zellselektierung etwas getan hat. Die Funktion für die vorher selektierte Zelle wird mit dem Flagwert false aufgerufen, während die der neu selektierten Zelle mit dem Flagwert true aufgerufen wird. FooGrid kennt bisher nur eine Selection Policy, die HighlightSelectionPolicy, welche die Hintergrundfarbe der Zelle verändert, sobald diese selektiert wurde. 3.3.10. Die RightClick Policies Abb. 11: Klassendiagramm der Selection Policies Die letzte der vier Cell Policies ist zugleich auch die einfachste. Mit ihrer Hilfe lässt sich das Verhalten einer Zelle im Falle eines Rechts-Klicks des Benutzers auf diese Zelle konfigurieren. Das Interface ICellRightClickPolicy enthält nur eine einzige Methode, deren einziger Parameter die Bildschirmkoordinaten des Punktes, auf den der Benutzer geklickt hat, ist. Die einzige RightClick Policy, die vom PE Editor benötigt wird, ist die ContextMenuPolicy. Diese Policy zeigt bei Bedarf ein zur Zelle gehöriges Kontextmenü an dem Punkt, auf den der Benutzer geklickt hat, an. Abb. 12: Klassendiagramm der RightClick Policies Sebastian Porst - Entwicklung eines PE Editors Seite 14 von 61

3.3.11. Die verbleibenden Klassen Es gibt noch eine Reihe weiterer, kleinerer Klassen, die eine untergeordnete Rolle spielen und deshalb an dieser Stelle nur kurz erwähnt werden sollen. - Die Elemente, die in der ComboBox von ComboBox Zellen dargestellt werden sollen, müssen vom Typ IComboCellItem sein. Dies ist nötig, da es notwendig wurde einem Element zwei verschiedene Werte zuzuordnen, je nachdem, ob der Wert direkt im Grid oder in der ComboBox der Zelle angezeigt wurde. - Es gibt eine ganze Reihe von Argumentklassen für Eventhandler, die von den Events im Grid benutzt werden. Ebenso gibt es eine ganze Reihe von Delegates, die zu diesen Events gehören. - Zellen vom Typ TextCellValid prüfen den Wert in ihrer Zelle auf Korrektheit mit Hilfe eines Objekts vom Typ ValidityChecker. - Es gibt eine ganze Reihe vordefinierter Callbackobjekte. Dies sind Objekte, die Callbackfunktionen enthalten. Diese Objekte können Zellen zugewiesen werden und die Callbackfunktion in diesen Objekten wird dann aufgerufen, sobald sich der Inhalt der Zelle ändert. - Anstatt Farben vom Typ System.Drawing.Color zu benutzen, werden selbst gemachte Objekte vom Typ GridColor intern im AbstractCellModel benutzt. Dies wurde notwendig, da die Performance von System.Drawing.Color so schlecht ist, dass knapp 50% der Zeit, die zum Anlegen eines CellModels benötigt wurde, ausschließlich für das Initialisieren von Text- und Hintergrundfarbe verbraucht wurden. Mit Hilfe der GridColor Klasse sank diese Zeit quasi auf null. - Man kann eine bestimmte Formatierung des Wertes einer Zelle mit Hilfe von Objekten vom Typ ICellFormat, die man dem CellModel zuweist, erzwingen. Zwei Klassen, die ICellFormat implementieren, existieren bereits: HexFormatter zur Darstellung eines Wertes in Hexadezimalschreibweise und DecFormatter zur Darstellung eines Wertes als Dezimalwert. 3.4. Überlegungen zum Design Das Klassendesign der FooGrid Komponente erscheint mir äußerst flexibel und doch robust zu sein. Durch die Abkopplung von Grid und Zellen können neue Zelltypen auf einfachste Weise hinzugefügt werden. Wenn aus irgendwelchen Gründen andere Teile, wie zum Beispiel das alte Table Model, durch ein neues Table Model ersetzt werden müssen, ist dies genauso einfach möglich. Es gibt dennoch zumindest ein schwereres Designproblem. Der Benutzer der GridControl kann direkt auf den CellContainer und somit auf die einzelnen Reihen und Spalten des Grids zugreifen (GridName.TableModel.Cells). Hier könnte der Benutzer dann direkt die Insert Methoden aufrufen (anstatt indirekt über das TableModel wie eigentlich vorgesehen) was dazu führen würde, dass die Anzahl der Zellen im Grid nicht mehr mit der Anzahl der Zeilen und Spalten in den CellDimension Objekten übereinstimmen. Fürs erste wurde dieses Problem umgangen, indem der Sichtbarkeitsbereich der Insert Methoden auf internal gesetzt wurde. Dies hat jedoch den Nachteil, dass das Table Model des Grid nicht mit neuen Models aus anderen Assemblies ersetzt werden kann, da andere Assemblies nicht auf die Insert Methoden Sebastian Porst - Entwicklung eines PE Editors Seite 15 von 61

zugreifen können. 3.5. Überlegungen zur Performance Das FooGrid arbeitet zum Anzeigen der Zellen nach dem Sliding-Window Prinzip. Nur die Zellen, die wirklich dargestellt werden werden auch vom Grid beachtet. Die nicht sichtbaren Zellen bleiben solange unberührt im Speicher, bis der Benutzer das Grid so weit gescrollt hat, dass diese sichtbar werden. Dies hat den Vorteil, dass nach dem einmaligen Erstellen aller Zellen quasi kein Performanceproblem mehr entstehen kann. Im Test 4 funktioniert somit das Benutzen des Grid auch mit mehreren Millionen Zellen problemlos. Im PE Editor sollte die Anzahl von benötigten Zellen in einem einzigen Grid sowieso nur in Ausnahmefällen 1000 Zellen überschreiten. Auch an anderen Stellen kommen Optimierungen zum Einsatz. So werden beim Erstellen des TextCellModel, beim ersten Zuweisen des Zellenwertes, die Events zum Melden einer Wertveränderung umgangen was einen Geschwindigkeitsbonus für die Erstellung eines Objekts dieser Klasse um ca. 38% zu Folge hatte. Ein weiteres Beispiel ist, dass die Schriftart zur Zelldarstellung beim Erstellen einer Zelle als Parameter übergeben werden muss. Dies ermöglicht das Wiederverwenden eines Font Objekts zwischen beliebigen Zellen. Bevor dies der Fall war, hatten die 1 Million Testzellen 1 Million Font Objekte was zu einer deutlich spürbaren Beeinträchtigung der Griderstellung führte. Es gibt auch noch einige kuriosere Optimierungen. So sind die normalen.net Methoden zum Vergleichen von Strings 5 erschreckend langsam. An performancekritischen Stellen wird deshalb explizit erstmal die Länge der zwei zu vergleichenden Strings verglichen was zu spürbaren Verbesserungen führte. Ähnlich war die Situation bei der bereits erwähnten Klasse GridColor. Ein großer Teil der Initialisierung der AbstractCellModel Objekte wurde für die Initialisierung der Text- und Hintergrundfarbe der Zelle gebraucht. Ein kurzer Blick auf den Quellcode der.net Color Klasse 6 erklärte dann auch warum. Selbst die vorgegebenen Farben wie Color.White oder Color.Blue sind extrem ineffizient implementiert. Im Laufe der Optimierung konnte durch diese und weitere Maßnahmen die maximale Gridgröße von knapp 500 Zellen in der ersten unoptimierten Version auf mehrere Millionen Zellen 7 gesteigert werden. 4 Auf meinem AMD64 3000+ mit 1 GB RAM 5 Operator==, String.Equals und String.Compare 6 Möglich mit dem Tool.NET Reflector - http://www.aisto.com/roeder/dotnet/ 7 Auf meinem AMD64 3000+ benötigt ein Grid mit 1 Million Zellen zum Initialisieren knapp 1.5 2 Sekunden. Nach der Initialisierung müßten sich Grids beliebiger Größe wegen des Sliding-Window Prinzips gleich verhalten. Dies entspricht auch den Beobachtungen, die ich während meiner Tests machte. Sebastian Porst - Entwicklung eines PE Editors Seite 16 von 61

4. Migration von PeLib von C++ nach C++.NET 4.1. Übersicht PeLib, die vom PE Editor benutzte Open-Source Bibliothek zur Verarbeitung von PE Dateien, wurde in komplett standardkonformen ANSI-C++ geschrieben. Um diese Bibliothek jetzt vom PeEditor, der ja in C# geschrieben ist, aus nutzen zu können, musste eine Brücke zwischen nativem C++ Code und von der.net Virtual Machine verwaltetem.net Code geschlagen werden. Glücklicherweise gibt es eine Sprache, mit der das ohne Probleme möglich ist, und sogar von Microsoft so gedacht war: Managed C++. Mit Hilfe von Managed C++ kann man so genannte Mixed Assemblies erstellen. Dies sind.net Assemblies welche teils nativen Code und teils verwalteten Code enthalten. Nativer und verwalteter Code kann innerhalb solcher Assemblies voll miteinander interagieren. Mit Hilfe von Managed C++ 2005 wurde so eine.net Version von PeLib namens PeLib.NET erstellt. PeLib.NET bietet an sich keine eigene Funktionalität, die über die Funktionalität von PeLib hinausgeht. Die neue.net Bibliothek enthält lediglich Klassen und Funktionen, welche die Funktionen aus der nativen PeLib Bibliothek aufrufen, deren nativer Code ebenfalls Teil der Mixed Assembly namens PeLibNet.DLL ist. So wird das indirekte Benutzen des nativen PeLib Codes aus verwaltetem Code beliebiger.net Sprachen über den Umweg des verwalteten, öffentlichen Interfaces von PeLib.NET ermöglicht. 4.2. Motivation Theoretisch gesehen wäre die Erstellung von PeLib.NET nicht nötig gewesen da es auch möglich ist in C# Programmen nativen Code aus DLL Dateien aufzurufen. Eine native PeLib DLL, die nutzbar gewesen wäre, stand bereits vor Beginn der Arbeit als Teil von PeLib zur Verfügung. Nichtsdestotrotz gab es sehr gute Gründe für die Erstellung einer.net Version von PeLib, obwohl dies mit einigem Mehraufwand verbunden war. Immerhin enthält PeLib mehr als 700 öffentliche Funktionen, die auch in PeLib.NET nutzbar sein müssen. Die Vorteile von PeLib.NET gegenüber der nativen PeLib DLL sind im einzelnen: - Die C# Sprachkonstrukte, die beim Aufruf von DLL Dateien benutzt werden, sind extrem hässlich und sollten nur im Ausnahmefall eingesetzt werden 8. - PeLib.NET kann aus beliebigen.net Sprachen benutzt werden, nicht nur aus solchen, die Sprachkonstrukte zur Kommunikation mit DLL Dateien bereitstellen. - Um größtmögliche Nutzbarkeit in der Sprachwelt vor.net zu gewährleisten setzt die native DLL auf ein strikt imperatives und zu C kompatibles öffentliches Interface. Die Benutzung eines solchen Interfaces aus den zumeist stark objektorientierten Sprachen der.net Familie wäre für.net Programmierer ungewohnt und fehleranfällig. Mit Hilfe von PeLib.NET ist es möglich ein öffentliches Interface für PeLib anzubieten, welches direkt auf die Benutzer von.net Sprachen zugeschnitten ist. Was das genau bedeutet, wird weiter unten im Detail 8 Stichwort PInvoke: http://msdn.microsoft.com/library/en-us/ cscon/html/vctskcodeusingpinvokevisualc.asp Sebastian Porst - Entwicklung eines PE Editors Seite 17 von 61

beschrieben. - PeLib.NET lässt sich wie jede andere.net Assembly in Visual Studio Projekte einbinden. Die Assembly verhält sich dann innerhalb des Projekts ganz genau so, wie auch Code, der direkt zum Projekt gehört. So kann man zum Beispiel auf Klassen von PeLib.NET genauso zugreifen wie auf Klassen, die direkt im Projekt definiert wurden, ohne erst umständlich Informationen über den externen Code zu importieren. Dies schließt On-Demand Hilfe und Code Completion für Klassen und Funktionen aus PeLib.NET während des Schreibens des Codes mit ein. 4.3. Details zur Migration Mit.NET wurden einige neue Sprachkonstrukte eingeführt, die in C++ nicht nativ unterstützt werden und deshalb auch nicht in PeLib verwendet werden. Diese müssen jedoch wegen ihrer Wichtigkeit für die.net Sprachen unbedingt im öffentlichen Interface von PeLib.NET benutzt werden, um natürliches Benutzen von PeLib.NET in.net Sprachen zu gewährleisten. Diese Konstrukte wären im folgenden: - Alle vormals nativen Klassen von PeLib wurden in PeLib.NET zu verwalteten Klassen konvertiert. Dies bedeutet, dass der.net Garbage Collector sich um die Deallokierung nicht mehr benötigter Klassen kümmert. Eine explizite Löschung von Instanzen dieser Klassen über den Operator delete entfällt. Dies war nötig, um PeLib.NET Klassen überhaupt in.net Sprachen außer Managed C++ nutzen zu können. - Get/Set Funktionen, die in PeLib ohne weitere Parameter aufgerufen werden, wurden zu Properties konvertiert. - Get/Set Funktionen, die einen weiteren Parameter zur Indizierung eines bestimmten Wertes innerhalb einer Menge von Werten besaßen, wurden zu Indexern konvertiert. - Das Fehlersystem wurde von Rückgabewerten auf die in.net gebräuchlichen Exceptions umgestellt. - Große Teile von PeLib.NET arbeiten nach dem Prinzip des Observer Patterns. Quasi alle Werte, die sich verändern können, bieten in PeLib.NET einen Event an, bei dem sich andere Funktionen anmelden können. Wird der Wert dann verändert, wird der Event ausgelöst und die angemeldeten Funktionen werden über die Änderung des Wertes benachrichtigt. Da Events zeitaufwändig sind, gibt es die Möglichkeit PeLib.NET auch über einfaches Setzen eines Präprozessorflags ohne Events zu kompilieren. - Bei der Erstellung der nativen PeLib Bibliothek konnte nicht vorausgesehen werden in welchen Programmen sie zum Einsatz kommt. Aus Performancegründen prüft PeLib deshalb übergebene Parameter nicht auf Gültigkeit, falls diese vom Aufrufer der Funktion auf einfache Weise geprüft werden können. Darunter fallen zum Beispiel Feldüberschreitungen. Fehlerhafte Parameter führen deshalb in PeLib in den meisten Fällen zu explizit dokumentiertem, undefiniertem Verhalten, nicht zu wohldefinierter Fehlerbehandlung. In PeLib.NET kann man über ein Präprozessorflag wählen, ob man dieses System beibehalten will oder ob auf ein Exception-Basiertes Modell zur Überprüfung von Funktionsparametern umgestellt werden soll. Sebastian Porst - Entwicklung eines PE Editors Seite 18 von 61

Nicht alles bezüglich der Migration zu.net war jedoch positiv. Es gab auch einen Aspekt, der in PeLib.NET objektiv schlechter ist als in PeLib: Um einen Einsatz von PeLib.NET in beliebigen.net Sprachen zu erlauben, musste der Code von PeLib.NET CLS-Kompatibel 9 sein. CLS bringt quasi alle.net Sprachen auf einen kleinsten gemeinsamen Nenner, einige wichtige Sprachelemente dürfen nicht verwendet werden um dies zu gewährleisten. Das größte Problem für PeLib.NET war die Nichtexistenz von unsigned Datentypen in CLS Code. PeLib arbeitet fast ausschließlich mit nicht-negativen unsigned Werten, was in PeLib.NET nicht möglich ist. Größere Probleme bereitet dies nicht, jedoch ist es sehr unschön, dass es möglich ist einen negativen Wert an Funktionen zu übergeben, die logisch gesehen nichts mit negativen Werten anfangen können. Ein weiteres Opfer der CLS-Kompatibilität sind globale Funktionen, die es in CLS kompatiblem Code nicht geben darf. 4.4. Fazit Trotz des benötigten Zeitaufwands für die Erstellung von PeLib.NET hat sich der Einsatz von PeLib.NET voll gelohnt. Die Vorteile von PeLib.NET gegenüber PeLib und erst recht gegenüber der nativen PeLib DLL sind so extrem, dass ich in Zukunft wahrscheinlich immer mit PeLib.NET arbeiten werde. Vollkommen natürliche Integration in.net Code, die durch Properties und Indexer stark vereinfachte Syntax und vor allem die automatische Hilfe, die bereits während des Schreibens eines PeLib.NET Elements von Visual Studio anzeigt wird, erleichtern und beschleunigen das Benutzen von PeLib um ein Vielfaches. 9 CLS steht für Common Language Specification. Diese Spezifikation ist die minimale Definition aller Sprachelemente die eine.net Sprache implementieren muß. http://msdn.microsoft.com/library/enus/cpguide/html/cpconwhatiscommonlanguagespecification.asp Sebastian Porst - Entwicklung eines PE Editors Seite 19 von 61

5. Eine kurze Übersicht über das PE Dateiformat Um zu verstehen, welche Daten der PE Editor eigentlich anzeigen und editieren kann, folgt in diesem Abschnitt eine kurze Einführung in die einzelnen Strukturen von PE Dateien und ihre Bedeutung. Es ist zu Beachten, dass nur die Teile von PE Dateien besprochen werden, die auch vom PE Editor bearbeitet werden können. Es gibt auch solche, mit denen der PE Editor nicht umgehen kann. Dies liegt ausschließlich an der nicht-vorhandenen Dokumentation dieser Teile. Deshalb werden sie hier nicht weiter erwähnt. 5.1. Der MZ Header Am Anfang jeder PE Datei steht der so genannte MZ Header 10. Er ist ein Relikt aus weit zurückliegenden DOS Tagen und hat heute quasi keinerlei Bedeutung mehr. Von den 64 Bytes, die Teil dieser Struktur sind, werden heute nur noch 6 Bytes genutzt. Die ersten zwei Bytes des Headers (e_magic genannt) werden als Signatur zur ersten Gültigkeitsüberprüfung der Datei benutzt. In gültigen MZ Headern sind diese zwei Bytes immer 'MZ'. Die anderen 4 der noch genutzten 6 Bytes befinden sich am Ende des Headers im so genannten e_lfanew Feld. Dieses Feld dient als Zeiger auf den nächsten Header. Bei PE Dateien ist dies immer der PE Header, es gibt jedoch auch andere Dateiformate, die auf dem MZ Header aufbauen. Es gibt noch eine Reihe anderer Felder im MZ Header, deren ursprünglicher Sinn zum Beispiel die Angabe der Dateigröße oder die Initialisierung von Speichersegmenten oder Registern war. Diese werden jedoch von heutigen Windows-Versionen nicht mehr genutzt und können daher beliebige Werte enthalten. Genaue Struktur des so genannten IMAGE_DOS_HEADER 11. typedef struct _IMAGE_DOS_HEADER { WORD e_magic; WORD e_cblp; WORD e_cp; WORD e_crlc; WORD e_cparhdr; WORD e_minalloc; WORD e_maxalloc; WORD e_ss; WORD e_sp; WORD e_csum; WORD e_ip; WORD e_cs; WORD e_lfarlc; WORD e_ovno; WORD e_res[4]; WORD e_oemid; 10 MZ sind die Initialien des ursprünglichen Entwicklers des MZ Dateiformats: Mark Zbikowski 11 Diese und alle anderen Strukturdefinitionen, die in diesem Kapitel zu finden sind wurden aus der Datei winnt.h des MinGW C++ Compilers entnommen. Sebastian Porst - Entwicklung eines PE Editors Seite 20 von 61

WORD e_oeminfo; WORD e_res2[10]; LONG e_lfanew; } IMAGE_DOS_HEADER,*PIMAGE_DOS_HEADER; 5.2. Der PE Header Weitaus interessanter als der MZ Header ist der PE Header. Er ist in allen nativ ausführbaren 32 Bit Dateien in heutigen Windows-Systemen zu finden. Dieser Header setzt sich aus fünf Teilen zusammen, der Signatur, dem File Header, dem Optional Header, dem Image Directory und den Sections. Die Signatur (NTSignature) dient zur ersten Verifikation des PE Headers und besteht nur aus vier Bytes die bei gültigen Headern immer den Wert 'PE\0\0' haben. Der File Header ist eine Struktur mit sieben Einträgen, die sehr generell darüber Auskunft geben wie und wo die Datei zu gebrauchen ist. Vier der sieben Einträge sind von äußerster Wichtigkeit. Sind diese Einträge falsch kann die Datei nicht mehr ausgeführt werden. Dies ist zum einen ein 2-Byte Feld namens Machine. Es identifiziert die CPU, auf der die Datei ausgeführt werden kann. Dieses Feld existiert, weil Windows NT früher auf verschiedenen Architekturen lief. Heutzutage ist das Feld aufgrund der Dominanz der x86 Architektur und der Einstellung der Windows-Entwicklung für andere CPUs theoretisch von geringerer Bedeutung. Nichtsdestotrotz muss der Wert in diesem Feld natürlich korrekt sein. Das zweite sehr wichtige Feld im File Header nennt sich NumberOfSections. Es enthält die Anzahl der Sektionen, die in dieser Datei zu finden sind. Mehr dazu später. Das Feld SizeOfOptionalHeader gibt die Größe des OptionalHeader an. In der Theorie ist dieser Eintrag und somit die Größe des OptionalHeader variabel. In der Praxis ist mir jedoch kein Compiler bekannt, der hier nicht den Wert 0xE0 setzt. Die einzige Möglichkeit, die Größe des optionalen Headers zu verändern, ist Einträge im Image Directory hinzuzufügen oder wegzulassen. Das vierte und letzte der wichtigsten Felder des FileHeaders ist das Feld Characteristics. Es enthält 16 Flags, welche Auskunft über die Datei geben. Dies sind zum Beispiel ob die Datei ein Multiprozessorsystem benötigt, ob die Datei eine DLL ist, ob die Datei im Little Endian oder Big Endian Format vorliegt oder wie Speicher für diese Datei verwaltet werden soll. Der dritte Teil des PE Headers, der Optional Header, ist weit länger als alle anderen Teile. Er enthält fast drei Dutzend Einträge, die zumeist darüber Auskunft geben wie die Datei aufgebaut ist. Das erste Feld in diesem Header heißt Magic und wird zur Verifikation des Headers benutzt. Es hat immer den festen Wert 0x010B. Das nächste wichtige Feld ist ImageBase. ImageBase ist ein 4-Byte Wert, der die erwünschte Basisadresse angibt, an die die Datei beim Ausführen geladen werden soll. Für EXE Dateien ist diese Basisadresse meistens 0x400000. Direkt hinter dem ImageBase Feld folgen zwei Felder namens SectionAlignment und FileAlignment. Diese geben Auskunft wie die Sektionen im Speicher (SectionAlignment) und in der Datei (FileAlignment) angelegt sind. Ein typischer Wert für FileAlignment ist zum Sebastian Porst - Entwicklung eines PE Editors Seite 21 von 61

Beispiel 0x200. Dies bedeutet, dass die Größe jeder Sektion der Datei auf der Festplatte ein Vielfaches von 0x200 sein muss. Der typische Wert für SectionAlignment ist 0x1000. Wird eine Datei zum Ausführen in den Speicher geladen muss die Größe jeder Sektion nach dem Laden im Speicher ein Vielfaches von 0x1000 sein. Ein weiteres wichtiges Feld ist das Feld SizeOfImage. Es enthält die Gesamtgröße der Datei, nachdem sie in den Speicher geladen wurde. Zu Windows 9X Zeiten wurde diese Größe noch kompliziert aus den Teilgrößen der einzelnen Header und Sektionen berechnet. In neueren Windows-Versionen steht in diesem Eintrag jedoch nur die Adresse des letzten Bytes in der letzten Sektion im Speicher. Das letzte wichtige Feld ist das Feld NumberOfRvaAndSizes. Es enthält die Anzahl der Einträge im darauf folgenden Image Directory. Dies wäre auch das Feld in dem die Größe des Optional Header verändert werden könnte, in der Praxis setzen aber alle gängigen Compiler dort den Wert 0x10 ein. Nach dem Optional Header folgt das Image Directory. Das Image Directory ist eine zweispaltige Tabelle, die in der ersten Spalte eine RVA 12 und in der zweiten Spalte eine Größenangabe enthält. Die einzelnen Zeilen dieser Tabelle geben Auskunft darüber wo sich weitere Spezialstrukturen wie zum Beispiel Ressourcen oder importierte Funktionen innerhalb der Datei befinden. Der letzte Teil des PE Headers ist der Section Header. Ähnlich wie das Image Directory hat auch der Section Header Tabellenform. Die einzelnen Einträge in dieser Tabelle haben jedoch 10 Spalten. Eine Sektion ist ein logisch zusammenhängender Block einer Datei wie zum Beispiel Code, Ressourcen, exportierte Funktionen oder statische Daten. Typischerweise haben PE Dateien 3 5 Sektionen. Die wichtigsten Attribute einer Sektion sind deren Namen, Adresse und Größe sowohl im Speicher als auch in der Datei auf Festplatte und die Characteristics der Sektion. Übersicht über die Strukturen des PE Headers: typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; 12 Siehe Glossar Sebastian Porst - Entwicklung eines PE Editors Seite 22 von 61

DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Reserved1; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER,*PIMAGE_OPTIONAL_HEADER; typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY; typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; union { DWORD PhysicalAddress; DWORD VirtualSize; } Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics; } IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER; 5.3. Das Export Directory Der erste Eintrag im ImageDirectory spezifiziert Größe und Position des Export Directory. Diese Struktur definiert in Bibliotheken (DLL Dateien) die Namen von exportierten Funktionen und wo sich diese Funktionen in der Datei befinden. Ein Export Directory besteht wie in Abb. 13 13 zu sehen ist aus einem Header (lila), der zum Beispiel angibt wie viele Funktionen exportiert werden oder wie die DLL Datei heißt, und einer Liste aus exportierten Funktionen (blau), die durch Name, ID (Ordinal) und RVA an der die Funktion zu finden ist charakterisiert sind. 13 Abbildung wurde übernommen aus [PIETREK4] Sebastian Porst - Entwicklung eines PE Editors Seite 23 von 61

Abb. 13: Struktur des Export Directories Genaue Definition des Export Directory: typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; DWORD Base; DWORD NumberOfFunctions; DWORD NumberOfNames; PDWORD *AddressOfFunctions; PDWORD *AddressOfNames; PWORD *AddressOfNameOrdinals; } IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY; 5.4. Das Import Directory Das Import Directory ist das Gegenstück zum Export Directory. Während das Export Directory, wie bereits gezeigt, Funktionen aus DLL Dateien exportiert, ist es über das Import Directory möglich genau diese exportierten Funktionen zu importieren und dadurch zu Benutzen. Dieses Directory ähnelt in seiner Struktur auch dem Export Directory. Ebenso wie dieses besteht auch das Import Directory aus einem Header (in der Abbildung 14 rot gefärbt) und einer Liste von Funktionen (blau). Im Gegensatz zum Export Directory wo es nur einen Header und eine Liste von Funktionen gibt, gibt es im Import Directory jedoch pro importierter DLL Datei einen Header und eine Liste. Wenn eine PE Datei gestartet wird, durchsucht der PE Loader dann das Import Directory der Datei und versucht die dort importierten Bibliotheken und Funktionen zu laden. Die Adressen der Funktionen, die der PE Loader auf diese Weise findet, werden dann zu der Liste der Funktionen ins Import Directory geschrieben (in der Abbildung ockergelb gefärbt). Dadurch wird es dem Code innerhalb der Datei ermöglicht, über das Import Directory auf importierte Funktionen zuzugreifen. Genaue Definition der Strukturen des Import Directory: 14 Abbildung übernommen aus [PIETREK4] Sebastian Porst - Entwicklung eines PE Editors Seite 24 von 61

Abb. 14: Struktur des Import Directory typedef struct _IMAGE_IMPORT_DESCRIPTOR { _ANONYMOUS_UNION union { DWORD Characteristics; PIMAGE_THUNK_DATA OriginalFirstThunk; } DUMMYUNIONNAME; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; PIMAGE_THUNK_DATA FirstThunk; } IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR; typedef struct _IMAGE_THUNK_DATA { union { PBYTE ForwarderString; PDWORD Function; DWORD Ordinal; PIMAGE_IMPORT_BY_NAME AddressOfData; } u1; } IMAGE_THUNK_DATA,*PIMAGE_THUNK_DATA; typedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; BYTE Name[1]; } IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME; 5.5. Das Resource Directory Das Resource Directory ist der Teil einer PE Datei, in der Ressourcen wie zum Beispiel Bilder, Cursor oder Videos gespeichert werden. Dieses Directory ist der einzige Teil einer PE Datei, der in einer Art Baumstruktur gespeichert wird (siehe Abb. 15 15 ). Ganz oben im Baum befindet sich ein Header, der pro benutztem Ressourcentyp einen Eintrag enthält. Ressourcentypen sind die bereits angesprochenen Kategorien wie Bitmap oder Cursor. Mit diesen einfachen Kategorien ist es jedoch nicht getan. Insgesamt gibt es um die 20 Ressourcentypen, etwa auch so komplizierte wie HTML Dokument oder Gerätetreiber. Jeder Eintrag für einen Ressourcentyp führt zu einer Liste von Ressourcen dieses Typs, die in der Datei gespeichert sind. Ressourcen können dabei entweder über eine eindeutige Nummer oder einen eindeutigen ASCII-Namen identifiziert werden. 15 Abbildung wurde übernommen aus [KATH1] Sebastian Porst - Entwicklung eines PE Editors Seite 25 von 61

Sollten Ressource in verschiedenen Sprachen zur Verfügung stehen, werden unterhalb der Knoten, die einzelne Ressourcen identifizieren, nochmals Knoten für die einzelnen Sprachen in den Baum eingefügt. An letzter Stelle folgen an den Blättern des Baums die Binärdaten der Ressourcen. Das genaue Format dieser Daten ist natürlich vom Typ der Ressource abhängig zu der sie gehören. Abb. 15: Die Struktur des Ressource Directory Genaue Definition der Strukturen des Resource Directory: typedef struct _IMAGE_RESOURCE_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; WORD NumberOfNamedEntries; WORD NumberOfIdEntries; } IMAGE_RESOURCE_DIRECTORY,*PIMAGE_RESOURCE_DIRECTORY; _ANONYMOUS_STRUCT typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY { _ANONYMOUS_UNION union { _ANONYMOUS_STRUCT struct { DWORD NameOffset:31; DWORD NameIsString:1; Sebastian Porst - Entwicklung eines PE Editors Seite 26 von 61

}DUMMYSTRUCTNAME; DWORD Name; WORD Id; } DUMMYUNIONNAME; _ANONYMOUS_UNION union { DWORD OffsetToData; _ANONYMOUS_STRUCT struct { DWORD OffsetToDirectory:31; DWORD DataIsDirectory:1; } DUMMYSTRUCTNAME2; } DUMMYUNIONNAME2; } IMAGE_RESOURCE_DIRECTORY_ENTRY,*PIMAGE_RESOURCE_DIRECTORY_ENTRY; typedef struct _IMAGE_RESOURCE_DIRECTORY_STRING { WORD Length; CHAR NameString[1]; } IMAGE_RESOURCE_DIRECTORY_STRING,*PIMAGE_RESOURCE_DIRECTORY_STRING; typedef struct _IMAGE_RESOURCE_DIR_STRING_U { WORD Length; WCHAR NameString[1]; } IMAGE_RESOURCE_DIR_STRING_U,*PIMAGE_RESOURCE_DIR_STRING_U; typedef struct _IMAGE_RESOURCE_DATA_ENTRY { DWORD OffsetToData; DWORD Size; DWORD CodePage; DWORD Reserved; } IMAGE_RESOURCE_DATA_ENTRY,*PIMAGE_RESOURCE_DATA_ENTRY; 5.6. Das BaseReloc Directory Das BaseReloc Directory wird auch Relocations Directory genannt, weil es die sogenannten Relocations enthält. Wenn eine PE Datei nicht an die Adresse geladen werden kann, die im PE Header im ImageBase Wert spezifiziert ist, dann muss der PE Loader die geladene Datei patchen, da in den Befehlen der Datei manchmal absolute Offsets spezifiziert sind, die nur für die angenommene ImageBase, nicht jedoch für die vom PE Loader gewählte Ausweichadresse korrekt sind. Das BaseReloc Directory enthält in den Relocations Informationen darüber, wo der PE Loader zu patchende Befehle finden kann, und wie er diese zu patchen hat. Die Struktur dieser Informationen sind einfach gehaltene Arrays die Einträge einer bestimmten Struktur enhalten. Genaue Definition der Strukturen des BaseReloc Directory: typedef struct _IMAGE_BASE_RELOCATION { DWORD VirtualAddress; DWORD SizeOfBlock; } IMAGE_BASE_RELOCATION,*PIMAGE_BASE_RELOCATION; 5.7. Das TLS Directory Das TLS (Thread Local Storage) Directory dient zur Spezifikation von thread-lokalen Variablen. In Visual C++ kann man dies zum Beispiel mit Hilfe der Anweisung declspec(thread) bei der Variablendeklaration erreichen. Wo diese Variablen zu finden sind Sebastian Porst - Entwicklung eines PE Editors Seite 27 von 61

und wie sie initialisiert werden steht in diesem Directory. Genaue Definition der Strukturen des TLS Directory: typedef struct _IMAGE_TLS_DIRECTORY { DWORD StartAddressOfRawData; DWORD EndAddressOfRawData; PDWORD AddressOfIndex; PIMAGE_TLS_CALLBACK *AddressOfCallBacks; DWORD SizeOfZeroFill; DWORD Characteristics; } IMAGE_TLS_DIRECTORY,*PIMAGE_TLS_DIRECTORY; typedef void(ntapi *PIMAGE_TLS_CALLBACK)(PVOID,DWORD,PVOID); 5.8. Das BoundImport Directory Das BoundImport Directory dient dazu, die Ladezeit von PE Dateien zu verkürzen. Dazu wird vom Linker bereits während des Link-Vorgangs versucht, die Ladeadresse, die eine importierte DLL Datei hat, und die Ladeadresse der Funktionen, die aus dieser DLL Datei importiert werden, vorauszusehen. Gelingt dies nämlich, so muss der PE Loader das Import Directory nicht mehr verarbeiten, sondern kann direkt auf die Werte aus dem BoundImport Directory zurückgreifen. Um die Erwartungen des Linkers überprüfbar zu machen speichert der Linker den Zeitstempel jeder importierten DLL Datei im BoundImport Directory. Der PE Loader vergleicht dann diesen Zeitstempel mit dem der DLL Datei die er geladen hat. Ist dieser Zeitstempel gleich, so wird davon ausgegangen, dass die DLL Datei genau jene ist, die vom Linker erwartet wurde. Der PE Loader kann also auf die Werte aus dem Bound Import Directory zurückgreifen. Genaue Definition der Strukturen des BoundImport Directory: typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR { DWORD TimeDateStamp; WORD OffsetModuleName; WORD NumberOfModuleForwarderRefs; } IMAGE_BOUND_IMPORT_DESCRIPTOR,*PIMAGE_BOUND_IMPORT_DESCRIPTOR; typedef struct _IMAGE_BOUND_FORWARDER_REF { DWORD TimeDateStamp; WORD OffsetModuleName; WORD Reserved; } IMAGE_BOUND_FORWARDER_REF,*PIMAGE_BOUND_FORWARDER_REF; 5.9. Das IAT Directory Das IAT Directory (Import Address Table) ist ein weiteres Directory, das sich mit dem Importieren von Funktionen aus DLL Dateien befasst. Eigentlich wurde die IAT bereits beim Import Directory erwähnt, ist diese Tabelle doch genau der Teil des Import Directories, in den der PE Loader die ermittelten Adressen der importierten Funktionen schreibt. Deshalb ist das IAT Directory auch nur ein Array aus Adressen. Manche PE Dateien verzichten ganz darauf ein IAT Directory im PE Header zu spezifizieren Sebastian Porst - Entwicklung eines PE Editors Seite 28 von 61

und speichern stattdessen die IAT direkt im Import Directory. 5.10. Das COM+ Runtime Header Directory Das neueste aller Directories ist das COM+ Runtime Header Directory. Es ist ausschließlich in.net PE Dateien zu finden wo es die Aufgabe hat, den PE Loader darauf aufmerksam zu machen, dass die Datei nicht nativ ausgeführt werden kann, sondern von der.net Virtual Machine interpretiert werden muss. Außerdem enthält dieses Directory Informationen, die die Virtual Machine zum Ausführen der Datei benötigt, wie zum Beispiel Metadaten über den Bytecode der Datei. Von der Struktur her ist dieses Directory sehr einfach gehalten. Es besteht aus einer einzigen Struktur fester Länge mit zwölf Einträgen. Genaue Definition der Strukturen des COM+ Runtime Header Directory: struct IMAGE_COR20_HEADER { DWORD cb; WORD MajorRuntimeVersion; WORD MinorRuntimeVersion; IMAGE_DATA_DIRECTORY MetaData; DWORD Flags; DWORD EntryPointToken; IMAGE_DATA_DIRECTORY Resources; IMAGE_DATA_DIRECTORY StrongNameSignature; IMAGE_DATA_DIRECTORY CodeManagerTable; IMAGE_DATA_DIRECTORY VTableFixups; IMAGE_DATA_DIRECTORY ExportAddressTableJumps; IMAGE_DATA_DIRECTORY ManagedNativeHeader; }; Sebastian Porst - Entwicklung eines PE Editors Seite 29 von 61

6. Der Editor Nachdem jetzt die theoretischen Grundlagen und die bei der Implementierung benutzten Komponenten und Bibliotheken beschrieben wurden, kommen wir zum eigentlichen System. Der PE Editor besteht hauptsächlich aus zwei Teilen, dem Basissystem und den Plugins. 6.1. Das Basissystem Das Basissystem besteht aus vier Komponenten, der EXE Datei, über die man den Editor startet (PeEditor.exe), der Plugin Registry (PluginRegistry.dll), der Klasse zur Verwaltung der Log-Dateien (Logger.dll) und die Klasse, mit der man auf die Einstellungsdatei zugreift (SettingsFile.dll). 6.1.1 Das Hauptprogramm Nachdem das Hauptprogramm vom Benutzer durch Ausführen der EXE Datei gestartet wurde, hat das Basissystem zuerst die Aufgabe der Plugin-Registry mitzuteilen, dass die Plugins jetzt geladen werden sollen. Ist das Laden erfolgreich und wurde zumindest ein GUI Plugin gefunden, so übergibt das Hautprogramm die weitere Ausführung komplett an das geladene GUI Plugin das von nun an für das Programm verantwortlich ist. Eventuelle Fehler beim Laden der Plugins werden in der Log-Datei protokolliert und gegebenenfalls angezeigt. Es ist dem Benutzer außerdem möglich die Reihenfolge, in welcher die Plugins an der aktiven GUI angemeldet werden, in der Einstellungsdatei anzugeben. Dies ist notwendig, damit der Benutzer zum Beispiel die Reihenfolge der dynamisch erzeugten Menüs und Buttons in der GUI nach seinen Bedürfnissen anpassen kann. Ist diese Reihenfolge noch nicht spezifiziert, so bittet das Hauptprogramm beim Programmstart außerdem die aktive GUI, den GUI-spezifischen Dialog anzuzeigen, mit dessen Hilfe der Benutzer die Ladereihenfolge der Plugins angeben kann. In Abbildung 16 ist ein idealisierter Ladevorgang als Sequenzdiagramm dargestellt. Auf eventuelle Fehler, wie zum Beispiel das Fehlen eines gültigen GUI Plugins, und deren Behandlung wurde aus Platzgründen verzichtet. Nachdem das gefundene GUI Plugin aktiviert wurde, ist die Arbeit des Hauptprogramms eigentlich beendet. Die einzige Funktion, die das Hauptprogramm ab diesem Zeitpunkt noch hat, ist die Behandlung aller Exceptions, die das GUI Plugin vergisst zu behandeln. Damit diese Exceptions nicht einfach zum Absturz des Programms führen bleibt im Hauptprogramm ein globaler Exception-Handler aktiv, der Details über aufgetretene Exceptions in die Log- Datei protokolliert, damit die Suche nach Herkunft und Grund der aufgetretenen Exception erleichtert wird. Sebastian Porst - Entwicklung eines PE Editors Seite 30 von 61

Abb. 16: Ladevorgang des Basissystems 6.1.2 Die Plugin Registry Die Klasse PluginRegistry ist eine Singleton- Klasse über deren Hilfe beliebige Komponenten des PE Editors zentralisiert auf alle geladenen Plugins zugreifen können. Beim Start des Programms teilt das Hauptprogramm der Plugin Registry mit, dass diese doch versuchen solle, die Plugins im Plugin-Verzeichnis zu laden. Die Plugin Registry versucht daraufhin, alle DLL Dateien, die im Plugin-Verzeichnis gefunden wurden, zu laden. Jede erfolgreich geladene DLL Datei wird dann auf Klassen, die das Interface IPlugin implementieren, durchsucht. Alle Klassen, die dieses Kriterium erfüllen, werden dann instantiiert und der Plugin-Collection der Plugin Registry zugefügt. Abb. 17: Klassendiagramm der Klasse PluginRegistry Nachdem das Laden der Plugins abgeschlossen ist, steht die Plugin Registry zur Verfügung. Ab diesem Zeitpunkt haben alle Teile des PE Editors, also sowohl Basissystem, als auch beliebige Plugins, vollen Zugriff auf alle geladenen Plugins. Um eine Plugin Instanz zu erhalten, wird die Methode GetPlugin benutzt, die als Parameter einen GUID Wert verlangt, der das Plugin dessen Instanz verlangt wird, eindeutig identifiziert. Die Plugin Registry verwaltet jedoch nicht nur Plugins des Typs IPlugin, sondern auch solche des Typs IDynamicPlugin. Diese Plugins werden nicht direkt beim Programmstart geladen, Sebastian Porst - Entwicklung eines PE Editors Seite 31 von 61

sondern irgendwann während des Programmablaufs angelegt. Sie können im Gegensatz zu den normalen IPlugin Plugins, an der Plugin Registry sowohl angemeldet, als auch abgemeldet werden. Zugriff auf die angemeldeten IDynamicPlugin Plugins erfolgt analog zum Zugriff auf die IPlugin Plugins über eine Get-Methode. Allerdings werden dynamische Plugins nicht durch eine GUID, sondern durch ihre Position in der Collection identifiziert. 6.1.3 Die Logdatei Mit Hilfe der Singleton-Klasse Logger wird ein einfaches Interface zur zentralisierten Protokollierung von aufgetretenen Problemen bereitgestellt. Logger verwaltet eine Logdatei namens logfile.log im Unterverzeichnis log, auf die alle Teile des PE Editors schreibenden Zugriff über die von Logger angebotenen Funktionen haben. Um in die aktuelle Log-Datei schreiben zu können, wird die zwei-parametrige Funktion Log aufgerufen. Der erste Parameter ist vom Typ LogLevel und gibt die Wichtigkeit des Ereignisses an. Der zweite Parameter ist die Nachricht, die es zu protokollieren gilt. Abb. 18: Klassendiagramm der Klasse Logger Nur falls das Ereignis mindestens von gleicher Wichtigkeit wie die vom Benutzer gewählte Log-Sensibilität ist wird das Ereignis auch protokolliert. Beispiele für LogLevel Werte sind zum Beispiel NO_LOGGING (keinerlei Protokollierung), SEVERE_ERRORS (nur wichtige Fehler werden protokolliert) oder DEBUG_MODE (alles wird protokolliert). Es ist zu beachten, dass die Logdatei bei jedem Start des Programms gelöscht und neu geschrieben wird. Dieses Verhalten hat sich in der Praxis als einfacher und besser herausgestellt, als die zuvor gewählte Alternative der Erstellung von Logdateien mit Zeitstempel im Namen. Eine Anwendung wie der implementierte PE Editor benötigt jedoch keinerlei Loghistorie und das ursprüngliche Konzept wurde deshalb durch das einfachere Konzept ersetzt. Hier folgt nun ein Beispiel für eine Logdatei, die einen erfolgreichen Programmablauf protokolliert hat. Aus Platzgründen wurde das Ladeprotokoll von etwa 20 Plugins durch drei Punkte ersetzt. Außerdem wurde der vollständige Pfad zu den geladenen DLL Dateien entfernt. Man kann sehen, dass das generelle Format jedes Eintrags mit dem Namen der Komponente des PE Editors beginnt, die den Eintrag in die Logdatei geschrieben hat. Danach folgt die protokollierte Nachricht. Logger: Logging started at 12:36:12.8272528 PeEditor: Starting system PeEditor: Loading plugins from directory D:\Coding\PeEditor\Development\BaseSystem\PeEditor\bin\Debug\plugins Loading plugins PluginRegistry: Trying to load possible plugin file FooGrid.dll PluginRegistry: Trying to load possible plugin file SJPHexEdit1.dll PluginRegistry: Trying to load possible plugin file StandardPlugins.dll PluginRegistry: Trying to load possible plugin type PeHeaderPlugin PluginRegistry: Trying to load possible plugin type ExportRawData PluginRegistry: Trying to load possible plugin type TlsDirectoryPlugin... PluginRegistry: Trying to load possible plugin type ComHeaderDirectoryPlugin PeEditor: Loading GUI from plugin Default GUI DefaultGui: Trying to register plugin HexPlugin Sebastian Porst - Entwicklung eines PE Editors Seite 32 von 61

DefaultGui: Trying to register plugin File Locator... Als Gegenstück dazu folgt nun ein Beispiel für eine Logdatei, die einen nicht-erfolgreichen Programmablauf dokumentiert. Die oben erwähnten Maßnahmen zur Reduzierung des benötigten Platzes gelten immer noch. Außerdem wurde der protokollierte Stacktrace stark verkürzt. In diesem Beispiel treten zwei künstlich erzeugte Exceptions auf (rot dargestellt), von denen die erste vom Programm abgefangen wird und die zweite das Programm terminiert. Der Stacktrace wurde vom bereits erwähnten, globalen Exception-Handler protokolliert. Logger: Logging started at 14:03:44.6990736 PeEditor: Starting system PeEditor: Loading plugins from directory D:\Coding\PeEditor\Development\BaseSystem\PeEditor\bin\Debug\plugins Loading plugins PluginRegistry: Trying to load possible plugin file FooGrid.dll PluginRegistry: Trying to load possible plugin file SJPHexEdit1.dll PluginRegistry: Trying to load possible plugin file StandardPlugins.dll PluginRegistry: Trying to load possible plugin type PeHeaderPlugin... PluginRegistry: Trying to load possible plugin type MzHeaderPlugin PluginRegistry: Loading plugin type failed (Plugin threw an exception)... PeEditor: Loading GUI from plugin Default GUI DefaultGui: Trying to register plugin HexPlugin DefaultGui: Trying to register plugin File Locator PeEditor: TestException PeEditor: StandardPlugins PeEditor: at PeEditor.Plugins.MzHeaderPanel.get_Header() at PeEditor.Plugins.MzHeaderPanel.CreateCells() at PeEditor.Plugins.MzHeaderPanel..ctor(PeFile32 file) at PeEditor.Plugins.MzHeaderPlugin.MenuClicked(PeFile32 file) at PeEditor.Plugins.MzHeaderPlugin.<>c DisplayClass4.<FileLoaded>b 3(Object o, EventArgs e) at System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e) at System.Windows.Forms.ToolStripMenuItem.OnClick(EventArgs e) at System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e)... 6.1.3 Die Einstellungsdatei Sämtliche Einstellungen des PE Editors und aller Plugins sollen zentral in der XML-Datei settings.xml im Hauptverzeichnis der Anwendung gespeichert werden. Um den Zugriff auf diese Datei zu vereinfachen und ein bestimmtes Format für diese Datei zu erzwingen gibt es die Singleton-Klasse SettingsFile, über deren Methoden alle Teile der Anwendung sowohl lesend, als auch schreibend auf die Einstellungsdatei vollen Zugriff haben. Fünf öffentliche Methoden dieser Klasse sind von besonderer Wichtigkeit. So gibt es die Methode NodeExists mit deren Hilfe man feststellen kann, ob der gewünschte Knoten bereits im XML Baum existiert. Ist dies nicht der Fall kann man mit der Methode CreateNode einen neuen Knoten anlegen. Falls man nicht nur einen einzelnen Knoten sondern einen kompletten Zweig anlegen möchte so kann man auf CreatePath zurückgreifen. Diese Methode erzeugt dann die im Parameter spezifizierte Knotenhierarchie. Mit GetNode kann man dann auf vorhandene Knoten zugreifen. Die letzte der fünf Funktionen ist CreateAttribute mit deren Hilfe man ein Sebastian Porst - Entwicklung eines PE Editors Seite 33 von 61

XML Attribut erzeugen kann, das man dann einem Knoten zuweist. Der Wurzelknoten der Einstellungsdatei trägt den Namen Settings. Unterhalb dieses Knotens können dann beliebige Einträge hinzugefügt werden. Hier ist ein Beispiel einer Einstellungsdatei, die alle bereits vom PE Editor benutzten Optionen beinhaltet. Dies wären zum einen die Ladereihenfolge der Plugins und zum anderen die zuletzt geöffneten Dateien. Sowohl die Plugins, als auch die zuletzt geladenen Dateien werden in der Reihenfolge behandelt, wie sie in der XML Datei auftauchen. Das einzige XML Attribut, das bisher benutzt wird ist das Attribut Max, das angibt wie viele zuletzt geladene Dateien im entsprechenden Menü in der GUI maximal angezeigt werden sollen. Aus Platzgründen wurden die Plugin GUIDs im oberen Teil der Datei stark gekürzt. <Settings> <General> <PluginOrder> <Plugin>6b2eb6a0-b31c-11d9-9669-0800200c9a66</Plugin> <Plugin>82ae4a20-dbee-11d9-8cd5-0800200c9a66</Plugin> <Plugin>f98a9230-b00a-11d9-9669-0800200c9a66</Plugin>... <Plugin>da9d7d40-b237-11d9-9669-0800200c9a66</Plugin> <Plugin>f50376d0-0b38-11da-8cd6-0800200c9a66</Plugin> </PluginOrder> <RecentFiles Max="5"> <File>C:\windows\appversions.dll</File> <File>C:\windows\vmmreg32.dll</File> <File>D:\Coding\PeEditor\Kopie von kernel32.dll</file> <File>D:\Coding\PeEditor\Kopie von notepad.exe</file> </RecentFiles> </General> </Settings> 6.1. Die Pluginarchitektur Abb. 19: Klassendiagramm der Klasse SettingsFile Der PE Editor kann mit nicht weniger als sieben verschiedenen Plugintypen umgehen. Diese lassen sich jedoch in nur zwei große Gruppen unterteilen. Die erste Gruppe sind die so genannten statischen Plugins, die zweite Gruppe sind die so genannten dynamischen Plugins. Diese Namen lassen allerdings nicht so recht auf die Funktion der Plugins schließen. Statische Plugins sind solche, die lediglich einmal am Programmstart von der Plugin Registry geladen werden und dann bis zur Beendigung des Programms geladen bleiben. Im Gegensatz dazu stehen die dynamischen Plugins, die irgendwann instantiiert werden und jederzeit an der Plugin Registry an- und abgemeldet werden können. Statische Plugins werden über das Interface IPlugin identifiziert. Nur Klassen, die dieses Interface implementieren, werden von der Plugin Registry beim Programmstart als statisches Plugin erkannt und geladen. Da der einzige Anspruch, der an diese Art von Plugins gestellt wird, der ist, dass solche Plugins pro Programminstanz nur einmal geladen werden dürfen, ist Sebastian Porst - Entwicklung eines PE Editors Seite 34 von 61

das Interface IPlugin entsprechend minimal gehalten. Es besteht lediglich aus drei Properties; Name, Guid und Dependencies. Name gibt den Namen des Plugins zurück, Guid gibt einen global unique identifier zurück, der die globale Einmaligkeit des Plugins gewährleisten soll und Dependencies liefert eine Liste von Guids, die die Plugins identifizieren von denen ein Plugin abhängt. Diese Abhängigkeiten bestimmen die Ladereihefolge der Plugins. Es wird versucht die Plugins so zu laden, dass alle Plugins, von denen ein Plugin abhängt, vor diesem geladen werden soll. Dynamische Plugins werden über das Interface IDynamicPlugin identifiziert. Dieses Interface ist leer, es beinhaltet keine einzige Deklaration. Trotzdem ist dieses Interface extrem wichtig, es dient nämlich der Identifikation aller nicht-einmaligen Plugins, die beliebig oft und zu jedem Zeitpunkt erstellt und gelöscht werden können. Ohne dieses Interface wäre es notwendig, dass die Plugin Registry Collections für alle Plugin-Spezialtypen verwaltet. Dank dieses Plugins genügt eine einzige Collection und die genaue Identifizierung welcher Spezialtyp vorliegt, bleibt dem Benutzer der Plugin Registry überlassen. Die anderen fünf Plugintypen dienen dazu die Funktionsweise der dynamischen und statischen Plugins zu verfeinern. Es wurde dabei bewusst auf alle Versuche der Hierarchiebildung verzichtet. Alle Versuche eine solche Hierarchie zu erschaffen, sind garantiert zum Scheitern verurteilt, da man nicht voraussehen kann in welchen Kombinationen die einzelnen Plugintypen benutzt werden. Der erste der Spezialtypen ist IGuiPlugin. Dieses Plugin Interfaces muss von Klassen implementiert werden, die als GUI für den PE Editor verwendet werden sollen. Dementsprechend hoch sind die Ansprüche, die an dieses Interface gestellt werden. Mit einem Event, drei Properties und sieben Methoden ist es das bei weitem größte aller Plugin Interfaces. Abb. 20: Das Interface IPlugin Abb. 21: Das Interface IDynamicPlugin Eine der wichtigeren Methoden ist die Methode Start(), mit deren Hilfe der Programmablauf beim Programmstart vom Hauptprogramm auf die gewünschte GUI übertragen wird. Abb. 22: Das Interface IGuiPlugin Mit Hilfe von AddControl, RemoveControl und RegisterMenu wird es Plugins ermöglicht die GUI zu verändern. So erlauben die ersten beiden Methoden die Anzeige von speziellen Steuerelementen in der GUI, während die letzte Methode zur Konfiguration des Hauptmenüs der GUI benutzt werden kann. Falls der Benutzer des Programms von einem Ereignis in Kenntnis gesetzt werden muss, geschieht dies mit Hilfe der Methoden HandleError und NotifyUser. Hier kann zwischen kritischen und nicht-kritischen Ereignissen unterschieden werden. Dies ist notwendig, damit der Benutzer nicht bei jedem unwichtigen Ereignis die Arbeit unterbrechen muss, um zum Beispiel eine MessageBox wegzuklicken. Nicht-kritische Ereignisse sollten am besten irgendwo am Rand angezeigt werden. Die anderen Methoden des Interfaces spielen eine untergeordnete Rolle. Zur genauen Aufgabe dieser Methode möchte ich an dieser Stelle auf die Dokumentation verweisen. Sebastian Porst - Entwicklung eines PE Editors Seite 35 von 61

Ein weiterer Spezialtyp ist IFileIndependentPlugin, der Plugintyp für alle Plugins, die auch ohne geladene Datei agieren können. Alle Plugins, die dieses Interface implementieren, müssen zu jedem Zeitpunkt aus von der GUI erreichbar sein. Bei der Standard-GUI geschieht dies über das Menü Plugins, in das sich alle dateiunabhängigen Plugins beim Aufruf der Interface-Methode Register eintragen sollten. Diese Methode ist zugleich auch die einzige Methode dieses Interfaces. Das Gegenteil zu einem dateiunabhängigen Plugin existiert natürlich auch, und das Interface für solche Plugins heißt sinnigerweise IFileDependentPlugin. Plugins dieses Typs machen nur Sinn, falls bereits eine PE Datei geladen ist, mit deren Daten das Plugin arbeiten kann. Während dateiunabhängige Plugins in der GUI im globalen Plugin Menü aufgeführt werden, können sich dateiabhängige Plugins im Menü der jeweiligen Datei registrieren. Das Interface selbst stellt zwei Methoden zur Verfügung, eine die aufgerufen wird, sobald eine neue PE Datei geladen wurde und eine die aufgerufen wird, falls eine PE Datei geschlossen wurde. In der Methode FileLoaded kann das Plugin dann auf die neue Datei reagieren während in der Methode FileClosed dann eventuelle Aufräumarbeiten wie das Schließen von Fenstern erledigt werden. Der nächste der Plugintypen ist IDataPlugin. Dieses Interface wird von Plugins implementiert, die anderen Plugins Binärdaten bereitstellen wollen. Es ist mit nur einem Event und zwei Properties dementsprechend einfach gehalten. Das Property HexData liefert die Daten, während das Property DataDescription eine Beschreibung der Abb. 25: Das Interface Daten zurück gibt, die dann in der GUI angezeigt werden kann. Das IDataPlugin Event OnDataChanged wird ausgelöst, sobald sich die Daten verändern. Dann können andere Plugins, die mit diesen Daten arbeiten, auf die Änderung reagieren. Der siebte und letzte Plugintyp ist IConversionPlugin, ein Interface das signalisiert, dass Plugins dieses Typs in der Lage sind Binärdaten in ein anderes Format umzuwandeln. Auch dieses Interface ist mit einer Property und einer Methode eher klein gehalten. Das Property Description gibt eine Beschreibung der Binärdatenumwandlung, die mit Hilfe der Methode Convert durchgeführt werden kann, zurück. Es ist zu beachten, dass der Rückgabetyp von Convert ein Byte-Array ist. Dies bedeutet jedoch nicht, dass diese Methode Binärdaten zurückliefert. Es bedeutet lediglich, dass das Format der zurückgegebenen Daten nicht genauer spezifiziert werden kann, da jedes denkbare Format Ergebnis der Konvertierung sein kann. 6.2. Die Plugins Abb. 24: Das Interface IFileDependentPlugin Abb. 23: Das Interface IFileIndependentPlugin Abb. 26: Das Interface IConversionPlugin Nachdem jetzt die Pluginarchitektur ausreichend dargestellt wurde, ist es an der Zeit die konkreten Plugins, die bei dieser Version des PE Editors mitgeliefert werden, vorzustellen. Generell gilt: Fast alle Plugins folgen dem gleichen generellen Muster. Dies liegt ganz einfach daran, dass die meisten Plugins zur Darstellung von Daten aus verschiedenen Teilen der PE Sebastian Porst - Entwicklung eines PE Editors Seite 36 von 61

Dateien benutzt werden. Woher genau die Daten kommen und wie genau sie schließlich in der GUI dargestellt werden, sind bereits Details der Implementierung und spielen deshalb für die Plugin-Architektur keine Rolle. Um mich in den folgenden Abschnitten nicht all zu oft zu wiederholen, soll deshalb bereits hier die grundlegende Architektur der Plugins zur Darstellung der Daten besprochen werden. Zu diesem Zweck werden pro Sektion, aus der die Daten gelesen werden, zwei Plugins benötigt. Ein statisches, dateiabhängiges Plugin (IPlugin + IFileDependentPlugin) namens [Sektion]Plugin, welches beim Programmstart geladen wird und dann darauf wartet, dass der Benutzer eine PE Datei öffnet. Sobald dies geschieht, erzeugt dieses Plugin einen Menüeintrag in dem Teil des Hauptmenüs, der zur Datei gehört. Bei einem Klick auf dieses Menü wird dann ein dynamisches Daten-Plugin (IDynamicPlugin + IDataPlugin) erstellt, welches außerdem IPluginControl implementiert. Das bedeutet also, dass es ein Panel bereitstellt, dass in der GUI angezeigt werden kann. Abb. 27: Die generelle DataPanel Hierarchie Abb. 28: Die generelle DataPlugin Hierarchie Sebastian Porst - Entwicklung eines PE Editors Seite 37 von 61

Abb. 29: Ablauf der Erzeugung eines DataPanel vom Programmstart bis zur Anzeige des Panels 6.2.1. Das MZ Header Plugin Das MZ Header Plugin ist das erste Plugin, welches der oben beschriebenen Form entspricht. Natürlich besteht es streng genommen auch aus den zwei Komponenten MzHeaderPlugin und MzHeaderPanel. Da weder die eine noch die andere der beiden Komponenten losgelöst von der zweiten existieren kann, wird jedoch im folgenden nur noch vom MzHeaderPlugin gesprochen, obwohl implizit eigentlich beide Komponenten gemeint sind. Dies gilt auch für alle folgenden Plugins die dem generellen Typ entsprechen. Wie schon in der theoretischen Einführung über das PE Dateiformat erwähnt, ist der MZ Header einer Datei sehr einfach aufgebaut und heute nahezu nutzlos. Dementsprechend einfach ist auch das Panel zur Darstellung der Daten des MZ Headers gehalten. Sebastian Porst - Entwicklung eines PE Editors Seite 38 von 61

Abb. 30: Die GUI Komponente des MZ Header Plugins Alle Daten des Headers werden in einer einfachen, auf dem FooGrid basierenden Tabellenstruktur dargestellt, wo sie nach Belieben editiert werden können. Alle Werte werden dabei wegen ihrer Breite von 16 Bit durch einen Hexadezimalwert von 4 Zeichen dargestellt. Der erste Wert, Magic, unterscheidet sich dabei leicht von den anderen. Im Gegensatz zu allen anderen Zellen kann der Inhalt dieser Zelle auf seine Gültigkeit überprüft werden. Abb. 31: MZ Header Panel mit ungültigem Magic Wert und selektierter BytesOnLastPage Zelle Deshalb kommt im Grid eine Zelle vom Typ TextCellValid zum Einsatz, deren Textfarbe je nach Gültigkeit des Wertes rot oder grün ist. Außerdem erscheint bei falschen Werten ein Button innerhalb der Zelle über den der Benutzer den Wert der Zelle ganz einfach korrigieren kann. Die einzelnen Zellen im Grid verfügen außerdem über Kontextmenüs, die nach einen Rechtsklick auf eine Zelle angezeigt werden. Diese Kontextmenüs sind allerdings nicht fest codiert, damit sie durch beliebige Plugins erweitert werden können. Wie genau das funktioniert wird in einem späteren Teil der Arbeit erklärt (Stichwort: IMenuProvider). Abb. 32: Buttons des MZ Header Panels mit aufgeklapptem Export Button Zu guter Letzt stehen dem Benutzer außerdem vier Buttons am oberen Rand des Panels zur Sebastian Porst - Entwicklung eines PE Editors Seite 39 von 61

Verfügung. Diese Buttons finden sich auch auf jedem anderen Panel des generellen Plugintyps wieder und werden deshalb nur einmal an dieser Stelle kurz beschrieben. Der erste Button von links (SaveButton) dient dazu die Daten des aktuellen Panels, in diesem Falle also den MZ Header der geöffneten Datei, zu speichern. Dabei gibt es für den Benutzer keinerlei Auswahlmöglichkeiten, als Zieldatei wird die geöffnete Datei benutzt, aus der der Header vorher gelesen wurde. Möchte der Benutzer die Daten des Headers in einer anderen Datei speichern, so kommt der zweite Button zum Einsatz (ExportButton). Er dient dazu die Daten in beliebigen Formaten (Binär, C++ Array,...) exportieren zu können. Analog zu den Kontextmenüs des Grids wird auch dieser Button dynamisch erzeugt, damit beliebige Plugins die Funktionalität dieses Buttons erweitern können. Die Erweiterung des Buttons kann über Plugins des Typs IConversionPlugin erfolgen, aber auch dazu später mehr. Der dritte Button (FixButton) dient dazu, alle ungültigen Werte im Grid zu korrigieren. Dies ist funktional äquivalent zum Klicken jedes sichtbaren TextCellValid Buttons im Grid. Über den vierten und letzten Button könnte der Benutzer den Teil der Hilfedatei aufrufen, der ihm weitere Informationen über den MZ Header gibt 16. 6.2.2. Das PE Header Plugin Abb. 33: Das PE Header Panel Das PE Header Plugin ist das zweite der allgemeinen, aus Plugin und Panel bestehenden, Plugins zur Anzeige von Daten aus PE Dateien. Durch den komplizierteren Aufbau des PE Headers einer Datei ist auch das Panel zur Anzeige der Daten entsprechend komplizierter. Die Anzeige ist grob gesehen dreigeteilt. Im linken Grid werden Daten aus den Strukturen IMAGE_FILE_HEADER und IMAGE_OPTIONAL_HEADER dargestellt. Im rechten Grid ist das nach dem optionalen Header folgende Array aus IMAGE_DATA_DIRECTORY Strukturen zu sehen. Über die Schaltfläche Sections gelangt der Benutzer zur dritten Ansicht, über die er die Daten des IMAGE_SECTION_HEADER Arrays anschauen und manipulieren kann. 16 Der Gebrauch des Konjunktiv in diesem Satz kommt ganz einfach daher, dass keine solche Hilfedatei existiert. Würde diese jedoch existieren, dann würde sie über diesen Button aufgerufen. Sebastian Porst - Entwicklung eines PE Editors Seite 40 von 61

Wie man in Abbildung 33 an der grünen Farbe einzelner Zellen des linken Grids erkennen kann kommen auch im PE Header Panel wieder TextCellValid Zellen zur Überprüfung und Berichtigung von Werten zum Einsatz. Zum ersten Mal werden außerdem in diesem Grid Zellen des Typs DialogCell benutzt. So erscheint bei einem Doppelklick auf die Zelle, die den Characteristics Wert enthält der Dialog aus Abbildung 35 der es dem Benutzer komfortabel ermöglicht, den Wert der Zelle zu verändern. Durch aktivieren oder deaktivieren der Checkboxes im Dialog ist es dem Benutzer möglich, ohne Kenntnisse über die genaue Struktur des 16 Bit Wertes Characteristics, diesen über symbolische Konstanten zu verändern. Die letztendliche Konvertierung der Konstanten zurück zum 16 Bit Wert wird vom Dialog übernommen. Auch ein vierter Zelltyp, die ComboBoxCell, kommt in diesem Grid zum Einsatz. Versucht der Benutzer den Wert der Zelle Machine zu verändern, klappt eine ComboBox auf, über die der Benutzer über die Namen der Prozessoren einen neuen Wert für das Feld auswählen kann (Abbildung 36). Das rechte Grid bringt auch eine Neuerung. Es ist das erste Grid, das keine feste Länge hat. Dies bedeutet, dass es dem Benutzer möglich sein muss, Einträge aus dem rechten Grid sowohl zu löschen als auch hinzuzufügen. Erreicht wird das Abb. 34: Menü zum Einfügen und Löschen von Einträgen in die IMAGE_DATA_DIRECTORY Struktur über ein Kontextmenü, welches beim Rechtsklick auf eine Zelle dieses Grids erscheint. In diesem Menü hat der Benutzer die Möglichkeit einen neuen Eintrag vor oder nach dem gewählten Eintrag hinzuzufügen oder den gewählten Eintrag zu löschen. Abb. 35: Das Fenster zum Editieren der Characteristics Zelle Das dritte und letzte Grid zur Darstellung von Werten aus dem PE Header stellt die Werte des so genannten Section Header dar und kombiniert Elemente der ersten beiden Grids. Zum einen ist es erweiterbar wie das zweite Grid. Zum anderen kommen erneut Zellen des Typs DialogCell zum Einsatz. In diesem Grid kann der Benutzer komfortabel die Werte der Sebastian Porst - Entwicklung eines PE Editors Seite 41 von 61

Zellen der Characteristics Spalte über einen Dialog editieren der genau so aussieht wie der bereits gezeigte Dialog, der im ersten Grid zum Einsatz kommt (natürlich mit anderen Werten). Aus Platzgründen wird darauf verzichtet auch diesen Dialog hier zu präsentieren. Abb. 36: Die möglichen Werte der Zelle Machine 6.2.3. Das Export Directory Plugin Das dritte Plugin zur Anzeige von Daten aus der Datei ist das erste Plugin, welches nicht bei allen Dateien verfügbar ist. Dies liegt ganz einfach daran, dass es das erste Plugin ist, das Daten eines optionalen Teils einer PE Datei darstellt. Das Panel dieses Plugins enthält zwei Grids, wobei das linke Grid von fester Länge ist und für die Darstellung der Werte aus dem Header des Export Directories verantwortlich ist. Das rechte Grid hingegen ist von variabler Größe und gibt eine Übersicht über die Funktionen, die die geladene Datei exportiert. An dieser Stelle sei erwähnt, dass dieses Plugin einen Mangel in der Pluginarchitektur offenbarte. Bei großen DLL Dateien, wie zum Beispiel kernel32.dll, die mehrere hundert Funktionen exportieren, dauert das Öffnen des Panels zwei bis drei Sekunden. Dies liegt ganz einfach daran, dass für jede einzelne Zelle des rechten Grids ein eigenes Kontextmenü erstellt wird und die Erschaffung mehrerer hundert solcher Menüs eben eine Weile dauert. Behoben wurde dieser Mangel aus Zeitgründen bisher nicht, aber um dieses Problem zu lösen, sehe ich grundsätzlich zwei Möglichkeiten. Zum einen könnte man versuchen für verschiedene Zellen das gleiche Menü zu benutzen und zum anderen wäre es möglich die gebrauchten Menüs erst beim Rechtsklick auf die Zelle zu erstellen. Ich halte die zweite Variante für besser, da es wahrscheinlich nicht möglich ist, vorauszusehen welche Zellen so gleichartig sind, dass man das gleiche Kontextmenü für sie verwenden könnte. Sebastian Porst - Entwicklung eines PE Editors Seite 42 von 61

Abb. 37: Das Panel des ExportDirectory Plugin 6.2.4. Das ImportDirectory Plugin Das ImportDirectory Plugin ist das nächste Plugin, welches optional vorhandene Daten aus einer PE Datei darstellt. Auch dieses Plugin enthält zwei Grids, wobei das obere Grid zur Darstellung der importierten DLL Dateien ist und das untere Grid eine Auflistung der importierten Funktionen aus der aktuelle selektierten DLL Datei gibt. Zu Beachten ist bei diesem Plugin, dass es das einzige Plugin ist, welches lediglich zur Darstellung und nicht zur Editierung der Daten dient. Dies kommt daher, dass PeLib, die zugrunde liegende Bibliothek zur Verarbeitung von PE Dateien, das Abb. 38: Das Panel des ImportDirectory Plugin Verändern von Daten aus dem ImportDirectory in der aktuellen Version noch nicht unterstützt, da dies etwas komplizierter ist als bei allen anderen Teilen einer PE Datei. Aus diesem Grunde sind auch alle Buttons bis auf den Hilfe-Button auf diesem Panel inaktiv. 6.2.5. Das ResourceDirectory Plugin Das Plugin zur Darstellung der Ressourcen ist das erste Plugin, das sich zur Anzeige der Daten noch anderer Steuerelemente außer FooGrid bedient. Dies kommt daher, dass das ResourceDirectory der einzige Teil einer PE Datei ist welches nicht als Liste strukturiert ist. Stattdessen werden die einzelnen Einträge in einer einfachen Baumstruktur geordnet, welche dann natürlich auch auf der GUI am besten als Baum dargestellt wird. Deshalb kommt zur Anzeige der Struktur eine Komponente vom Typ TreeView zum Einsatz. Weiterhin müssen auf der Blattebene des Baums die eigentlichen Ressourcen wie Bilder, Sebastian Porst - Entwicklung eines PE Editors Seite 43 von 61

Strings oder Videos in Binärform dargestellt werden, um eine Editierung zu ermöglichen. Zu diesem Zweck kommt hier zum ersten Mal die SJPHexEdit Komponente zum Einsatz, über die man Binärdaten in der gewohnten Weise als Hexdump ansehen und editieren kann. Abb. 39: Das Panel des ResourceDirectory Plugins 6.2.6. Das RelocationsDirectory Plugin Abb. 40: Das Panel des RelocationsDirectory Plugins Nach den komplizierteren Plugins ist das nächste Plugin wieder etwas einfacher gestrickt. Weil das RelocationsDirectory lediglich aus einem Header und den zu jedem Eintrag im Header Sebastian Porst - Entwicklung eines PE Editors Seite 44 von 61

gehörigen Tabellen besteht sind auf dem Panel lediglich zwei Grids mit je zwei Spalten zu sehen. Auch dieses Plugin leidet am Menüproblem das ich beim ExportDirectory Plugin beschrieben habe. Oft ist hier das Problem sogar noch offensichtlicher als beim ExportDirectory. 6.2.7. Das DebugDirectory Plugin Auch das jetzt folgende Plugin ist sehr einfach gehalten. Es besteht lediglich aus einem einzigen Grid, in dem die Tabellenstruktur des Debug Directories für den Benutzer sichtbar und editierbar ist. In der letzten Spalte des Grid kommen Zellen vom Typ TextCellValid zum Einsatz, die überprüfen ob das Offset in der letzten Spalte gültig ist. Abb. 41: Das Panel des DebugDirectory Plugins 6.2.8. Das TlsDirectory Plugin Von ganz anderer Art als die bisher vorgestellten Panels ist das Panel des TlsDirectory Plugins. Das TLS Directory ist so klein, dass bei der Darstellung der Daten dieses Directories auf die Grid Komponente zu Gunsten von normalen Textfeldern verzichtet wurde. 6.2.9. Das BoundImportDirectory Plugin Abb. 42: Das Panel des TLSDirectory Plugins Aus den gleichen Gründen wie das Panel des TlsDirectory Plugins kommt auch das Panel des BoundImportDirectory Plugin ohne Grid aus. Stattdessen kann der Benutzer mit Hilfe der Sebastian Porst - Entwicklung eines PE Editors Seite 45 von 61

Baumkomponente im linken Teil des Panels einen Eintrag aus diesem Directory auswählen. Danach ist es möglich die zu diesem Eintrag gehörenden Werte über die Textfelder im rechten Teil des Panels zu editieren. Abb. 43: Das Panel des BoundImport Directory Plugin 6.2.10. Das IATDirectory Plugin Das einfachste aller Plugins zum Anzeigen von Daten aus einer geladenen PE Datei ist das IATDirectory Plugin. Da das IAT Directory nur ein Array aus Speicheradressen ist kommt zur Anzeige dieses Arrays lediglich ein ein-spaltiges Grid zum Einsatz. 6.2.11. Das COM Header Directory Plugin Abb. 44: Das Panel des IAT Directory Plugin Das letzte aller Plugins ermöglicht es dem Benutzer die Werte des COM Header Directories zu Sebastian Porst - Entwicklung eines PE Editors Seite 46 von 61

verändern. Von der allgemeinen Struktur her gleicht das zugehörige Panel exakt dem des MZ Header Plugins. Auch hier wird ein zwei-spaltiges Grid aus Name/Wert-Paaren benutzt um dem Benutzer der Anwendung die Möglichkeit zur Ansicht und zur Editierung der Daten zu geben. Abb. 45: Das Panel des COM Header Directory Plugin 6.2.12. Das OffsetConversion Plugin Das letzte der dateiabhängigen Plugins ist zugleich das erste Plugin, das nicht zur Anzeige von Daten aus der Datei selbst stammt. Stattdessen dient es zur Umrechnung von RVAs, VAs und Adressen in der Datei. Da diese Umrechnungen auf dem Header einer Datei basiert, können sie nicht dateiunabhängig implementiert werden. Im obersten Textfeld hat der Benutzer die Möglichkeit eine RVA einzugeben, die dann sofort in die beiden anderen Adresstypen umgewandelt wird. Im zweiten Textfeld werden dagegen VAs und im dritten Textfeld Dateiadressen eingegeben. Abb. 46: Das Panel des OffsetConversion Plugin Da der Benutzer zur Eingabe von VAs die ImageBase der Datei kennen muss wird diese aus Komfortgründen auch angezeigt. Um es dem Benutzer einfacher zu machen sich zurechtzufinden wird außerdem die Sektion angezeigt, in der sich angegebene Adresse befindet. Sebastian Porst - Entwicklung eines PE Editors Seite 47 von 61

6.2.13. Das Hex View Plugin Abb. 47: Das Panel des Hex Viewer Plugin Das erste von zwei dateiunabhängigen Plugins, die auch in der GUI sichtbar sind, ist das Hex View Plugin. Es dient zur Visualisierung der Binärdaten der gerade geöffneten Teile einer PE Datei mit Hilfe der SJPHexView 17 Komponente. In Abbildung 47 ist zum Beispiel gerade die GUI des Hex View Plugin beim Anzeigen eines geöffneten Resource Directory zu sehen. Die Combobox, über die der Benutzer wählen kann, welche gerade geöffnete Komponente im Binärformat dargestellt werden soll, wird automatisch generiert. Zu diesem Zweck reagiert das Plugin auf die PluginRegistry Events DynamicPluginAdded und DynamicPluginRemoved. Sollte das dynamische Plugin welches den Event ausgelöst hat außerdem das Interface IDataPlugin implementieren, so wird es in die Liste aufgenommen beziehungsweise daraus gelöscht. 6.2.14. Das File Locator Plugin Das andere der zwei dateiunabhängigen Plugins, welche auch eine GUI bieten, wurde aus der Not des Beta-Testens geboren. Manche Directories sind so selten in PE Dateien zu finden, dass es schwer ist genug Dateien zu finden, die zum Testen herangezogen werden können. Dieses Plugin soll das Suchen nach Dateien die bestimmte Directories besitzen automatisieren. Über den linken Button kann der Benutzer ein Wurzelverzeichnis auswählen, dass nach einem Klick auf den rechten Button rekursiv durchsucht wird. In der Combobox unter dem Texfeld kann der Benutzer das Directory wählen nach dem Dateien durchsucht werden sollen. Alle gefundenen Dateien, die dieses Kriterium erfüllen, werden dann in der großen Listbox angezeigt. Durch einen Doppelklick auf die Listbox wird dann die gewählte Datei in den PE Editor geöffnet. 17 Diese Komponente wurde von mir bereits im Vorfeld zu diesem Projekt entwickelt und wird deshalb als gegeben betrachtet und nicht weiter besprochen. Sebastian Porst - Entwicklung eines PE Editors Seite 48 von 61

Abb. 48: Die GUI des FileLocar Plugin 6.2.15. Die Standard GUI Abb. 49: Die Standard GUI mit einigen geöffneten Plugin Panels Um es dem Benutzer zu ermöglichen die GUI des PE Editors zu wechseln wurde auch die GUI Teil des Pluginsystems. Da natürlich nicht genug Zeit war, um gleich mehrere GUIs zu implementieren, kommt in der aktuellen Version des Programms nur die so genannte Standard Sebastian Porst - Entwicklung eines PE Editors Seite 49 von 61

GUI zum Einsatz. Diese GUI ist ein einfaches MDI Fenster, welches die von den Plugins erzeugten Panels in eigenen Fenstern anzeigen kann. Im unteren Teil des Fensters befindet sich ein graues Textfeld, welches sich über die Breite des gesamten Fensters erstreckt. In diesem Textfeld werden nicht-kritische Nachrichten angezeigt von denen es schön wäre, wenn der Benutzer sie zur Kenntnis nähme, aber es auch nicht schlimm ist, wenn er mal eine Nachricht verpasst. Am oberen Rand des Fensters befinden sich das Hauptmenü und eine Buttonleiste, die zur Zeit nur aus einem einzigen Button besteht. Über diesen Button kann der Benutzer neue PE Dateien öffnen. Das Hauptmenü besteht aus fünf verschiedenen Einträgen. Da wären zum einen das beliebte File Menü, welches zum Öffnen von PE Dateien und zum Schließen der Anwendung benutzt werden kann. Abb. 50: Das geöffnete Last Open Menü der Standard GUI Rechts daneben befindet sich das Menü, über das der Benutzer Zugriff auf die bereits geöffneten Dateien enthält. Hier kann er dann alle dateiabhängigen Plugins aufrufen oder geöffnete Dateien schließen. Abb. 51: Das An dritter Position folgt das Menü, mit dem der Benutzer Zugriff auf alle geöffnete Plugins dateiunabhängigen Plugins hat. Menü der Standard GUI Das Settings Menü ist dafür gedacht, dass sich Plugins die besonders konfiguriert werden müssen oder können dort ein Untermenü registrieren um einen zentralen Ort zur Pluginkonfiguration zu bieten. In der ersten Version der Anwendung enthält dieses Plugin jedoch nur einen einzigen Eintrag zur Anzeige des Dialogs über den der Benutzer die Reihenfolge Plugins festlegen kann. Das fünfte und letzte Menü bietet die Möglichkeit die (nicht-vorhandene) Hilfedatei aufzurufen oder sich den About-Dialog anzusehen. Abb. 52: Das geöffnete Settings Menü der Standard GUI Der Dialog zur Festlegung der Reihenfolge sieht bei der Standard GUI folgendermaßen aus: Über die Liste auf der linken Seite kann man die Reihenfolge festlegen, in der die Plugins geladen werden sollen und ob ein Plugin überhaupt geladen werden soll. Es könnte ja vorkommen, dass ein Plugin beim letzten Benutzen des Programms Probleme bereitet hat und der Benutzer es erst mal vorübergehend deaktivieren will. um der Sebastian Porst - Entwicklung eines PE Editors Seite 50 von 61

Abb. 54: Das geöffnete Help Menü der Standard GUI Abb. 53: Beispiel für eine geöffnete Datei und die für die Datei verfügbaren Panels Die Reihenfolge der Plugins kann über die U (Up) und D (Down) Buttons verändert werden. Beim Klick auf diese Buttons wird das aktuell selektierte Plugin in der Hierarchie um eine Position nach oben oder unten bewegt. Im rechten Teil des Dialogs erscheinen Informationen über das zur Zeit gewählte Plugin. Abb. 55: Der Dialog in dem der Benutzer die Reihenfolge der Plugins festlegen kann 6.2.16. Die Kontextmenüs der Grids Sebastian Porst - Entwicklung eines PE Editors Seite 51 von 61

Wie bereits erwähnt wurde, besitzen alle Zellen der Grids auch Kontextmenüs, die der Benutzer durch einen Rechtsklick auf die Zelle aktivieren kann. Da diese Menüs durch beliebige Plugins erweiterbar sein müssen, war es nicht möglich diese Menüs bereits während der Implementierung festzulegen. Stattdessen kommen hier statische Plugins, die außerdem das Interface IMenuProvider implementieren, zum Einsatz. Immer, wenn eine neue Zelle in einem Grid angelegt wird, wird mit Hilfe der Klasse ContextMenuProvider und ihrer statischen Methoden CreateContextMenu für diese Zelle ein Kontextmenü erstellt. Zu diesem Zweck wird innerhalb der Methode über alle registrierten Plugins iteriert wobei jedes gefundene IMenuProvider Plugin gebeten wird doch bitte ein Menü für diese Zelle zu erstellen. Damit ein IMenuProvider überhaupt weiß, ob er ein Menü für diese Zelle erstellen kann, bekommt er mehrere Informationen über die Zelle übergeben. Diese Informationen sind die PE Datei zu der das Grid gehört, die Zelle selbst und eine Beschreibung der Zelle. Zellenbeschreibungen sind sicherlich der wichtigste Parameter, den der IMenuProvider bekommt. Mit Hilfe dieser Beschreibung, die nicht mehr als ein String-Array ist, kann der IMenuProvider entscheiden, ob und welche Menüs er für die Zelle generieren soll. Hier ist ein Beispiel für einen Ausschnitt eines solchen Arrays aus Zellenbeschreibungen. In diesem Array werden die Zellenbeschreibungen für die ersten fünf Felder des PE Headers definiert. Zu Beachten ist, dass die Beschreibungen in jeder Zeile von speziell zu allgemein geordnet sind, um dem IMenuProvider die Suche zu erleichtern. new String[] {"PeHeader::NtSignature", Constants.STR_DWORD}, new String[] {"PeHeader::Machine", Constants.STR_WORD}, new String[] {"PeHeader::NumberOfSections", Constants.STR_WORD}, new String[] {"PeHeader::TimeDateStamp", Constants.STR_TIMEDATESTAMP, Constants.STR_DWORD}, new String[] {"PeHeader::PointerToSymbolTable", Constants.STR_FILEOFFSET, Constants.STR_WORD}, An erster Stelle in der Beschreibung folgt immer der Name des Wertes. An letzter Stelle befindet sich immer der Typ des Zelleninhalts als simpler Datentyp (Byte, Word, Dword, String...). Dazwischen ist es möglich beliebig viele weitere Beschreibungen zu definieren. So ist zum Beispiel das TimeDateStamp Feld im obigen Beispiel als STR_TIMEDATESTAMP gekennzeichnet, um es Plugins, die Abb. 56: Kontextmenü einer mit beliebigen TimeDateStamps arbeiten, zu ermöglichen diese Gridzelle Zelle zu erkennen. Genauso verhält es sich mit der Kennzeichnung von PointerToSymbolTable als STR_FILEOFFSET. In Abbildung 56 wurde zum Beispiel die Zelle in der Spalte VAddress von einem IMenuProvider als RVA erkannt. Dieser IMenuProvider hat daraufhin dem Menü ein weiteres Untermenü hinzugefügt über das das OffsetConversion Plugin aufgerufen wird. 6.2.17. Die Menüs des Export Buttons Ganz analog zu den Kontextmenüs der Grids werden auch die Menüs der Export Buttons in den meisten Plugin Panels aufgebaut. In Abbildung 32 kann man ein Beispiel eines solchen Export Button sehen, der die Möglichkeit bietet, den aktuellen MZ Header entweder in Binärform, als C Array oder als Hex Dump zu exportieren. Sebastian Porst - Entwicklung eines PE Editors Seite 52 von 61

Anstatt nach Plugins des Typs IMenuProvider wird jetzt jedoch nach solchen des Typs IConversionPlugin gesucht. Für jedes gefundene Plugin dieses Typs wird dann dem Button ein Menü zugefügt, über das die Funktionalität des Plugins abrufbar ist. In der ersten Version des PE Editors werden drei Plugins vom Typ IConversionPlugin mitgeliefert. Das erste, ExportAsCArray, konvertiert die gewählten Daten in ein Array das in C und C++ Quellcodes verwendet werde kann. Eine mögliche Ausgabe des MZ Headers einer Datei sieht zum Beispiel folgendermaßen aus. char hexdata[] = { 0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00 }; Das zweite mitgelieferte Plugin nennt sich ExportAsText und erzeugt Hex Dumps. Die gleichen Daten wie oben sehen nach dem Export mit dem zweiten Plugin so aus. 00000000 4D5A 9000 0300 0000 0400 0000 FFFF 0000 MZ...??.. 00000010 B800 0000 0000 0000 4000 0000 0000 0000?... 00000020 0000 0000 0000 0000 0000 0000 0000 0000... 00000030 0000 0000 0000 0000 0000 0000 E000 0000...?... Das dritte Plugin dient vor allem zur Speicherung der gewählten Binärdaten in einer anderen Datei als der, aus der die Daten vom PE Editor gelesen wurden. Eine Beispielausgabe ist für dieses Plugin nicht möglich, da dieses PDF Dokument auf ASCII Zeichen beschränkt ist. Sebastian Porst - Entwicklung eines PE Editors Seite 53 von 61

7. Die Lokalisierungsstrategie 7.1. Übersicht Um die Benutzerfreundlichkeit des PE Editors zu erhöhen, sollten alle für den Benutzer sichtbaren Elemente auf einfache Weise in beliebige Sprachen übersetzt werden können. Glücklicherweise stellen Visual Studio und das.net Framework bereits standardisierte Funktionen zur Lokalisierung und Internationalisierung von.net Programmen zur Verfügung. 7.2. Übersicht über das.net Lokalisierungs-/Internationalisierungskonzept Das standardisierte Konzept zur Anpassung von Programmen an verschiedene Sprachen (z.b. Englisch, Deutsch,...) und/oder Kulturen (English-UK, Deutsch-DE,...) basiert auf einer Hierarchie so genannter Resource files mit der Dateiendung resx. Diese Dateien können theoretisch beliebige Ressourcen wie z.b. Strings, Bitmaps oder Icons enthalten. Für die Internationalisierung des PE Editors sind jedoch nur String-Ressourcen von Belang. Diese können in Visual Studio in einem recht komfortablen String-Ressourcen Editor erstellt und bearbeitet werden (siehe Abb. 57). Abb. 57: Der String-Ressourcen Editor von Visual Studio An der Spitze der Hierarchie steht eine so genannte Default Language resx-datei welche beim Erstellen des Projekts direkt zur erstellten Binärdatei hinzugefügt wird. Ressourcen aus dieser Datei werden benutzt, falls keine spezialisierteren Ressourcen für die gewählte Sprache oder Kultur gefunden werden können. Diese Standardressourcendatei hat in allen Projekten des PE Editors den Namen LocalizedStrings.resx und enthält Strings in englischer Sprache. Will man jetzt Ressourcendateien erstellen, die auf eine andere Sprache zugeschnitten sind, so muss man dem Projekt eine neue Ressourcendatei hinzufügen, welche fast so heißt wie die Standard-Ressourcendatei. Der einzige Unterschied im Namen der Datei ist, dass vor der Datei- Erweiterung resx das ISO-Kürzel zur Identifikation der Sprache/Kultur eingefügt wird. So heißen die lokalisierten Ressourcendateien für die deutsche Sprache in allen Projekten des PE Sebastian Porst - Entwicklung eines PE Editors Seite 54 von 61