
Halo semuanya
serangkaian artikel tentang menulis berbagai hal bermanfaat untuk IDA Pro berlanjut. Terakhir kali kami memperbaiki modul prosesor , dan hari ini kami akan fokus pada penulisan modul pemuat (loader) untuk satu sistem operasi vintage, yaitu untuk AmigaOS . Kami akan menulis dengan Python. Saya juga akan mencoba untuk mengungkapkan beberapa seluk-beluk ketika bekerja dengan reloc (mereka juga relocations
), yang ditemukan di banyak file yang dapat dieksekusi ( PE
, ELF
, MS-DOS
, dll.).
Pendahuluan
Mereka yang sebelumnya bekerja dengan format Amiga Hunk (dalam AmigaOS, ini adalah nama objek yang berisi kode yang executable
: executable
-, file library
, dll.) Dan memuat setidaknya satu file seperti itu di IDA, mungkin melihat bahwa bootloader adalah sudah ada (apalagi, bahkan ada sumber di IDA SDK
):


Ya, memang, semuanya sudah ditulis sebelum kita, tapi ... Semuanya diimplementasikan dengan sangat buruk sehingga menjadi tidak mungkin untuk bekerja dengan setidaknya beberapa file yang dapat dieksekusi normal.
Masalah Implementasi Saat Ini
Jadi di sini adalah daftar masalah:
- Relokasi. Dalam file Amiga Hunk, keberadaan mereka adalah praktik normal. Dan dalam implementasi yang ada, mereka bahkan berlaku saat mengunduh file. Tetapi ini tidak selalu dilakukan dengan benar (tautan yang dihasilkan mungkin tidak dihitung dengan benar).
Selain itu, Anda tidak akan dapat melakukan " program Rebase ... ". Fungsi ini tidak ada dalam loader. - File diunduh di alamat dasar
0x00000000
. Ini jelas salah, karena berbagai pustaka sistem dimuat pada titik nol. Akibatnya, tautan ke perpustakaan ini dibuat di ruang alamat file yang diunduh - Loader dapat diatur dengan berbagai flag yang tidak memungkinkan (atau, sebaliknya, memungkinkan) IDA untuk melakukan tindakan "mengenali" tertentu: mendefinisikan pointer, array, instruksi assembler.
Dalam situasi di mana itu tidak menyangkut x86 / x64 / ARM, seringkali setelah mengunduh file, daftar assembler sepertinya Anda ingin menutup IDA dari pandangan sekilas. (dan juga hapus file yang diselidiki dan pelajari cara menggunakan radare2) . Alasannya adalah flag bootloader default.

Menulis template bootloader
Sebenarnya, menulis bootloader itu mudah. Ada tiga panggilan balik yang perlu diterapkan:
1) accept_file(li, filename)
Melalui fungsi ini, IDA menentukan apakah pemuat ini dapat digunakan untuk mengunduh filename
def accept_file(li, filename): li.seek(0) tag = li.read(4) if tag == 'TAG1':
2) load_file(li, neflags, format)
Di sini, isi file diunduh ke basis data, pembuatan segmen / struktur / jenis, penggunaan relocks dan tindakan lainnya.
def load_file(li, neflags, format):
3) move_segm(frm, to, sz, fileformatname)
Jika ada relokasi dalam file yang diunduh, maka Anda tidak bisa hanya mengambil dan mengubah alamat pangkalan. Anda perlu menghitung ulang semua relokasi dan menambal tautannya. Secara umum, kode akan selalu sama. Di sini kita hanya melihat semua relocks yang dibuat sebelumnya, menambahkan delta padanya dan menerapkan tambalan ke byte dari file yang diunduh.
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
Templat bootloader import idaapi import ida_idp import ida_fixup def accept_file(li, filename): li.seek(0) tag = li.read(4) if tag == 'TAG1':
Menulis kode bootloader utama
Jadi, dengan dasar-dasar disortir. Mari kita siapkan "tempat kerja" untuk diri kita sendiri. Saya tidak tahu untuk apa orang suka menulis kode dengan Python, tapi saya suka melakukannya di PyCharm. Mari kita buat proyek baru dan tambahkan direktori dari IDA di jalur untuk mencari impor:

Orang-orang yang telah menghadapi masalah mengemulasi file yang dapat dieksekusi untuk AmigaOS mungkin pernah mendengar tentang proyek seperti amitools . Ini memiliki seperangkat alat yang hampir lengkap untuk bekerja dengan Amiga Hunk (baik untuk emulasi dan hanya untuk parsing). Saya mengusulkan atas dasar untuk melakukan "bootloader" (izin proyek memungkinkan, dan bootloader kami akan bersifat nirlaba).
Setelah pencarian singkat di amitools
file amitools
ditemukan. Ini mengimplementasikan parsing file, mendefinisikan segmen, relokasi, dan banyak lagi. Sebenarnya, file Relocate.py bertanggung jawab atas penggunaan relocks.
Sekarang bagian tersulit: dari semua pohon file amitools
, Anda amitools
menyeret semua yang direferensikan di BinFmtHunk.py
dan Relocate.py
ke file bootloader kami, membuat koreksi kecil di beberapa tempat.
Hal lain yang ingin saya tambahkan adalah definisi untuk setiap segmen dari posisi dalam file dari mana data diunduh. Ini dilakukan dengan menambahkan atribut data_offset
ke dua kelas: HunkSegmentBlock
dan HunkOverlayBlock
. Ternyata kode berikut:
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)
Sekarang kita perlu menambahkan atribut ini ke kelas Segment
, yang dibuat nanti dari blok Hunk
:
Segmen 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
Selanjutnya, untuk kelas BinFmtHunk
, BinFmtHunk
menambahkan penggunaan data_offset
saat membuat segmen. Ini dilakukan dalam metode create_image_from_load_seg_file
dalam loop enumerasi blok segmen:
create_image_from_load_seg_file segs = lsf.get_segments() for seg in segs:
Menulis kode untuk panggilan balik IDA
Sekarang kita memiliki semua yang kita butuhkan, mari kita menulis kode untuk panggilan balik. Yang pertama adalah 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
Semuanya sederhana: kita membaca empat byte pertama, membuat file virtual ( StringIO
) darinya, dan meneruskannya ke fungsi is_image_fobj
, yang mengembalikan True
jika file tersebut dalam format yang sesuai. Dalam hal ini, kami mengembalikan kamus dengan dua bidang: format
(deskripsi teks dari format yang dimuat) dan processor
(di bawah platform mana kode yang dapat dieksekusi ditulis).
Selanjutnya, Anda perlu mengunggah file ke IDB. Itu lebih rumit. Hal pertama yang harus dilakukan adalah memaksa tipe prosesor untuk diinstal pada Motorola 68040
diperlukan:
idaapi.set_processor_type('68040', ida_idp.SETPROC_LOADER)
Atur flag untuk bootloader sehingga tidak ada game yang dikenali dan array tidak dibuat dari semuanya secara berurutan (deskripsi flag bisa ditemukan di sini ):
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
Kami mentransfer konten file yang diunduh ke BinFmtHunk
(parsing dan semua itu):
li.seek(0) data = li.read(li.size()) bf = BinFmtHunk() fobj = StringIO.StringIO(data) bi = bf.load_image_fobj(fobj)
Dengan nol alamat pengunduhan, saya mengusulkan untuk berurusan dengan memilih ImageBase
lain. Omong-omong, file yang dapat dieksekusi di AmigaOS diunduh hanya pada alamat yang dapat diakses, tidak ada alamat virtual di sana. Saya memilih 0x21F000
, itu indah dan tidak mungkin bertepatan dengan konstanta. Terapkan itu:
rel = Relocate(bi)
Tambahkan alamat mulai dari mana program dimulai:
Saatnya memuat segmen ke dalam basis data dan membuat relocks (dalam terminologi IDA: 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())
Hasil akhir untuk load_file
:
return 1
Kode move_segm
hanya diambil tanpa perubahan.
Kode ringkasan dan kesimpulan bootloader
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 , . , - , , .
.