iOS-逆向12-MachO文件

《iOS底层原理文章汇总》
上一篇文章iOS-逆向11-代码注入得知,要想动态注入,必须要修改MachO文件,通过工具yololib使其中能增加一行自定义的动态库的路径,才能动态注入自己的代码,从而达到hook的目的,MachO文件的结构是什么样的呢?

图片.png

图片.png

1.MachO文件

Mach-O其实是Mach Object文件格式的缩写,是mac以及iOS上可执行文件的格式, 类似于windows上的PE格式 (Portable Executable ), linux上的elf格式 (Executable and Linking Format)

2.MachO文件格式

Mach-O为Mach object文件格式的缩写,它是一种用于可执行文件、目标代码、动态库的文件格式。作为a.out格式的替代,Mach-O提供了更强的扩展性。
属于MachO格式的常见文件
目标文件.o
库文件
.a
.dylib
Framework
可执行文件
dyld
.dsym
File指令
通过 $file 文件路径 查看文件类型

3.目标文件.o

I.单个c文件通过clang(llvm的前端)编译为.o文件

  • 打开Xcode->Cmd+N新建test.c文件,编写main函数


    图片.png
#include <stdio.h>
int main(){
    printf("test\n");
    return 0;
}
  • 通过clang -o test.c生成test.o文件,通过file指令查看test.o的文件结构属于Mach-O文件,架构为x86-64位处理器


    图片.png
  • 再次执行clang test.o将.o文件编译为可执行文a.out,是可执行文件executable,不再是object文件


    image
  • 也可以通过clang -o test2 test.c一次性将test.c编译生成可执行文件
    也可以通过clang -o test3 test.o将.o文件编译生成可执行文件
    a.out、test2、test3属于同一个文件,哈希值相等只是文件名不相同,改后缀名后哈希值仍然相等


    image

    源文件到可执行文件的中间产物是.o文件

II.项目中会存在多个.c文件,两个.c文件,test.c和test1.c文件,test.c中会调用test1.c中的方法

执行clang -o demo test1.c test.c
./demo输入
test
test1

//  test.c
#include <stdio.h>
void test1();
int main(){
    printf("test\n");
    test1();
    return 0;
}
//  test1.c
#include <stdio.h>
void test1(){
    printf("test1\n");
}

图片.png

执行clang -c test1.c test.c生成.o文件,执行clang -o demo1 test.o test1.o生成demo1可执行文件
但两次文件不一样,因为编译链接顺序发生变化,一次test1.c在前,一次test1.c在前,生成的可执行文件哈希值不相同
图片.png

通过objdump --macho -d demo2查看可执行文件顺序,发现demo2(和demo相同)与demo1的文件顺序不一致,准确的说是text段不一致,demo2中_main函数在前,_test1函数在后,demo中_main函数在后,_test1函数在前
image

demo2如下
image

demo如下
image

demo1如下
image

相当于Xcode工程Build Phases目录下的Compile Sources的文件顺序,文件顺序不一致,编译生成的二进制文件排列不一致
图片.png

4.库文件

I.以.a文件结尾的动态库静态库文件,查找.a文件,find /usr -name "*.a"

发现libpython3.9.a是动态库可执行文件


image

发现libx264.a、libSDL2.a、libfdk-aac.a是静态库可执行文件


image

II.以.dylib结尾的dylib也是MachO文件

图片.png

III.dyld动态链接器文件,系统内核触发dyld

image

IV.dsym文件,App打包时生成,edit schemes中修改为release模式下,build编译,App包统计目录下生成Demo.app.dSYM,若遇到崩溃根据堆栈信息,要用此符号文件进行排查分析拿到方法名称

图片.png

图片.png

Demo.app.dSYM也是一个包,右键显示包内容,有一个Demo的MachO文件


图片.png

image

5.MachO文件架构

MachO-Type类型,Build Settings中查看,生成的文件类型


图片.png

iOS11.0以上的系统只支持arm64架构,32位的架构在11.0的系统安装不了了,无法适配
若将DeploymentInfo改为支持iOS9.0以上,则会出现armv7和arm64架构


image

若要添加支持的系统架构,可在Build Settings中Architectures添加架构类型如armv7s,armv7s支持iPhone5和iPhone5c
image

图片.png

6.通用二进制文件(Universal binary)

苹果公司提出的一种程序代码。能同时适用多种架构的二进制文件
同一个程序包中同时为多种架构提供最理想的性能。
因为需要储存多种代码,通用二进制应用程序通常比单一平台二进制的程序要大。
但是 由于两种架构有共通的非执行资源(代码以外的),所以并不会达到单一版本的两倍之多。
而且由于执行中只调用一部分代码,运行起来也不需要额外的内存。

支持armv7s、armv7和arm64文件架构的二进制文件是通用二进制文件


图片.png

当用hopper打开Demo可执行文件时,会提示是Fat archive,表示是通用二进制文件,选择一种,hopper会分析选择的其中一种


image

图片.png
  • lipo命令
    使用lifo -info 可以查看MachO文件包含的架构
    lipo -info MachO文件 使用lifo –thin 拆分某种架构lipo MachO文件 –thin 架构 –output 输出文件路径
    使用lipo -create 合并多种架构
    $lipo -create MachO1 MachO2 -output 输出文件路径
image

7.MachO文件结构

image

Mach-O 的组成结构如图所示包括了
Header 包含该二进制文件的一般信息
字节顺序、架构类型、加载指令的数量等。
使得可以快速确认一些信息,比如当前文件用于32位还是64位,对应的处理器是什么、文件类型是什么
Load commands 一张包含很多内容的表
内容包括区域的位置、符号表、动态符号表等。
Data 通常是对象文件中最大的部分
包含Segement的具体数据

  • 通用二进制文件会有多个上述图中的结构,armv7一个,arm64一个,armv7s一个
  • 可通过otool查看MachO中的数据,otool指令如下
Usage: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/otool [-arch arch_type] [-fahlLDtdorSTMRIHGvVcXmqQjCP] [-mcpu=arg] [--version] <object file> ...
    -f print the fat headers
    -a print the archive header
    -h print the mach header
    -l print the load commands
    -L print shared libraries used
    -D print shared library id name
    -t print the text section (disassemble with -v)
    -x print all text sections (disassemble with -v)
    -p <routine name>  start dissassemble from routine name
    -s <segname> <sectname> print contents of section
    -d print the data section
    -o print the Objective-C segment
    -r print the relocation entries
    -S print the table of contents of a library (obsolete)
    -T print the table of contents of a dynamic shared library (obsolete)
    -M print the module table of a dynamic shared library (obsolete)
    -R print the reference table of a dynamic shared library (obsolete)
    -I print the indirect symbol table
    -H print the two-level hints table (obsolete)
    -G print the data in code table
    -v print verbosely (symbolically) when possible
    -V print disassembled operands symbolically
    -c print argument strings of a core file
    -X print no leading addresses or headers
    -m don't use archive(member) syntax
    -B force Thumb disassembly (ARM objects only)
    -q use llvm's disassembler (the default)
    -Q use otool(1)'s disassembler
    -mcpu=arg use `arg' as the cpu for disassembly
    -j print opcode bytes
    -P print the info plist section as strings
    -C print linker optimization hints
    --version print the version of /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/otool
image

otool -f Demo,查看MachO文件头信息,Demo是通用二进制文件支持三种架构


image

8.MachOView查看MachO文件

Fat Binary中有三种不同的架构armv7,armv7s,arm64
首先是Fat Header,cputype=12表示ARM架构CPU_TYPE_ARM,
cpusubtype=9(小端模式00000009)表示CPU_SUBTYPE_ARM_V7
cpusubtype=11(小端模式0000000B)表示CPU_SUBTYPE_ARM_V7S
cpusubtype=0(小端模式00000000)表示CPU_SUBTYPE_ARM64_ALL


image

image

每种架构的偏移地址Offset和Size相加后会有间隔,是因为分页的原因,iOS中每页的大小为16k,MacOS中每页大小为4K


图片.png

从Load Commands开始一直到底都是DATA数据段
DATA数据段中Section分为两部分,TEXT和DATA
image

类名和方法名会有联系,工具classdump会dump出类的名称和方法的列表
image

Load Commands中能看到加载哪些库
图片.png

64位环境中PAGEZERO占用4G的空间0xffffffff,后面的所有指令都从0xffffffff00000001开始,目的是和32位指令隔开,插入PAGEZERO后和32位指令不会有重叠,这是PAGEZERO的目的,64位和32位做区分,在内存中执行时和32位完全隔离,是一个空区域分割区,让内存地址加上0xffffffff=4294967296,所有的数据往后移,和32位架构的指令不重复,如果有数据指向PAGEZERO为空,里面是不放数据的
早期的架构armv7和armv7s的PAGEZERO为16384

64位大小的地址
0x12345678a2345678
32位大小的地址
0x12345678
0xffffffff=4G 
image

9.MachO Header

  • Header的数据结构,可在loader.h文件中查看


    图片.png

    image

    filetype文件类型


    图片.png

    image

10.Load Commands

image

DATA部分由三部分组成,SECTION TEXT代码段,SECTION DATA数据段,LINKEDIT,指明起始位置,偏移位置


image

ASLR,操作系统为每一个进程分配随机的ASLR,
应用程序加载进内存中时,实际地址=ASLR+Rebase Info Size,在MachO指定后,方便应用程序
加载进内存后调用,编译时期生成的是偏移地址Rebase Info
Offset,表示在整个MachO文件中偏移多少,程序在第一次加载进内存中时根据ASLR+Rebase Info Offset


image

汇编跳转bl,在编译时期生成的是偏移地址,在文件中偏移多少,运行时期的地址每次都变化,重定向改变的是汇编代码


image

image

重定向改变的是汇编代码,地址前的0x1表示PAGEZERO


image

Binding Info offset和Binding Info Size,外部的符号将地址绑定上去,Weak Binding Info Offset和Weak Binding Info Size弱绑定,Lazy Binding Info offset和Lazy Binding Info Size懒绑定,用到的时候再去绑定,Export Info Offset和Export Info Size对外开放的函数
图片.png

image

每一段数据以页为单位


图片.png

Load Commands和Section之间会有空间,之前动态注入修改成功是因为有空间,才能插入一条
图片.png

Size of load Commands + 2720 = 0002E734和S
ection64 Text段的地址00032374中间有一段地址隔开,故动态注入时能插入一条新的内容在Load Commands的最底部,若没有空间则无法插入
image

图片.png

image

上篇文章动态注入时,我们看到WeChat可执行文件自己的动态库andromedo的路径@rpath/andromeda.framework/andromeda,@rpath路径在Load Command中已经指明为@executable_path/Frameworks,才能找到动态库andromedo
image

image

image

11.DATA数据段

Load Commands中已经指明了DATA数据段包括三部分内容


image

TEXT段可以通过工具objdump --macho -d Demo查看到


image

图片.png

0x1000065a0都指向开头
图片.png

image

Symbol Stubs和Assembly结合起来做符号绑定


image

外部符号表:调用外部函数,只有在运行那一刻,才能找到
启动时刻就绑定了,应用程序一启动,外部函数和符号表进行绑定
Lazy Symbol Pointers中的函数做绑定时会调用Non-Lazy Symbol Pointers的dyld_stub_binder做绑定,绑定符号的目的,将外部函数的真实地址告诉MachO文件,方便调用,先绑定专门用来绑定的函数,之后用此函数去绑定所有的函数
图片.png

调用Non-Lazy Symbol Pointers中的dyld_stub_binder去绑定
image

image
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容