1. Otool简介

Otool可以提取并显示ios下目标文件的相关信息,包括头部,加载命令,各个段,共享库,动态库等等。它拥有大量的命令选项,是一个功能强大的分析工具,当然还可以做反汇编的工具使用。

2. Mach-o基本结构

Mach-o包含三个基本区域:

  • 头部(header structure)。
  • 加载命令(load command)。
  • 段(segment)。可以拥有多个段(segment),每个段可以拥有零个或多个区域(section)。每一个段(segment)都拥有一段虚拟地址映射到进程的地址空间。
  • 链接信息。一个完整的用户级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中定义;

  • Cpusubtype指定CPU确切的模型。其定义同样参见/usr/include/mach/machine.h;
  • Filetype字段用于标识文件的布局包括对齐方式等。在/usr/include/mach-o/loader.h中定义了filetype字段的常量。
  • Ncmds和sizeofcmds字段,加载命令的数目和大小。
  • Flag字段。

4. 加载命令

头部之后就是加载命令。加载命令的数目以及总的大小在header中已经给出。加载命令标识文件的布局以及链接相关的信息,主要包含:

  • 虚拟地址的出师布局,包括某段(segment)某区域(section)的起始地址以及大小;
  • 用于动态链接的符号表;
  • 主线程的初始化状态;
  • 共享库。

利用命令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

  • cmdsize是加载命令的字节数。

当加载命令的类型为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;

  • cmdsize字段 当前段(segment)所包含的sections的字节数。
  • flag字段 SG_HIGHVM,SG_FVMLIB,SG_NORELOC,SG_PROTECTED_VERSION_1;具体解释参照/usr/include/mach-o/loader.h

文件映射的起始位置是由fileoff给出,映射到地址空间的vmaddr处。

vmsize等于或者大于filesize。

看到上面两个定义,我们在回头看截取的load command 0,发现:

  • 类型为LC_SEGMENT,故完全符合segment_command的定义
  • 在截取信息中,我们可以清晰的看到命令字节数,虚拟地址起始位置,所占空间等信息。

如果当前段(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。

  • 段名(segname)和区域名(sectname)本身没有意义。但是为了支持传统的UNIX系统所以使用了一些传统意义上的段名和区域名称,比如TEXT段,DATA段。

下面是__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

  • Cmd为LC_SEGMENT,所以前面是segment_command结构体,紧跟着就是当前段所拥有的section,这里之截取了一个section
  • __TEXT段的__text section,参考section的定义,可以很清除的明白个字段的意义。

Load command中根据类型(即cmd字段)的不同,分别定义了相关的结构体,具体请参详/usr/include/mach-o/loader.h。

5. 段(segment和section)

段的命名规则是两个下划线紧跟着大写字母(如__TEXT),而section的命名则是两个下划线紧跟着小写字母(__text)。

下面列出段中可能包含的section:

  • __TEXT段:

__text, __cstring, __picsymbol_stub, __symbol_stub, __const, __litera14, __litera18;

  • __DATA段

__data, __la_symbol_ptr, __nl_symbol_ptr, __dyld, __const, __mod_init_func, __mod_term_func, __bss, __commom;

  • __IMPORT段

__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://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html

http://www.mc2lab.com/?p=68

otool介绍(转http://www.mc2lab.com/?p=68)的更多相关文章

  1. mach-o格式分析

    0x00 摘要 人生无根蒂,飘如陌上尘. 分散逐风转,此已非常身. — 陶渊明 <杂诗> mach-o格式是OS X系统上的可执行文件格式,类似于windows的PE与linux的ELF, ...

  2. 最新Java技术

    最近在网上查资料碰到好多没接触过的技术,先汇总在这里备用,以后慢慢吸收 1. JNA JNI的替代品,调用方式比JNI更直接,不再需要JNI那层中间接口,几乎达到Java直接调用动态库 2. Smal ...

  3. mongodb副本集群搭建

    一.环境介绍 1.机器信息 10.40.6.68 10.40.6.108 10.40.6.110 软件环境为centos 6.x 2.mongodb 下载链接地址 https://www.mongod ...

  4. CSS3 background-image背景图片相关介绍

    这里将会介绍如何通过background-image设置背景图片,以及背景图片的平铺.拉伸.偏移.设置大小等操作. 1. 背景图片样式分类 CSS中设置元素背景图片及其背景图片样式的属性主要以下几个: ...

  5. MySQL高级知识- MySQL的架构介绍

    [TOC] 1.MySQL 简介 概述 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司. MySQL是一种关联数据库管理系统,将数据保存在不同的表中,而 ...

  6. Windows Server 2012 NIC Teaming介绍及注意事项

    Windows Server 2012 NIC Teaming介绍及注意事项 转载自:http://www.it165.net/os/html/201303/4799.html Windows Ser ...

  7. Linux下服务器端开发流程及相关工具介绍(C++)

    去年刚毕业来公司后,做为新人,发现很多东西都没有文档,各种工具和地址都是口口相传的,而且很多时候都是不知道有哪些工具可以使用,所以当时就想把自己接触到的这些东西记录下来,为后来者提供参考,相当于一个路 ...

  8. JavaScript var关键字、变量的状态、异常处理、命名规范等介绍

    本篇主要介绍var关键字.变量的undefined和null状态.异常处理.命名规范. 目录 1. var 关键字:介绍var关键字的使用. 2. 变量的状态:介绍变量的未定义.已定义未赋值.已定义已 ...

  9. HTML DOM 介绍

    本篇主要介绍DOM内容.DOM 节点.节点属性以及获取HTML元素的方法. 目录 1. 介绍 DOM:介绍DOM,以及对DOM分类和功能的说明. 2. DOM 节点:介绍DOM节点分类和节点层次. 3 ...

随机推荐

  1. Hadoop权威指南:HDFS-目录,查询文件系统,删除文件

    Hadoop权威指南:HDFS-目录,查询文件系统,删除文件 [TOC] 目录 FileSystem实例提供了创建目录的方法 public boolean mkdirs(Path f) throws ...

  2. 一个基于POI的通用excel导入导出工具类的简单实现及使用方法

    前言: 最近PM来了一个需求,简单来说就是在录入数据时一条一条插入到系统显得非常麻烦,让我实现一个直接通过excel导入的方法一次性录入所有数据.网上关于excel导入导出的例子很多,但大多相互借鉴. ...

  3. QT编程环境搭建

    使用QT需要QT的库以及QT creator,在QT5以后的版本中,两者已经集成,不需要单独下载了,只需要下载一个文件即可.配置步骤如下: 1.下载qt-opensource-windows-x86- ...

  4. html中如何修改选中 用input做的搜索框 的边框颜色

    html中如何修改选中 用input做的搜索框 的边框颜色 如图,当我鼠标选中输入框时,内边框会变成蓝色 我的问题是: 1.如何把蓝色去掉? 2.如何改成别的颜色? 首先感谢 UI设计师提出的需求,解 ...

  5. BZOJ 3412: [Usaco2009 Dec]Music Notes乐谱(离线处理)

    这道题貌似怎么写都可以吧= =,我先读入询问然后从小到大处理就行了= = PS:水水题真的好!无!聊!但是好!欢!乐! CODE: #include<cstdio>#include< ...

  6. Linux less命令详解

    less 在Linux下查看文件内容的命令大致有以下几种: cat 由第一行开始显示内容,并将所有内容输出 tac 从最后一行倒序显示内容,并将所有内容输出 more 根据窗口大小,一页一页的现实文件 ...

  7. 为 instance 配置静态 IP - 每天5分钟玩转 OpenStack(157)

    这是 OpenStack 实施经验分享系列的第 7 篇. 传统运维中为服务器配置静态 IP 是再常见不过的了.但在 OpenStack 环境下只能指定 network,IP 都是 Neutron 从 ...

  8. angular drag and drop (ngDraggable) 笔记

    这是原文 https://github.com/fatlinesofcode/ngDraggable 这是另一个dnd,这比较灵活,可以监听事件.我只用简单的排序功能,其他没去了解太多.有机会遇到功能 ...

  9. 每天一个linux命令(32)--/etc/group文件详解

    Linux /etc/group 文件与 /etc/passwd 和/etc/shadow 文件都是有关于系统管理员对用户和用户组管理时相关的文件.Linux /etc/group 文件是有关于系统管 ...

  10. rem与em

    最近有朋友在进行rem布局的时候总搞不懂rem  em  px  与百分比布局的区别在哪里  这里简单给大家介绍一下 Em为单位: 这种技术需要一个参考点,一般都是以<body>的&quo ...