Was ist die kleinstmögliche ausführbare ausführbare Mach-O- Datei auf x86_64? Das Programm kann nichts tun (nicht einmal einen Rückkehrcode zurückgeben), sondern muss eine gültige ausführbare Datei sein (muss fehlerfrei ausgeführt werden).
Mein Versuch:
GNU Assembler ( null.s
):
.text
.globl _main
_main:
retq
Zusammenstellung & Verknüpfung:
as -o null.o null.s
ld -e _main -macosx_version_min 10.12 -o null null.o -lSystem
Größe: 4248 Bytes
Wenn man sich die Hex-Werte ansieht, scheint es eine Menge Null-Polster zu geben, die vielleicht entfernt werden können, aber ich weiß nicht wie. Ich weiß auch nicht, ob es möglich ist, das Exectubale auszuführen, ohne libSystem zu verknüpfen ...
code-golf
tips
assembly
machine-code
Martin M.
quelle
quelle
Antworten:
Das kleinste ausführbare Mach-O muss mindestens
0x1000
Bytes umfassen. Aufgrund der XNU-Einschränkung muss die Datei mindestens von seinPAGE_SIZE
. Siehexnu-4570.1.46/bsd/kern/mach_loader.c
um die Linie 1600.Wenn wir diese Auffüllung jedoch nicht zählen und nur sinnvolle Nutzdaten zählen, beträgt die unter macOS ausführbare minimale Dateigröße
0xA4
Bytes.Es muss mit mach_header beginnen (oder
fat_header
/mach_header_64
, aber diese sind größer).Die Größe ist
0x1C
Bytes.magic
muss seinMH_MAGIC
.Ich werde verwenden,
CPU_TYPE_X86
da es einex86_32
ausführbare Datei ist.filtetype
muss sein ,MH_EXECUTE
für ausführbare Datei,ncmds
undsizeofcmds
hängen von Befehlen und haben ihre Gültigkeit.flags
sind nicht so wichtig und zu klein, um einen anderen Wert zu liefern.Als nächstes folgen Ladebefehle. Der Header muss genau in einer Zuordnung mit RX-Rechten enthalten sein - wiederum XNU-Einschränkungen.
Wir müssten unseren Code auch in eine RX-Zuordnung einfügen, damit dies in Ordnung ist.
Dafür brauchen wir eine
segment_command
.Schauen wir uns die Definition an.
cmd
muss seinLC_SEGMENT
undcmdsize
muss seinsizeof(struct segment_command) => 0x38
.segname
Inhalte spielen keine Rolle, und wir werden sie später verwenden.vmaddr
muss eine gültige Adresse sein (ich werde sie verwenden0x1000
),vmsize
muss gültig sein und ein Vielfaches vonPAGE_SIZE
,fileoff
muss sein0
,filesize
muss kleiner als die Dateigröße sein, aber größer alsmach_header
mindestens (sizeof(header) + header.sizeofcmds
ist das, was ich verwendet habe).maxprot
undinitprot
muss seinVM_PROT_READ | VM_PROT_EXECUTE
.maxport
in der Regel auch hatVM_PROT_WRITE
.nsects
sind 0, da wir eigentlich keine Abschnitte benötigen und sie sich zu der Größe addieren. Ich habeflags
auf 0 gesetzt.Jetzt müssen wir etwas Code ausführen. Dafür gibt es zwei Ladebefehle:
entry_point_command
undthread_command
.entry_point_command
passt nicht zu uns: siehexnu-4570.1.46/bsd/kern/mach_loader.c
, um Linie 1977:Um es zu verwenden, müsste DYLD funktionieren, und das bedeutet, wir brauchen
__LINKEDIT
, leersymtab_command
unddysymtab_command
,dylinker_command
unddyld_info_command
. Overkill für "kleinste" Datei.Wir werden es also verwenden
thread_command
, insbesondereLC_UNIXTHREAD
weil es auch einen Stapel erstellt, den wir benötigen.cmd
wird seinLC_UNIXTHREAD
,cmdsize
wäre0x50
(siehe unten).flavour
istx86_THREAD_STATE32
und count istx86_THREAD_STATE32_COUNT
(0x10
).Nun die
thread_state
. Wir brauchenx86_thread_state32_t
aka_STRUCT_X86_THREAD_STATE32
:Es sind also tatsächlich 16
uint32_t
, die in entsprechende Register geladen werden, bevor der Thread gestartet wird.Durch Hinzufügen von Header, Segmentbefehl und Threadbefehl erhalten wir
0xA4
Bytes.Jetzt ist es Zeit, die Nutzlast herzustellen.
Nehmen wir an, wir möchten, dass es gedruckt wird
Hi Frand
undexit(0)
.Syscall-Konvention für macOS x86_32:
Weitere Informationen zu Syscalls unter macOS finden Sie hier .
Da wir das wissen, ist hier unsere Nutzlast in der Montage:
Beachten Sie zuerst die Zeile
int 0x80
.segname
kann alles sein, erinnerst du dich? So können wir unsere Nutzlast hineinlegen. Es sind jedoch nur 16 Bytes, und wir brauchen etwas mehr.Bei
14
Bytes platzieren wir also ajmp
.Ein weiterer "freier" Speicherplatz sind Thread-Statusregister.
Wir können in den meisten von ihnen alles einstellen, und wir werden den Rest unserer Nutzlast dort ablegen.
Außerdem legen wir unsere Saite in
__eax
und__ebx
, da sie kürzer ist als das Bewegen.So können wir nutzen
__ecx
,__edx
,__edi
den Rest unserer Nutzlast zu passen. Wenn wir den Unterschied zwischen der Adressethread_cmd.state.__ecx
und dem Ende von betrachtensegment_cmd.segname
, berechnen wir, dass wir die letzten zwei Bytes von eingeben müssenjmp 0x3a
(oderEB38
)segname
.Unsere zusammengebaute Nutzlast ist also
53 50 31C0 89E7 6A08 57 6A01 50 B004
für den ersten Teil,EB38
für jmp undCD80 6A00 B001 50 CD80
für den zweiten Teil.Und letzter Schritt - Einstellen der
__eip
. Unsere Datei wird um0x1000
(erinnernvmaddr
) geladen und die Nutzlast beginnt mit dem Offset0x24
.Hier ist die
xxd
Ergebnisdatei:Füllen Sie es mit bis zu
0x1000
Bytes, chmod + x und führen Sie es aus :)PS Über x86_64 - 64-Bit-Binärdateien sind erforderlich
__PAGEZERO
(jede Zuordnung mitVM_PROT_NONE
Schutzdeckblatt bei 0x0). IIRC sie [Apple] haben es nicht im 32-Bit-Modus erforderlich gemacht, nur weil einige ältere Software es nicht hatte und sie Angst haben, es zu brechen.quelle
truncate -s 4096 foo
(wobei foo die ausführbare Datei ist), um sie an0x1000
Bytes anzupassen, und sie funktioniert perfekt :)28 Bytes, vorkompiliert.
Unten ist ein formatierter Hex-Dump der Mach-O-Binärdatei.
Besteht vollständig aus dem Header und benötigt weder die Daten noch die Cmds. Dies ist von Natur aus die kleinstmögliche Mach-O-Binärdatei. Es läuft möglicherweise auf keiner denkbaren Hardware korrekt, entspricht jedoch der Spezifikation.
Ich würde die eigentliche Datei liefern, aber sie besteht vollständig aus nicht druckbaren Zeichen.
quelle
(uint) 0x00000007 ist "I386" und "X86" (Name abhängig davon, wo in der XNU-Spezifikation Sie suchen, aber es ist der richtige Bogen) (uint) 0x0x01000007 ist X86_64
Theoretisch können Sie mit 0x1000000 einen beliebigen CPU-Wert ODER eine 64-Bit-Version erstellen. XNU scheint sie nicht immer als diskrete Werte zu betrachten. Beispielsweise sind ARM 32 und 64 0x0000000C bzw. 0x0100000C.
Zum Teufel, hier ist die Liste, die ich vor ein paar Jahren herausfinden musste. Beachten Sie, dass die meisten davon älter sind als OS / X:
quelle