Mach-O入門 実践編

準備

というわけで、Mach-Oファイルの構造がわかったところで、実際のファイルで確かめてみることにする。
C言語で簡単な実行ファイルを作成して、otoolで構造を表示させてみよう。プログラムはこんな感じ。test.cというファイル名で保存する。

#include <stdio.h>

int main(void)
{
  printf("Hello World\n");
  return 0;
}

gccコンパイルしよう。

> gcc test.c -o test

準備完了。試しに実行してみる。

> ./test
Hello World

うん、いいみたいだね。

共有ライブラリ

x86Linuxだとlibcがダイナミックリンクされるプログラムだから、Mach-Oでも同じはず。otoolの-Lオプションを使って、依存する共有ライブラリを調べてみよう。

> otool -L test   
test:
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.3.4)

libSystem.Bに依存してるらしい。

Mach-Oヘッダ

otoolでMach-Oヘッダを表示してみる。

> otool -h -v test
test:
Mach header
      magic cputype cpusubtype   filetype ncmds sizeofcmds      flags
   MH_MAGIC    I386        ALL    EXECUTE    11       1104   NOUNDEFS DYLDLINK TWOLEVEL SUBSECTIONS_VIA_SYMBOLS

i386向けの実行ファイルで、ロードコマンドを11個含んでいることがわかる。

ロードコマンド

otoolでロードコマンドを表示してみる。-vオプションで、多少見やすくすることが可能。11個のロードコマンドがすべて表示されるので注意。

> otool -l -v test
test:
Load command 0
      cmd LC_SEGMENT
  cmdsize 56
  segname __PAGEZERO
   vmaddr 0x00000000
   vmsize 0x00001000
  fileoff 0
 filesize 0
  maxprot ---
 initprot ---
   nsects 0
    flags NORELOC
Load command 1
      cmd LC_SEGMENT
  cmdsize 260
  segname __TEXT
   vmaddr 0x00001000
   vmsize 0x00001000
  fileoff 0
 filesize 4096
  maxprot rwx
 initprot r-x
   nsects 3
    flags (none)
Section
  sectname __text
   segname __TEXT
      addr 0x00001e60
      size 0x00000143
    offset 3680
     align 2^2 (4)
    reloff 0
    nreloc 0
      type S_REGULAR
attributes PURE_INSTRUCTIONS SOME_INSTRUCTIONS
 reserved1 0
 reserved2 0
Section
  sectname __cstring
   segname __TEXT
      addr 0x00001fa4
      size 0x00000058
    offset 4004
     align 2^2 (4)
    reloff 0
    nreloc 0
      type S_CSTRING_LITERALS
attributes (none)
 reserved1 0
 reserved2 0
Section
  sectname __textcoal_nt
   segname __TEXT
      addr 0x00001ffc
      size 0x00000004
    offset 4092
     align 2^0 (1)
    reloff 0
    nreloc 0
      type S_COALESCED
attributes PURE_INSTRUCTIONS SOME_INSTRUCTIONS
 reserved1 0
 reserved2 0
Load command 2
      cmd LC_SEGMENT
  cmdsize 260
  segname __DATA
   vmaddr 0x00002000
   vmsize 0x00001000
  fileoff 4096
 filesize 4096
  maxprot rw-
 initprot rw-
   nsects 3
    flags (none)
Section
  sectname __data
   segname __DATA
      addr 0x00002000
      size 0x00000010
    offset 4096
     align 2^2 (4)
    reloff 0
    nreloc 0
      type S_REGULAR
attributes (none)
 reserved1 0
 reserved2 0
Section
  sectname __dyld
   segname __DATA
      addr 0x00002010
      size 0x00000008
    offset 4112
     align 2^2 (4)
    reloff 0
    nreloc 0
      type S_REGULAR
attributes (none)
 reserved1 0
 reserved2 0
Section
  sectname __common
   segname __DATA
      addr 0x00002020
      size 0x00000034
    offset 0
     align 2^4 (16)
    reloff 0
    nreloc 0
      type S_ZEROFILL
attributes (none)
 reserved1 0
 reserved2 0
Load command 3
      cmd LC_SEGMENT
  cmdsize 192
  segname __IMPORT
   vmaddr 0x00003000
   vmsize 0x00001000
  fileoff 8192
 filesize 4096
  maxprot rwx
 initprot rwx
   nsects 2
    flags (none)
Section
  sectname __pointers
   segname __IMPORT
      addr 0x00003000
      size 0x0000000c
    offset 8192
     align 2^0 (1)
    reloff 0
    nreloc 0
      type S_NON_LAZY_SYMBOL_POINTERS
attributes (none)
 reserved1 0 (index into indirect symbol table)
 reserved2 0
Section
  sectname __jump_table
   segname __IMPORT
      addr 0x0000300c
      size 0x00000014
    offset 8204
     align 2^0 (1)
    reloff 0
    nreloc 0
      type S_SYMBOL_STUBS
attributes PURE_INSTRUCTIONS SELF_MODIFYING_CODE SOME_INSTRUCTIONS
 reserved1 3 (index into indirect symbol table)
 reserved2 5 (size of stubs)
Load command 4
      cmd LC_SEGMENT
  cmdsize 56
  segname __LINKEDIT
   vmaddr 0x00004000
   vmsize 0x00001000
  fileoff 12288
 filesize 1052
  maxprot rw-
 initprot r--
   nsects 0
    flags NORELOC
Load command 5
          cmd LC_LOAD_DYLINKER
      cmdsize 28
         name /usr/lib/dyld (offset 12)
Load command 6
          cmd LC_LOAD_DYLIB
      cmdsize 52
         name /usr/lib/libSystem.B.dylib (offset 24)
   time stamp 1167494134 Sun Dec 31 00:55:34 2006
      current version 88.3.4
compatibility version 1.0.0
Load command 7
     cmd LC_SYMTAB
 cmdsize 24
  symoff 12288
   nsyms 31
  stroff 12716
 strsize 624
Load command 8
            cmd LC_DYSYMTAB
        cmdsize 80
      ilocalsym 0
      nlocalsym 4
     iextdefsym 4
     nextdefsym 20
      iundefsym 24
      nundefsym 7
         tocoff 0
           ntoc 0
      modtaboff 0
        nmodtab 0
   extrefsymoff 0
    nextrefsyms 0
 indirectsymoff 12688
  nindirectsyms 7
      extreloff 0
        nextrel 0
      locreloff 0
        nlocrel 0
Load command 9
     cmd LC_TWOLEVEL_HINTS
 cmdsize 16
  offset 12660
  nhints 7
Load command 10
        cmd LC_UNIXTHREAD
    cmdsize 80
     flavor i386_THREAD_STATE
      count i386_THREAD_STATE_COUNT
            eax 0x00000000 ebx    0x00000000 ecx 0x00000000 edx 0x00000000
            edi 0x00000000 esi    0x00000000 ebp 0x00000000 esp 0x00000000
            ss  0x0000001f eflags 0x00000000 eip 0x00001e60 cs  0x00000017
            ds  0x0000001f es     0x0000001f fs  0x00000000 gs  0x00000000

__TEXTセグメント

otoolの-tオプションで、__TEXTセグメントの__textセクションを表示してみる。-Vオプションで逆アセンブルすることが可能。

> otool -t -v -V test     
test:
(__TEXT,__text) section
start:
00001e60        pushl   $0x00
00001e62        movl    %esp,%ebp
00001e64        andl    $0xf0,%esp
00001e67        subl    $0x10,%esp
00001e6a        movl    0x04(%ebp),%ebx
00001e6d        movl    %ebx,0x00(%esp,1)
00001e71        leal    0x08(%ebp),%ecx
00001e74        movl    %ecx,0x04(%esp,1)
00001e78        addl    $0x01,%ebx
00001e7b        shll    $0x02,%ebx
00001e7e        addl    %ecx,%ebx
00001e80        movl    %ebx,0x08(%esp,1)
00001e84        calll   __start
00001e89        hlt
__start:
00001e8a        pushl   %ebp
00001e8b        movl    %esp,%ebp
00001e8d        pushl   %edi
00001e8e        pushl   %esi
00001e8f        pushl   %ebx
00001e90        subl    $0x2c,%esp
00001e93        movl    0x0c(%ebp),%edi
00001e96        movl    0x10(%ebp),%ebx
00001e99        movl    0x08(%ebp),%eax
00001e9c        movl    %eax,_NXArgc
00001ea1        movl    %edi,_NXArgv
00001ea7        movl    %ebx,_environ
00001ead        movl    (%edi),%ecx
00001eaf        testl   %ecx,%ecx
00001eb1        jne     0x00001eba
00001eb3        movl    $0x00001fa4,%ecx
00001eb8        jmp     0x00001ed3
00001eba        movl    %ecx,%edx
00001ebc        jmp     0x00001ecc
00001ebe        cmpb    $0x2f,%al
00001ec0        je      0x00001ec7
00001ec2        addl    $0x01,%edx
00001ec5        jmp     0x00001ecc
00001ec7        addl    $0x01,%edx
00001eca        movl    %edx,%ecx
00001ecc        movzbl  (%edx),%eax
00001ecf        testb   %al,%al
00001ed1        jne     0x00001ebe
00001ed3        movl    %ecx,___progname
00001ed9        movl    %ebx,%eax
00001edb        jmp     0x00001ee0
00001edd        addl    $0x04,%eax
00001ee0        movl    (%eax),%edx
00001ee2        testl   %edx,%edx
00001ee4        jne     0x00001edd
00001ee6        leal    0x04(%eax),%esi
00001ee9        movl    0x00003000,%eax
00001eee        movl    (%eax),%eax
00001ef0        testl   %eax,%eax
00001ef2        je      0x00001ef6
00001ef4        call    *%eax
00001ef6        movl    0x00003008,%eax
00001efb        movl    (%eax),%eax
00001efd        testl   %eax,%eax
00001eff        je      0x00001f03
00001f01        call    *%eax
00001f03        calll   0x00003011      ; symbol stub for: ___keymgr_dwarf2_register_sections
00001f08        leal    0xe0(%ebp),%eax
00001f0b        movl    %eax,0x04(%esp,1)
00001f0f        movl    $0x00001fa8,(%esp,1)
00001f16        calll   __dyld_func_lookup
00001f1b        call    *0xe0(%ebp)
00001f1e        leal    0xe4(%ebp),%eax
00001f21        movl    %eax,0x04(%esp,1)
00001f25        movl    $0x00001fd8,(%esp,1)
00001f2c        calll   __dyld_func_lookup
00001f31        movl    0xe4(%ebp),%eax
00001f34        testl   %eax,%eax
00001f36        je      0x00001f40
00001f38        movl    %eax,(%esp,1)
00001f3b        calll   0x00003016      ; symbol stub for: _atexit
00001f40        movl    0x00003004,%eax
00001f45        movl    $0x00000000,(%eax)
00001f4b        movl    %esi,0x0c(%esp,1)
00001f4f        movl    %ebx,0x08(%esp,1)
00001f53        movl    %edi,0x04(%esp,1)
00001f57        movl    0x08(%ebp),%eax
00001f5a        movl    %eax,(%esp,1)
00001f5d        calll   _main
00001f62        movl    %eax,(%esp,1)
00001f65        calll   0x0000300c      ; symbol stub for: _exit
00001f6a        nop
00001f6b        nop
dyld_stub_binding_helper:
00001f6c        pushl   $__mh_execute_header
00001f71        jmp     *0x00002010
00001f77        nop
__dyld_func_lookup:
00001f78        jmp     *0x00002014
_main:
00001f7e        pushl   %ebp
00001f7f        movl    %esp,%ebp
00001f81        pushl   %ebx
00001f82        subl    $0x14,%esp
00001f85        calll   ___i686.get_pc_thunk.bx
00001f8a        leal    0x00000066(%ebx),%eax
00001f90        movl    %eax,(%esp,1)
00001f93        calll   0x0000301b      ; symbol stub for: _puts
00001f98        movl    $0x00000000,%eax
00001f9d        addl    $0x14,%esp
00001fa0        popl    %ebx
00001fa1        popl    %ebp
00001fa2        ret

ロードコマンドによると、__TEXTセグメントにはまだ__cstringと__textcoal_ntのセクションがあるから、それも表示してみる。otoolの-sオプションを使って、セグメントとセクションを指定すれば、任意のセクションを表示できる。-vオプションで多少見やすくなる。
__cstringには文字列が、__textcoal_ntセクションにはgccが用意した__i686.get_pc_thunk.bx関数が格納されている。

> otool -s __TEXT __cstring -v test    
test:
Contents of (__TEXT,__cstring) section
00001fa4  
00001fa5  
00001fa6  
00001fa7  
00001fa8  __dyld_make_delayed_module_initializer_calls
00001fd5  
00001fd6  
00001fd7  
00001fd8  __dyld_mod_term_funcs
00001fee  
00001fef  
00001ff0  Hello World
> otool -s __TEXT __textcoal_nt -v test
test:
Contents of (__TEXT,__textcoal_nt) section
___i686.get_pc_thunk.bx:
00001ffc        movl    (%esp,1),%ebx
00001fff        ret