标签:
1. Otool简介
Otool可以提取并显示ios下目标文件的相关信息,包括头部,加载命令,各个段,共享库,动态库等等。它拥有大量的命令选项,是一个功能强大的分析工具,当然还可以做反汇编的工具使用。
2. Mach-o基本结构
Mach-o包含三个基本区域:
3. 头部结构
Mach-o的头部帮助内核迅速确定当前文件所支持的CPU架构。它位于Mach-o文件的开始部分。
可以用otool –h XX来获取某文件的header structure。
Otool –h ButtonFun;
ButtonFun (architecture armv6):
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
0xfeedface 12 6 0×00 2 19 2404 0×00000085
ButtonFun (architecture armv7):
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
0xfeedface 12 9 0×00 2 19 2404 0×00200085
从上面的输出可以看到header部分的字段,下面来看一下头部结构的定义和意义:
[codesyntax lang="c" lines="normal"]
struct mach_header { uint32_t magic; /* mach magic number identifier */ cpu_type_t cputype; /* cpu specifier */ cpu_subtype_t cpusubtype; /* machine specifier */ uint32_t filetype; /* type of file */ uint32_t ncmds; /* number of load commands */ uint32_t sizeofcmds; /* the size of all the load commands */ uint32_t flags; /* flags */ };
[/codesyntax] 64位对应的定义(下文中不再追述64位下的定义):
[codesyntax lang="c" lines="fancy"]
struct mach_header_64 { uint32_t magic; /* mach magic number identifier */ cpu_type_t cputype; /* cpu specifier */ cpu_subtype_t cpusubtype; /* machine specifier */ uint32_t filetype; /* type of file */ uint32_t ncmds; /* number of load commands */ uint32_t sizeofcmds; /* the size of all the load commands */ uint32_t flags; /* flags */ uint32_t reserved; /* reserved */ };
[/codesyntax]Magic字段,用于标明当前文件是32位还是64位的常量。
32位通常定义如下:
[codesyntax lang="c" lines="fancy"]
#define MH_MAGIC 0xfeedface /* the mach magic number */
[/codesyntax] 64位通常定义如下:
[codesyntax lang="c" lines="fancy"]
#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */
[/codesyntax]Cputype段用于只是当前文件的目标架构,即当前代码只能在指定CPU上运行。
[codesyntax lang="c" lines="fancy"]
#define CPU_TYPE_ARM ((cpu_type_t) 12)
[/codesyntax] Cputype在/usr/include/mach/machine.h中定义;
4. 加载命令
头部之后就是加载命令。加载命令的数目以及总的大小在header中已经给出。加载命令标识文件的布局以及链接相关的信息,主要包含:
利用命令otool –l XX 可以获取加载命令区。
Otool –l ButtonFun;所得加载命令如下(截取一小部分,完整版见最后的附件):
ButtonFun:
Load command 0
cmd LC_SEGMENT
cmdsize 56
segname __PAGEZERO
vmaddr 0×00000000
vmsize 0×00001000
fileoff 0
filesize 0
maxprot 0×00000000
initprot 0×00000000
nsects 0
flags 0×0
Load command 1
cmd LC_SEGMENT
cmdsize 464
segname __TEXT
vmaddr 0×00001000
vmsize 0×00002000
fileoff 0
filesize 8192
maxprot 0×00000007
initprot 0×00000005
nsects 6
flags 0×0
先看一下加载命令定义:
[codesyntax lang="c" lines="fancy"]
struct load_command { uint32_t cmd; /* type of load command */ uint32_t cmdsize; /* total size of command in bytes */ };
[/codesyntax]cmd是一个字符串常量,用来指示命令类型。对于每一种命令类型都有一个专有的结构体。具体的加载命令类型可以参考/usr/include/mach-o/loader.h
当加载命令的类型为LC_SEGMENT时(segment load command),意味着这部分文件需要映射到进程的地址空间中去。其对应的结构体如下:
[codesyntax lang="c" lines="fancy"]
struct segment_command { /* for 32-bit architectures */ uint32_t cmd; /* LC_SEGMENT */ uint32_t cmdsize; /* includes sizeof section structs */ char segname[16]; /* segment name */ uint32_t vmaddr; /* memory address of this segment */ uint32_t vmsize; /* memory size of this segment */ uint32_t fileoff; /* file offset of this segment */ uint32_t filesize; /* amount to map from the file */ vm_prot_t maxprot; /* maximum VM protection */ vm_prot_t initprot; /* initial VM protection */ uint32_t nsects; /* number of sections in segment */ uint32_t flags; /* flags */ };
[/codesyntax]cmd字段 类型为LC_SEGMENT;
文件映射的起始位置是由fileoff给出,映射到地址空间的vmaddr处。
vmsize等于或者大于filesize。
看到上面两个定义,我们在回头看截取的load command 0,发现:
如果当前段(segment)包含section,那么section structure紧跟在segment command之后,section所占的字节数由当前段的cmdsize字段给出。
Section的定义如下:
[codesyntax lang="c" lines="fancy"]
struct section { /* for 32-bit architectures */ char sectname[16]; /* name of this section */ char segname[16]; /* segment this section goes in */ uint32_t addr; /* memory address of this section */ uint32_t size; /* size in bytes of this section */ uint32_t offset; /* file offset of this section */ uint32_t align; /* section alignment (power of 2) */ uint32_t reloff; /* file offset of relocation entries */ uint32_t nreloc; /* number of relocation entries */ uint32_t flags; /* flags (section type and attributes)*/ uint32_t reserved1; /* reserved (for offset or index) */ uint32_t reserved2; /* reserved (for count or sizeof) */ };
[/codesyntax]flag字段 section的flag字段分为两个部分,一个是区域类型(section type),一个是区域属性(section attributes)。其中type是互斥的,即只能有一个类型,而attributes不是互斥的,可以有多个属性。如果段(segment)中的任何一个section拥有属性S_ATTR_DEBUG,那么该段所有的section都必须拥有这个属性。具体的flag字段内容以及意义请参考/usr/include/mach-o/loader.h。
下面是__TEXT段的部分截取:
Load command 1
cmd LC_SEGMENT
cmdsize 464
segname __TEXT
vmaddr 0×00001000
vmsize 0×00002000
fileoff 0
filesize 8192
maxprot 0×00000007
initprot 0×00000005
nsects 6
flags 0×0
Section
sectname __text
segname __TEXT
addr 0x00001b50
size 0x000008f6
offset 2896
align 2^4 (16)
reloff 0
nreloc 0
flags 0×80000400
reserved1 0
reserved2 0
Load command中根据类型(即cmd字段)的不同,分别定义了相关的结构体,具体请参详/usr/include/mach-o/loader.h。
5. 段(segment和section)
段的命名规则是两个下划线紧跟着大写字母(如__TEXT),而section的命名则是两个下划线紧跟着小写字母(__text)。
下面列出段中可能包含的section:
__text, __cstring, __picsymbol_stub, __symbol_stub, __const, __litera14, __litera18;
__data, __la_symbol_ptr, __nl_symbol_ptr, __dyld, __const, __mod_init_func, __mod_term_func, __bss, __commom;
__jump_table, __pointers;
其中__TEXT段中的__text是实际上的代码部分;__DATA段的__data是实际的初始数据。
可以通过otool –s查看某segment的某个section。
例如 otool –sv __DATA __data ButtonFun,得到如下文件:
ButtonFun:
(__DATA,__data) section
00003648 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00003658 00 00 00 00 8d 29 00 00 84 32 00 00 00 00 00 00
00003668 00 00 00 00 80 31 00 00 00 00 00 00 00 00 00 00
00003678 28 00 00 00 00 00 00 00 00 00 00 00 0e 2b 00 00
00003688 00 00 00 00 8c 32 00 00 00 00 00 00 00 00 00 00
00003698 00 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00
000036a8 00 00 00 00 48 2d 00 00 b8 34 00 00 00 00 00 00
000036b8 00 00 00 00 68 34 00 00 00 00 00 00 00 00 00 00
000036c8 28 00 00 00 00 00 00 00
可以通过otool –t直接查看代码段(__TEXT)的反汇编代码,也可以通过otool –d查看数据段内容,例如:Otool –tv ButtonFun得到如下内容(截取部分):
ButtonFun:
(__TEXT,__text) section
start:
00001b50 pushl $0×00
00001b52 movl %esp,%ebp
00001b54 andl $0xf0,%esp
00001b57 subl $0×10,%esp
00001b5a movl 0×04(%ebp),%ebx
00001b5d movl %ebx,(%esp)
00001b60 leal 0×08(%ebp),%ecx
00001b63 movl %ecx,0×04(%esp)
00001b67 addl $0×01,%ebx
00001b6a shll $0×02,%ebx
00001b6d addl %ecx,%ebx
00001b6f movl %ebx,0×08(%esp)
00001b73 movl (%ebx),%eax
00001b75 addl $0×04,%ebx
00001b78 testl %eax,%eax
00001b7a jne 0x00001b73
00001b7c movl %ebx,0x0c(%esp)
00001b80 calll 0x00001b90
00001b85 movl %eax,(%esp)
00001b88 calll 0x0000244c
00001b8d hlt
00001b8e nop
00001b8f nop
参考资料:
http://blog.163.com/iswing@126/blog/static/166700480201129102259978/
http://www.mc2lab.com/?p=68
otool介绍(转http://www.mc2lab.com/?p=68)
标签:
原文地址:http://www.cnblogs.com/sonofelice/p/5571416.html