Tiny mach-0 are fun

I suppose I’m just not good with this kind of things, e.g. maintaining a blog.

Anyways, this post is about something easy and fun. I was asked about a tiny hello-world mach-o and since it was actually at least a year from last time I’ve been playing with a mach-o file it felt like a very good excuse for playing with them once again.

First thing I did was to go over to www.osxbook.com. I was pretty sure Amit Singh would have wrote a tiny mach-o already, and that was the case.

Downloaded tiny.asm and started from this phrase There are plenty of zeros lurking in there.

  1. LC_SEGMENT segnames must be padded to 16 bytes, we can actually use that space for storing few opcodes (there must be at least a null termination after segname)
  2. we can use registers from LC_UNIXTHREAD for storing initial values we might need in order to avoid using bigger opcodes

That’s all, and it gives us a 180 bytes valid/working hello-world mach-o, which is, pretty small.

I think it’s still possible to have even a smaller one, but it would probably be >=10.8 only.

You can grab sources here.

Or have a quick look here.

 1;
 2; yasm -f bin rev_mach-o.asm
 3; it's tiny.asm from http://osxbook.com/blog/2009/03/15/crafting-a-tiny-mach-o-executable/
 4; with very few modifications in order to have both a valid and working hello-world mach-o
 5;
 6; https://twitter.com/__rev
 7;
 8
 9BITS 32
10        org   0x1000
11
12        db    0xce, 0xfa, 0xed, 0xfe       ; magic
13        dd    7                            ; cputype (CPU_TYPE_X86)
14        dd    3                            ; cpusubtype (CPU_SUBTYPE_I386_ALL)
15        dd    2                            ; filetype (MH_EXECUTE)
16        dd    2                            ; ncmds
17        dd    _ep2 - _text                 ; cmdsize
18        dd    0                            ; flags
19_text:
20        dd    1                            ; cmd (LC_SEGMENT)
21        dd    56                           ; cmdsize | sizeof(segment_command) makes it valid
22        db    "T"                          ; segname
23        db    0                            ; segname padding
24_ep:
25        ; SYS_write
26        push  msg_size                     ; this is part of segname padding
27        push  msg
28        push  ecx
29        push  ebx
30        int   0x80
31
32        ; - SYS_exit
33        push  ebx
34        jmp   _ep2
35
36        dd    0x1000                       ; vmaddr
37        dd    0x1000                       ; vmsize
38        dd    0                            ; fileoff
39        dd    filesize                     ; filesize
40        dd    7                            ; maxprot
41        dd    5                            ; initprot
42        dd    0                            ; nsects
43        dd    0                            ; flags
44
45        dd    5                            ; cmd (LC_UNIXTHREAD)
46        dd    80                           ; cmdsize
47        dd    1                            ; flavor (i386_THREAD_STATE)
48        dd    16                           ; count (i386_THREAD_STATE_COUNT)
49        dd    4, 0, 1, 0, 0, 0, 0, 0       ; state
50        dd    0, 0, _ep, 0, 0, 0, 0, 0     ; state
51_ep2:
52        mov   eax, ecx
53        int   0x80
54
55filesize equ  $ - $$
56
57msg db 'Hello world', 0ah
58        msg_size equ $ - msg

Thanks:

References: