mach空串 php preg_Mach-O在内存中符号表地址、字符串表地址的计算

KSCrash 是一个用于 iOS 平台的崩溃捕捉框架,最近读了其部分源码,在 KSDynamicLinker 文件中有一个函数,代码如下:

/** Get the segment base address of the specified image.

*

* This is required for any symtab command offsets.

*

* @param idx The image index.

* @return The image's base address, or 0 if none was found.

*/

static uintptr_t segmentBaseOfImageIndex(const uint32_t idx)

{

const struct mach_header* header = _dyld_get_image_header(idx);

// Look for a segment command and return the file image address.

uintptr_t cmdPtr = firstCmdAfterHeader(header);

if(cmdPtr == 0)

{

return 0;

}

for(uint32_t i = 0;i < header->ncmds; i++)

{

const struct load_command* loadCmd = (struct load_command*)cmdPtr;

if(loadCmd->cmd == LC_SEGMENT)

{

const struct segment_command* segmentCmd = (struct segment_command*)cmdPtr;

if(strcmp(segmentCmd->segname, SEG_LINKEDIT) == 0)

{

return segmentCmd->vmaddr - segmentCmd->fileoff;

}

}

else if(loadCmd->cmd == LC_SEGMENT_64)

{

const struct segment_command_64* segmentCmd = (struct segment_command_64*)cmdPtr;

if(strcmp(segmentCmd->segname, SEG_LINKEDIT) == 0)

{

return (uintptr_t)(segmentCmd->vmaddr - segmentCmd->fileoff);

}

}

cmdPtr += loadCmd->cmdsize;

}

return 0;

}

该函数被如此调用:

const uintptr_t segmentBase = segmentBaseOfImageIndex(idx) + imageVMAddrSlide;

0 迷惑现场

一个 image 中会有多个 segment,参数 idx 传递的是 image 的索引,如果返回的是 segment base, 那么是哪个 segment?

有人会说,注释里不是说返回非 0 的话,就表示的是 image base。可是从原理上讲 vmaddr - fileoff 根本得不到 image base(后文有解释)。

而在被调用处,加上由 ASLR 引起的偏移,赋值给了 segmentBase。

在 fishhook 中,有这么一行代码:

uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;

暂不考虑由 ASLR 造成的 slide,那么又是上边提到的 vmaddr - fileoff,这里的变量命名是 linkedit_base。

KSCrash 中的所谓的 segmentBase 和 fishhook 中所谓的 linkedit_base,到底指的是什么?如果指的是 __LINKEDIT 端在内存中的真实地址那应该是 vmaddr + ASLR偏移 才对。

在查找资料的过程中,读了大量的博客、资料,对于这一块的解释,要么没提,要么一带而过,要么是错的。有的认为这个值是__LINKEDIT 段在内存中的基址,有的认为是当前 image 在内存中的基址。

1 揭开面纱

1.1 前置知识

在理解这个值到底是什么之前,我们需要一些前置知识。

Mach-O 文件的结构

虚拟内存

ASLR

下边我们简单的说一下 Mach-O 文件。

Mach-O

我们知道,进程是可执行文件在内存中加载得到的结果,而 Mach-O 就是一种 macOS 平台的可执行文件格式。

Mach-O 文件分为三个区域 Header、Load commands、Data。其中 Load commands 区的指令指导如何设置并加载二进制数据。下边列出 32 位平台下我们关心的几个:

指令

对应的数据结构

描述

LC_SEGMENT

segment_command

定义了这个文件中的一个 segment,在 Mach-O 文件被加载到时,这个 segment 会被映射到对应的地址空间。需要留意,segment_command 中有一个 segname,可通过 segname 来查找指定的 segment。

LC_SYMTAB

symtab_command

指定了这个文件的符号表。symtab_command 中包含符号表在文件中的偏移、符号数量、字符串表在文件中的偏移、字符串表的大小。

segment_command 代码如下:

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 */

};

对于每一个 segment 而言,设置进程虚拟内存的过程就是将相应的内容加载到内存中,也就是从 Mach-O 文件的 fileoff 初加载 filesize 字节到虚拟内存地址的 vmaddr 处,占用 vmsize 字节。需要留意,对某些 segment 来说,vmsize 可能会大于 filesize,如__Data、__LINKEDIT。

在后边的讨论中,我们需要关心的是 segname 为 __LINKEDIT 的段。__LINKEDIT 段由 dyld 使用,包含符号表、字符串表以及其他数据。

symtab_command 代码如下:

struct symtab_command {

uint32_tcmd;/* LC_SYMTAB */

uint32_tcmdsize;/* sizeof(struct symtab_command) */

uint32_tsymoff;/* symbol table offset */

uint32_tnsyms;/* number of symbol table entries */

uint32_tstroff;/* string table offset */

uint32_tstrsize;/* string table size in bytes */

};

在 symtab_command 中,symoff 为符号表在 Mach-O 文件中的偏移、stroff 为字符串表在 Mach-O 文件中的偏移。

1.2 揭秘

我们可以使用 MachOView 来打开一个 Mach-O 文件,观察 LC_SEGMENT(__LINKEDIT)、LC_SYMTAB。限于篇幅,这里就不截图观察了。但是你应当留意到符号表、字符串表在 Mach-O 文件的位置,位于 __LINKEDIT 段中,这也验证了上边对 __LINKEDIT 段的介绍。

我们从符号表在虚拟内存中的地址来倒推上边那个所谓的 segmentBase、linkedit_base,看一张图(不是很准确,但可以帮助我们搞明白这个问题)。

da4787e8d5948393a4fa558615bf4f6b.png

我们先忽略 ASLR,图中的深灰色背景表示是虚拟内存,__TEXT 段、__DATA 段我们不关心,图中没有体现。

sym_vmaddr 是指的是符号表在虚拟内存中地址,而在虚拟内存中符号表在 __LINKEDIT 段中偏移,即 sym_vmaddr - vmaddr,与其在 MachO 文件中的偏移,即 symoff - fileoff 相等。

也就是sym_vmaddr - vmaddr = symoff - fileoff,

vmaddr 移到右边,即 sym_vmaddr = symoff - fileoff + vmaddr

发现什么了吗?

接着上边推:

减去符号表偏移symoff:sym_vmaddr - symoff = vmaddr - fileoff(式1),

式 1 等号右边的部分加上 ASLR 偏移 slide:vmaddr - fileoff + slide,也就是所谓的 segmentBase、linkedit_base。

至此,真相大白。

参考

深入解析Mac OS X & iOS操作系统

深入理解计算机系统

Mach-O File Format

The Mac Hacker's Handbook

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值