Mach-O格式文件(用户态下的进程加载)


Mach-O二进制文件

Mach-O的文件头包含的内容:

  • 魔数
  • CPU类型及其子类型
  • 文件类型
  • 用于加载器的“加载命令”的条数和大小
  • 动态链接器的标志

Mach-O的加载命令

内核加载器会在加载的过程中使用这些命令来对进程进行一些设置:包括分配虚拟内存、创建主线程、启动动态链接器以及处理代码签名等工作。重要的命令有:

  • LC_SEGMENT或者LC_SEGMENT_64(设置进程的内存空间)
  • 代码段(__TEXT)、数据段(__DATA)、用户动态链接的桩(__stubs、__stub_helper)、主程序代码(__text)
  • LC_LOAD_DYLINKER(内核加载器在执行该命令时启动动态链接器)
  • LC_MAIN(设置进程的入口地址和栈大小,以及出程序计数器外的寄存器清零)
  • LC_CODE_SIGNATURE(代码签名)

otool可以用来可以用来分析加载命令和代码段,如:otool -l /bin/ls

动态库

动态链接

少量的进程只需要内核加载器就能完成加载,OSX中几乎所有的程序都是动态链接的--即填补对外部库和符号的引用。这个工作是由动态链接器来完成。该过程也被称为符号绑定。这个过程大概是这样的:

如果二进制文件使用了外部定义的函数或符号,那么在他们的文本段中就会有一个名为__stubs的区,在这个区中存放的是本地未定义符号的占位符。编译器生成代码时会创建对符号桩的调用,链接器在运行的时候会解决对桩的这些调用--即在被调用的地址处放置一条JMP指令,并将控制权交给真实的函数体。但不会修改栈。因此真实的函数可以正常返回,就像直接调用函数一样。

链接一般都是递归的,因为库也有可能引用其他的库。

共享库缓存(shared library cache)

共享库缓存是dyld支持的的另一种机制。是指:一些库经过预先链接,然后保存在磁盘的一个文件中。

在OS X中dyld共享缓存保存在/private/var/db/dyld目录下。在iOS中则保存在/System/Library/Caches/com.apple.dyld.

运行时加载

一般通过#include包含一些头文件,这种方式构建的可执行文件只有在解决了所有依赖条件之后才能加载执行。但是通过<dlfcn.h>头文件提供的函数就可以在运行时(runtime)加载库。这样函数有:

  • dlopen(const char *path)
  • dlopen_preflight(const char *path)
  • dlsym(void *handle ,char *sym)
  • dladdr(char *addr , DL_Info *info)
  • dlerror()

Cocoa和Carbon为dl*系列提供了高层的封装,以及CFBundle和NSBundle对象,用于加载Mach-O bundle文件。

弱定义的符号

  • 通常情况下符号都是被声明为强定义的,即文件在执行之前必须先解析这些符号,若发生解析失败,则程序运行失败,通常也会触发调试器陷阱。
  • 可以使用__attribute__(weak_import)将符号声明为弱符号。这样则在解析符号错误的时候,不会触发链接错误,动态链接器会将这个符号设置为NULL,效果跟运行时加载动态库类似(如dlopen)。

使用nm -m xxx.dylib可以显示弱符号。

dyld的特性

两级命名空间

  • 通过将DYLD_FORCE_FLAT_NAMESPACE环境变量设置为非零即可禁用。
  • 可执行文件也可以在文件头中设置MH_FORCE_FLAT标志,强制对其加载的所有库使用平坦命名空间。

函数拦截

  • DYLD_INTERPOSE宏允许一个库将其函数替换为另一个函数。(跟iOS的swizzle类似),例如:
DYLD_INTERPOSE(my_open ,open)
  • dyld的函数拦截功能提供一个新的__DATA区,名为__interpose,在这个区中依次列出了替换的函数和被替换的函数,其他事情则交给dyld处理。例如:
static const interpose_t interposing_functions[] \\\\
    __attribute__(section("__DATA,__interpose")) = {
        {(void *)my_free , (void *)free },
        {(void *)my_malloc , (void *) malloc },
    };

完整代码:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <malloc/malloc.h> // for malloc_printf()

// Note: Compile with GCC, not cc (important)
//
//
// This is the expected interpose structure
 typedef struct interpose_s { void *new_func;
                   void *orig_func; } interpose_t;
// Our prototypes - requires since we are putting them in 
//  the interposing_functions, below

void *my_malloc(int size); // matches real malloc()
void my_free (void *); // matches real free()

// For clang, add attribute(used)
static const interpose_t interposing_functions[] \\\\ 
    __attribute__ ((used, section("__DATA, __interpose"))) = {

 { (void *)my_free, (void *)free },
 { (void *)my_malloc, (void *)malloc } 

};

void *
my_malloc (int size) {
 // In our function we have access to the real malloc() -
 // and since we don’t want to mess with the heap ourselves,
 // just call it
 //
void *returned = malloc(size);
// call malloc_printf() because the real printf() calls malloc()
// // internally - and would end up calling us, recursing ad infinitum

  malloc_printf ( "+ %p %d\\\\n", returned, size); return (returned);
}
void
my_free (void *freed) {
// Free - just print the address, then call the real free()


  malloc_printf ( "- %p\\\\n", freed); free(freed);
}



#if 0
  From output 4-11:

 morpheus@Ergo(~)$ gcc -dynamiclib l.c -o libMTrace.dylib -Wall  // compile to dylib
 morpheus@Ergo(~)$ DYLD_INSERT_LIBRARIES=libMTrace.dylib ls     // force insert into ls
 ls(24346) malloc: + 0x100100020 88
 ls(24346) malloc: + 0x100800000 4096
 ls(24346) malloc: + 0x100801000 2160 
 ls(24346) malloc: - 0x100800000 
 ls(24346) malloc: + 0x100801a00 3312 ... // etc.

#endif

使用pagestuff命令可以显示文件逻辑页中的符号。如:pagestuff /usr/lib/libgmalloc.dylib 6,

进程的地址空间

  • 每一个进程都有自己私有的虚拟地址空间。
  • 32位地址空间,用户态可访问整个4G的内存空间。
  • 64位的地址允许高达16EB(16GGB)
  • 现代系统一般都会在每次启动进程的时候,将其地址空间随机化(随机的给每个段加上地址偏移)。

使用vmmap命令来查看内存的空间布局,可以加上参数-interleaved以清晰的方式导出地址空间。

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

推荐阅读更多精彩内容