1什么是macho
2 macho总体描述
3以查询一个类名为例,说明查找过程
1什么是macho
macho是mac os, ios可执行文件格式,类似windows上的pe和linux上的elf。macho是源码通过编译连接产生。
2 macho总体描述
先用文本打开,来个感官认识下,如下图:
macho也是文件,只不过组织结构文本查看器不能解析。
下面整体看下macho的结构。
文件的开始32个Byte是头部,头部包含的信息中包含要加载的指令条数。
接下来是要加载的指令。
这些指令以段(Segment)分为多个。
有的段又分为多个区(Section)。
上面的Section只是索引,包含该section在整个文件中的位置和大小,如果要查看section具体的数据,就要通过偏移位置和大小去查找。
下面是基本结构图:
接下来具体讲解下每部分的含义。
要参考loader.h(/usr/include/mach-o/loader.h或者https://opensource.apple.com/source/xnu/xnu-1456.1.26/EXTERNAL_HEADERS/mach-o/loader.h)
前32Byte可以转换为一个结构体表示。结构体的定义如下。
/*
* The 32-bit mach header appears at the very beginning of the object file for
* 32-bit architectures.
*/
struct mach_header {
uint32_tmagic;/* mach magic number identifier */
cpu_type_tcputype;/* cpu specifier */
cpu_subtype_tcpusubtype;/* machine specifier */
uint32_tfiletype;/* type of file */
uint32_tncmds;/* number of load commands */
uint32_tsizeofcmds;/* the size of all the load commands */
uint32_tflags;/* flags */
};
/*
* The 64-bit mach header appears at the very beginning of object files for
* 64-bit architectures.
*/
struct mach_header_64 {
uint32_tmagic;/* mach magic number identifier */
cpu_type_tcputype;/* cpu specifier */
cpu_subtype_tcpusubtype;/* machine specifier */
uint32_tfiletype;/* type of file */
uint32_tncmds;/* number of load commands */
uint32_tsizeofcmds;/* the size of all the load commands */
uint32_tflags;/* flags */
uint32_treserved;/* reserved */
};
2.1.1魔数标识macho文件是多少位的,是不是fat格式(多个架构合并),是不是小端序
前4byte为魔数,根据魔数可以判断macho是32位还是64位,还记录着是大端序或小端序
#defineMH_MAGIC0xfeedface/* the mach magic number */是32位
#defineMH_CIGAM0xcefaedfe/* NXSwapInt(MH_MAGIC) */是32位,解析需要转换成小端序
#defineMH_MAGIC_640xfeedfacf/* the 64-bit mach magic number */是64位
#defineMH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */是64位解析需要转换成小端序
其中0xCAFEBABE为多个架构的mach-o
2.1.2 cpu类型,标识该macho需要在哪些类型的cpu上运行
cputype如下,
cputype和cpusubtype具体含义查看头文件machine.h
2.1.3文件类型filetype标识文件的类型,是可执行文件还是其他类型的文件
部分类型说明如下:详细的说明参考文件loader.h
2.1.4 flags标识macho的一些特性,比如是否包含未定义的引用,如果是镜像是否有二级命名空间绑定。
部分定义见下面,详细的定义参考loader.h
2.1.5加载命令的信息ncmds和sizeofcmds,依据这些信息来解析加载命令
ncmds是加载命令的个数。sizeofcmds是加载命令索引的大小。
2.2解析加载命令
2.2.1每一个加载命令至少还有两个元素,第一个是类型,第二个是大小
具体下面结构体
struct load_command {
uint32_t cmd;/* type of load command */
uint32_t cmdsize;/* total size of command in bytes */
};
cmd,加载命令的类型定义如下(其中动态库的定义看文件loader.h)
/* Constants for the cmd field of all load commands, the type */
#defineLC_SEGMENT0x1/* segment of this file to be mapped */
#defineLC_SYMTAB0x2/* link-edit stab symbol table info */
#defineLC_SYMSEG0x3/* link-edit gdb symbol table info (obsolete) */
#defineLC_THREAD0x4/* thread */
#defineLC_UNIXTHREAD0x5/* unix thread (includes a stack) */
#defineLC_LOADFVMLIB0x6/* load a specified fixed VM shared library */
#defineLC_IDFVMLIB0x7/* fixed VM shared library identification */
#defineLC_IDENT0x8/* object identification info (obsolete) */
#define LC_FVMFILE0x9/* fixed VM file inclusion (internal use) */
#define LC_PREPAGE0xa/* prepage command (internal use) */
#defineLC_DYSYMTAB0xb/* dynamic link-edit symbol table info */
#defineLC_LOAD_DYLIB0xc/* load a dynamically linked shared library */
#defineLC_ID_DYLIB0xd/* dynamically linked shared lib ident */
#define LC_LOAD_DYLINKER 0xe/* load a dynamic linker */
#define LC_ID_DYLINKER0xf/* dynamic linker identification */
#defineLC_PREBOUND_DYLIB 0x10/* modules prebound for a dynamically */
/*linked shared library */
#defineLC_ROUTINES0x11/* image routines */
#defineLC_SUB_FRAMEWORK 0x12/* sub framework */
#defineLC_SUB_UMBRELLA 0x13/* sub umbrella */
#defineLC_SUB_CLIENT0x14/* sub client */
#defineLC_SUB_LIBRARY0x15/* sub library */
#defineLC_TWOLEVEL_HINTS 0x16/* two-level namespace lookup hints */
#defineLC_PREBIND_CKSUM0x17/* prebind checksum */
cmdsize包含改命令中的所有内容的大小,比如segment中的section。
其中常见的加载命令解释看下图:
2.2.2
解析一个
segment
Segment的定义看下面的结构体
struct segment_command { /* for 32-bit architectures */
uint32_tcmd;/* LC_SEGMENT */
uint32_tcmdsize;/* includes sizeof section structs */
charsegname[16];/* segment name */
uint32_tvmaddr;/* memory address of this segment */
uint32_tvmsize;/* memory size of this segment */
uint32_tfileoff;/* file offset of this segment */
uint32_tfilesize;/* amount to map from the file */
vm_prot_tmaxprot;/* maximum VM protection */
vm_prot_tinitprot;/* initial VM protection */
uint32_tnsects;/* number of sections in segment */
uint32_tflags;/* flags */
};
/*
* The 64-bit segment load command indicates that a part of this file is to be
* mapped into a 64-bit task's address space.If the 64-bit segment has
* sections then section_64 structures directly follow the 64-bit segment
* command and their size is reflected in cmdsize.
*/
struct segment_command_64 { /* for 64-bit architectures */
uint32_tcmd;/* LC_SEGMENT_64 */
uint32_tcmdsize;/* includes sizeof section_64 structs */
charsegname[16];/* segment name */
uint64_tvmaddr;/* memory address of this segment */
uint64_tvmsize;/* memory size of this segment */
uint64_tfileoff;/* file offset of this segment */
uint64_tfilesize;/* amount to map from the file */
vm_prot_tmaxprot;/* maximum VM protection */
vm_prot_tinitprot;/* initial VM protection */
uint32_tnsects;/* number of sections in segment */
uint32_tflags;/* flags */
};
segname:是该段的名字,占用16个byte。
vmaddr:该段所在内存的开始虚拟地址。64位虚拟地址的起始值是0x100000000,32位0x4000
fileoff:该段从macho文件开始位置的偏移值,从0开始计算
vmsize:该段占用虚拟内存的大小
filesize:该段占用文件中的大小,和vmsize相等。
nsects:该段中包含的区的个数。
2.2.3解析一个区
其中一个区的定义如下:
struct section { /* for 32-bit architectures */
charsectname[16];/* name of this section */
charsegname[16];/* segment this section goes in */
uint32_taddr;/* memory address of this section */
uint32_tsize;/* size in bytes of this section */
uint32_toffset;/* file offset of this section */
uint32_talign;/* section alignment (power of 2) */
uint32_treloff;/* file offset of relocation entries */
uint32_tnreloc;/* number of relocation entries */
uint32_tflags;/* flags (section type and attributes)*/
uint32_treserved1;/* reserved (for offset or index) */
uint32_treserved2;/* reserved (for count or sizeof) */
};
struct section_64 { /* for 64-bit architectures */
charsectname[16];/* name of this section */
charsegname[16];/* segment this section goes in */
uint64_taddr;/* memory address of this section */
uint64_tsize;/* size in bytes of this section */
uint32_toffset;/* file offset of this section */
uint32_talign;/* section alignment (power of 2) */
uint32_treloff;/* file offset of relocation entries */
uint32_tnreloc;/* number of relocation entries */
uint32_tflags;/* flags (section type and attributes)*/
uint32_treserved1;/* reserved (for offset or index) */
uint32_treserved2;/* reserved (for count or sizeof) */
uint32_treserved3;/* reserved */
};
flag标识section的一些特性,部分定义看下面
#define S_NON_LAZY_SYMBOL_POINTERS 0x6/* section with only non-lazy symbol pointers */
#define S_LAZY_SYMBOL_POINTERS 0x7/* section with only lazy symbolpointers */
#define S_SYMBOL_STUBS0x8/* section with only symbol stubs, byte size of stub in
the reserved2 field */
#define S_MOD_INIT_FUNC_POINTERS 0x9 /* section with only function pointers for
initialization*/
#define S_MOD_TERM_FUNC_POINTERS0xa/* section with only function pointers for termination*/
#define S_COALESCED0xb/* section contains symbols that are to be coalesced */
#define S_GB_ZEROFILL0xc/* zero fill on demand section
常见的区的解释见下图
3
解析类名
需要用的工具有MachoViewer和Hopper
用machoViewer打开macho文件。
如下图:
找到Data Segment中的_objc_classlist对应的section
记录下对应的Address
用Hopper打开macho文件
键盘输入大写字母G(跳到指定地址),或者选择Navigate-> Go To Address or Symbol进入挑战页面。
输入_objc_classlist的地址。
如下图
点击Go按钮跳到_objc_classlist的地址
上图显示有三个类,AppDelegate, MasterViewController, DetailViewController
但是从macho文件来解析,这是三个地址,这三个类名是Hopper解析号之后加到显示的注释上了。
选择查看原件,红框里面就是MasterViewController对应的地址
双击_OBJC_CLASS_$_MasterViewController,可以跳转到MasterViewController的引用处。
如下图:
双击MasterViewController_data可以跳转的MasterViewController的详细信息处。(同样这里MasterViewController_data也只是地址)
跳转之后如下图:
其中有一相是类名,这里是地址,双击可以到具体类名的地方。
跳转之后如下图:
到此查找类名结束。