
Hallo an alle,
Eine Reihe von Artikeln zum Schreiben verschiedener nützlicher Dinge für IDA Pro wird fortgesetzt. Das letzte Mal haben wir das Prozessormodul repariert , und heute werden wir uns darauf konzentrieren, ein Loader-Modul (Loader) für ein altes Betriebssystem zu schreiben, nämlich für AmigaOS . Wir werden in Python schreiben. Ich werde auch versuchen, einige Feinheiten bei der Arbeit mit Relocs (sie sind auch relocations
) aufzudecken, die in vielen ausführbaren Dateien ( PE
, ELF
, MS-DOS
usw.) enthalten sind.
Einführung
Diejenigen, die zuvor mit dem Amiga Hunk- Format gearbeitet haben (in AmigaOS ist dies der Name von Objekten, die ausführbaren Code enthalten: executable
Dateien -, library
usw.) und mindestens eine solche Datei in die IDA geladen haben, haben wahrscheinlich gesehen, dass dies der Bootloader ist es existiert bereits (außerdem gibt es sogar Quellen im IDA SDK
):


Ja, in der Tat wurde bereits alles vor uns geschrieben, aber ... Alles ist so schlecht implementiert, dass es einfach unmöglich wird, mit mindestens einer normalen ausführbaren Datei zu arbeiten.
Aktuelle Implementierungsprobleme
Hier ist eine Liste von Problemen:
- Umzüge. In Amiga Hunk-Dateien ist ihre Anwesenheit üblich. In der vorhandenen Implementierung gelten sie sogar beim Herunterladen einer Datei. Dies wird jedoch nicht immer korrekt durchgeführt (der resultierende Link wird möglicherweise nicht korrekt berechnet).
Außerdem können Sie kein " Rebase-Programm ... " ausführen . Diese Funktion fehlt im Lader. - Die Datei wird unter der Basisadresse
0x00000000
. Dies ist definitiv falsch, da verschiedene Systembibliotheken mit Nullpunktverschiebung geladen werden. Infolgedessen werden im Adressraum der heruntergeladenen Datei Links zu diesen Bibliotheken erstellt - Der Loader kann mit verschiedenen Flags gesetzt werden, die es der IDA nicht erlauben (oder umgekehrt erlauben), bestimmte "Erkennungs" -Aktionen auszuführen: Definieren von Zeigern, Arrays, Assembler-Anweisungen.
In Situationen, in denen x86 / x64 / ARM nicht betroffen ist, sieht die Assembler-Liste häufig nach dem Herunterladen einer Datei so aus, als ob Sie die IDA auf einen Blick schließen möchten (und löschen Sie auch die untersuchte Datei und lernen Sie, wie man radare2 verwendet) . Der Grund dafür sind die Standard-Bootloader-Flags.

Schreiben einer Bootloader-Vorlage
Eigentlich ist das Schreiben eines Bootloaders einfach. Es gibt drei Rückrufe, die implementiert werden müssen:
1) accept_file(li, filename)
Über diese Funktion bestimmt der IDA, ob dieser Loader zum Herunterladen des filename
def accept_file(li, filename): li.seek(0) tag = li.read(4) if tag == 'TAG1':
2) load_file(li, neflags, format)
Hier wird der Inhalt der Datei in die Datenbank heruntergeladen, Segmente / Strukturen / Typen erstellt, Relocks verwendet und andere Aktionen ausgeführt.
def load_file(li, neflags, format):
3) move_segm(frm, to, sz, fileformatname)
Wenn die heruntergeladene Datei Umzüge enthält, können Sie die Basisadresse nicht einfach übernehmen und ändern. Sie müssen alle Umzüge nachzählen und die Links patchen. Im Allgemeinen ist der Code immer der gleiche. Hier gehen wir einfach alle zuvor erstellten Relocks durch, fügen ihnen ein Delta hinzu und wenden Patches auf die Bytes der heruntergeladenen Datei an.
def move_segm(frm, to, sz, fileformatname): delta = to xEA = ida_fixup.get_first_fixup_ea() while xEA != idaapi.BADADDR: fd = ida_fixup.fixup_data_t(idaapi.FIXUP_OFF32) ida_fixup.get_fixup(xEA, fd) fd.off += delta if fd.get_type() == ida_fixup.FIXUP_OFF8: idaapi.put_byte(xEA, fd.off) elif fd.get_type() == ida_fixup.FIXUP_OFF16: idaapi.put_word(xEA, fd.off) elif fd.get_type() == ida_fixup.FIXUP_OFF32: idaapi.put_long(xEA, fd.off) fd.set(xEA) xEA = ida_fixup.get_next_fixup_ea(xEA) idaapi.cvar.inf.baseaddr = idaapi.cvar.inf.baseaddr + delta return 1
Bootloader-Vorlage import idaapi import ida_idp import ida_fixup def accept_file(li, filename): li.seek(0) tag = li.read(4) if tag == 'TAG1':
Schreiben des Haupt-Bootloader-Codes
Also, mit den Grundlagen geklärt. Bereiten wir uns einen „Arbeitsplatz“ vor. Ich weiß nicht, wofür jemand gerne Code in Python schreibt, aber ich mache es gerne in PyCharm. Erstellen wir ein neues Projekt und fügen das Verzeichnis aus der IDA in den Pfaden hinzu, um nach Importen zu suchen:

Leute, die bereits mit dem Problem der Emulation ausführbarer Dateien für AmigaOS konfrontiert waren, haben wahrscheinlich von einem Projekt wie amitools gehört . Es verfügt über einen fast vollständigen Satz von Tools für die Arbeit mit Amiga Hunk (sowohl zur Emulation als auch nur zum Parsen). Ich schlage auf seiner Grundlage vor, den „Bootloader“ zu machen (die Projektlizenz erlaubt es, und unser Loader wird gemeinnützig sein).
Nach einer kurzen Suche in amitools
Datei amitools
gefunden. Es implementiert das Parsen einer Datei, das Definieren von Segmenten, Verschiebungen und vieles mehr. Tatsächlich ist die Datei Relocate.py für die Verwendung von Relocks verantwortlich.
Jetzt der schwierigste Teil: Aus all diesem Baum von amitools
Dateien müssen Sie alles, BinFmtHunk.py
in BinFmtHunk.py
und Relocate.py
BinFmtHunk.py
in unsere Bootloader-Datei ziehen und an einigen Stellen kleinere Korrekturen vornehmen.
Eine andere Sache, die ich hinzufügen möchte, ist die Definition für jedes Segment der Position in der Datei, von der die Daten heruntergeladen wurden. Dazu wird das Attribut data_offset
zwei Klassen HunkSegmentBlock
: HunkSegmentBlock
und HunkOverlayBlock
. Es stellt sich folgender Code heraus:
HunkSegmentBlock class HunkSegmentBlock(HunkBlock): """HUNK_CODE, HUNK_DATA, HUNK_BSS""" def __init__(self, blk_id=None, data=None, data_offset=0, size_longs=0): HunkBlock.__init__(self) if blk_id is not None: self.blk_id = blk_id self.data = data self.data_offset = data_offset self.size_longs = size_longs def parse(self, f): size = self._read_long(f) self.size_longs = size if self.blk_id != HUNK_BSS: size *= 4 self.data_offset = f.tell() self.data = f.read(size)
HunkOverlayBlock class HunkOverlayBlock(HunkBlock): """HUNK_OVERLAY""" blk_id = HUNK_OVERLAY def __init__(self): HunkBlock.__init__(self) self.data_offset = 0 self.data = None def parse(self, f): num_longs = self._read_long(f) self.data_offset = f.tell() self.data = f.read(num_longs * 4)
Jetzt müssen wir dieses Attribut der Segment
hinzufügen, die später aus Hunk
Blöcken erstellt wird:
Segment class Segment: def __init__(self, seg_type, size, data=None, data_offset=0, flags=0): self.seg_type = seg_type self.size = size self.data_offset = data_offset self.data = data self.flags = flags self.relocs = {} self.symtab = None self.id = None self.file_data = None self.debug_line = None
Als Nächstes fügen BinFmtHunk
für die BinFmtHunk
Klasse die Verwendung von data_offset
beim Erstellen von Segmenten hinzu. Dies erfolgt in der Methode create_image_from_load_seg_file
in einer Segmentblock-Aufzählungsschleife:
create_image_from_load_seg_file segs = lsf.get_segments() for seg in segs:
Schreiben eines Codes für IDA-Rückrufe
Nachdem wir alles haben, was wir brauchen, schreiben wir einen Code für Rückrufe. Das erste ist accept_file
:
accept_file def accept_file(li, filename): li.seek(0) bf = BinFmtHunk() tag = li.read(4) tagf = StringIO.StringIO(tag) if bf.is_image_fobj(tagf): return {'format': 'Amiga Hunk executable', 'processor': '68040'} else: return 0
Alles ist einfach: Wir lesen die ersten vier Bytes, erstellen StringIO
eine virtuelle Datei ( StringIO
) und übergeben sie an die Funktion is_image_fobj
, die True
zurückgibt, wenn die Datei das entsprechende Format hat. In diesem Fall geben wir das Wörterbuch mit zwei Feldern zurück: format
(Textbeschreibung des geladenen Formats) und processor
(unter welcher Plattform der ausführbare Code geschrieben wird).
Als nächstes müssen Sie die Datei in IDB hochladen. Es ist komplizierter. Als erstes müssen Sie den Prozessortyp auf das erforderliche Motorola 68040
zwingen:
idaapi.set_processor_type('68040', ida_idp.SETPROC_LOADER)
Setzen Sie die Flags für den Bootloader so, dass kein Spiel erkannt wird und Arrays nicht aus allem in einer Reihe erstellt werden (eine Beschreibung der Flags finden Sie hier ):
idaapi.cvar.inf.af = idaapi.AF_CODE | idaapi.AF_JUMPTBL | idaapi.AF_USED | idaapi.AF_UNK | \ idaapi.AF_PROC | idaapi.AF_LVAR | idaapi.AF_STKARG | idaapi.AF_REGARG | \ idaapi.AF_TRACE | idaapi.AF_VERSP | idaapi.AF_ANORET | idaapi.AF_MEMFUNC | \ idaapi.AF_TRFUNC | idaapi.AF_FIXUP | idaapi.AF_JFUNC | idaapi.AF_NULLSUB | \ idaapi.AF_NULLSUB | idaapi.AF_IMMOFF | idaapi.AF_STRLIT
Wir übertragen den Inhalt der heruntergeladenen Datei an BinFmtHunk
(Parsing und all das):
li.seek(0) data = li.read(li.size()) bf = BinFmtHunk() fobj = StringIO.StringIO(data) bi = bf.load_image_fobj(fobj)
Mit einer Download-Adresse von Null schlage ich vor, mich mit der Auswahl einer anderen ImageBase
zu befassen. Übrigens werden ausführbare Dateien in AmigaOS nur an zugänglichen Adressen heruntergeladen, dort gibt es keine virtuellen Adressen. Ich habe 0x21F000
, es ist wunderschön und es ist unwahrscheinlich, dass es mit einer Konstanten übereinstimmt. Wende es an:
rel = Relocate(bi)
Fügen Sie die Startadresse hinzu, von der aus das Programm startet:
Es ist Zeit, Segmente in die Datenbank zu laden und Relocks zu erstellen (in der IDA-Terminologie: reloc == fixup
):
for seg in bi.get_segments(): offset = addrs[seg.id] size = seg.size to_segs = seg.get_reloc_to_segs() for to_seg in to_segs: reloc = seg.get_reloc(to_seg) for r in reloc.get_relocs(): offset2 = r.get_offset() rel_off = Relocate.read_long(datas[seg.id], offset2) addr = offset + rel_off + r.addend fd = idaapi.fixup_data_t(idaapi.FIXUP_OFF32) fd.off = addr fd.set(offset + offset2) idaapi.mem2base(str(datas[seg.id]), offset, seg.data_offset) idaapi.add_segm(0, offset, offset + size, 'SEG_%02d' % seg.id, seg.get_type_name())
Endergebnis für load_file
:
return 1
Der Code move_segm
nur ohne Änderungen.
Der Bootloader-Zusammenfassungscode und die Schlussfolgerungen
amiga_hunk.py import idaapi import ida_idp import ida_fixup import StringIO import struct HUNK_UNIT = 999 HUNK_NAME = 1000 HUNK_CODE = 1001 HUNK_DATA = 1002 HUNK_BSS = 1003 HUNK_ABSRELOC32 = 1004 HUNK_RELRELOC16 = 1005 HUNK_RELRELOC8 = 1006 HUNK_EXT = 1007 HUNK_SYMBOL = 1008 HUNK_DEBUG = 1009 HUNK_END = 1010 HUNK_HEADER = 1011 HUNK_OVERLAY = 1013 HUNK_BREAK = 1014 HUNK_DREL32 = 1015 HUNK_DREL16 = 1016 HUNK_DREL8 = 1017 HUNK_LIB = 1018 HUNK_INDEX = 1019 HUNK_RELOC32SHORT = 1020 HUNK_RELRELOC32 = 1021 HUNK_ABSRELOC16 = 1022 HUNK_PPC_CODE = 1257 HUNK_RELRELOC26 = 1260 hunk_names = { HUNK_UNIT: "HUNK_UNIT", HUNK_NAME: "HUNK_NAME", HUNK_CODE: "HUNK_CODE", HUNK_DATA: "HUNK_DATA", HUNK_BSS: "HUNK_BSS", HUNK_ABSRELOC32: "HUNK_ABSRELOC32", HUNK_RELRELOC16: "HUNK_RELRELOC16", HUNK_RELRELOC8: "HUNK_RELRELOC8", HUNK_EXT: "HUNK_EXT", HUNK_SYMBOL: "HUNK_SYMBOL", HUNK_DEBUG: "HUNK_DEBUG", HUNK_END: "HUNK_END", HUNK_HEADER: "HUNK_HEADER", HUNK_OVERLAY: "HUNK_OVERLAY", HUNK_BREAK: "HUNK_BREAK", HUNK_DREL32: "HUNK_DREL32", HUNK_DREL16: "HUNK_DREL16", HUNK_DREL8: "HUNK_DREL8", HUNK_LIB: "HUNK_LIB", HUNK_INDEX: "HUNK_INDEX", HUNK_RELOC32SHORT: "HUNK_RELOC32SHORT", HUNK_RELRELOC32: "HUNK_RELRELOC32", HUNK_ABSRELOC16: "HUNK_ABSRELOC16", HUNK_PPC_CODE: "HUNK_PPC_CODE", HUNK_RELRELOC26: "HUNK_RELRELOC26", } EXT_SYMB = 0 EXT_DEF = 1 EXT_ABS = 2 EXT_RES = 3 EXT_ABSREF32 = 129 EXT_ABSCOMMON = 130 EXT_RELREF16 = 131 EXT_RELREF8 = 132 EXT_DEXT32 = 133 EXT_DEXT16 = 134 EXT_DEXT8 = 135 EXT_RELREF32 = 136 EXT_RELCOMMON = 137 EXT_ABSREF16 = 138 EXT_ABSREF8 = 139 EXT_RELREF26 = 229 TYPE_UNKNOWN = 0 TYPE_LOADSEG = 1 TYPE_UNIT = 2 TYPE_LIB = 3 HUNK_TYPE_MASK = 0xffff SEGMENT_TYPE_CODE = 0 SEGMENT_TYPE_DATA = 1 SEGMENT_TYPE_BSS = 2 BIN_IMAGE_TYPE_HUNK = 0 segment_type_names = [ "CODE", "DATA", "BSS" ] loadseg_valid_begin_hunks = [ HUNK_CODE, HUNK_DATA, HUNK_BSS, HUNK_PPC_CODE ] loadseg_valid_extra_hunks = [ HUNK_ABSRELOC32, HUNK_RELOC32SHORT, HUNK_DEBUG, HUNK_SYMBOL, HUNK_NAME ] class HunkParseError(Exception): def __init__(self, msg): self.msg = msg def __str__(self): return self.msg class HunkBlock: """Base class for all hunk block types""" def __init__(self): pass blk_id = 0xdeadbeef sub_offset = None
: https://github.com/lab313ru/amiga_hunk_loader
IDA, , . «» Python , , ( -?), .
- IDA , . , - , , .
.