Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/macos/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何从对象文件中读取Mach-O头文件?_C_Macos_Executable - Fatal编程技术网

如何从对象文件中读取Mach-O头文件?

如何从对象文件中读取Mach-O头文件?,c,macos,executable,C,Macos,Executable,在过去的几天里,我一直在尝试汇编,现在我已经了解了汇编和机器代码之间的关系(通过OSX上的NASM使用x86,阅读) 现在我试图了解链接器如何工作的细节,特别是想了解Mach-O对象文件的结构,从Mach-O头开始 我的问题是,您是否可以将下面的Mach-O标题映射到otool命令输出(显示标题,但格式不同) 产生这个问题的一些原因包括: 它将帮助我了解“Mach-O头结构”上的文档在真实对象文件中的外观 这将简化理解的过程,所以我和其他新来者不必花很多时间或几天去想“他们是指这个还是这个”类

在过去的几天里,我一直在尝试汇编,现在我已经了解了汇编和机器代码之间的关系(通过OSX上的NASM使用x86,阅读)

现在我试图了解链接器如何工作的细节,特别是想了解Mach-O对象文件的结构,从Mach-O头开始

我的问题是,您是否可以将下面的Mach-O标题映射到
otool
命令输出(显示标题,但格式不同)

产生这个问题的一些原因包括:

  • 它将帮助我了解“Mach-O头结构”上的文档在真实对象文件中的外观
  • 这将简化理解的过程,所以我和其他新来者不必花很多时间或几天去想“他们是指这个还是这个”类型的事情。如果没有以前的经验,很难在现实世界中将一般的Mach-O文档转换为实际的对象文件
下面我展示了我尝试从真实对象文件解码Mach-O头的示例和过程。在下面的描述中,我试图展示出现的所有小/微妙问题的提示。希望这将提供一种新来者会感到非常困惑的感觉


例子 从名为
example.C
的基本C文件开始:

#include <stdio.h>

int
main() {
  printf("hello world");
  return 0;
}
运行
otool-h example.out
,它将打印:

example.out:
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777223          3  0x80          2    16       1296 0x00200085

研究 为了理解Mach-O文件格式,我发现以下资源非常有用:

来自opensource.apple.com的最后3个包含所有常量,例如:

#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */
#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */
...
#define CPU_TYPE_MC680x0  ((cpu_type_t) 6)
#define CPU_TYPE_X86    ((cpu_type_t) 7)
#define CPU_TYPE_I386   CPU_TYPE_X86    /* compatibility */
#define CPU_TYPE_X86_64   (CPU_TYPE_X86 | CPU_ARCH_ABI64)
Mach-O割台的结构如图所示:

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 */
};
根据这些信息,我们的目标是在
example.out
对象文件中找到Mach-O头的每个部分


第一:找到“神奇”数字 根据这个例子和研究,我能够识别马赫-O头的第一部分,即“幻数”。那很酷

但这不是一个简单的过程。下面是为了弄清楚这一点必须收集的一些信息

  • otool
    输出的第一列显示“magic”为
    0xfeedfacf
  • 标题应该是
    MH_MAGIC
    MH_CIGAM
    (“MAGIC”相反)。因此,在中通过谷歌找到了这些。因为我使用的是64位体系结构而不是32位,所以我选择了
    MH_MAGIC_64
    0xfeedfacf
    )和
    MH_CIGAM_64
    0xcffaedfe
  • 查看
    示例。out
    文件和前8个十六进制代码是
    cffa edfe
    ,与
    MH_CIGAM_64
    匹配!这是一个不同的格式,这让你有点不舒服,但他们是两个不同的十六进制格式,接近足以看到连接。它们也是相反的
下面是3个数字,它们足以算出神奇的数字是什么:

0xcffaedfe // value from MH_CIGAM_64
0xfeedfacf // value from otool
cffa edfe  // value in example.out
那太令人兴奋了!对于这些数字,我仍然不能完全确定我是否得出了正确的结论,但希望如此


下一步:查找cputype 现在它开始变得令人困惑。以下是需要拼凑起来才能理解的部分,但这正是我迄今为止遇到的问题:

  • otool
    显示
    16777223
    。就如何理解这一点给出了一些提示
  • 在中找到了
    CPU\u TYPE\u X86\u 64
    ,并进行了多次计算以确定其值
以下是用于计算
CPU\u TYPE\u X86\u 64的值的相关常数:

#define CPU_ARCH_ABI64  0x01000000      /* 64 bit ABI */
#define CPU_TYPE_X86        ((cpu_type_t) 7)
#define CPU_TYPE_I386       CPU_TYPE_X86        /* compatibility */
#define CPU_TYPE_X86_64     (CPU_TYPE_X86 | CPU_ARCH_ABI64)
所以基本上:

CPU_TYPE_X86_64 = 7 BITWISEOR 0x01000000 // 16777223
这个数字
16777223
otool
显示的数字匹配,很好

接下来,尝试在
示例.out
中查找该数字,但该数字不存在,因为它是十进制数字。我刚刚在JavaScript中将其转换为十六进制,其中

> (16777223).toString(16)
'1000007'
因此,不确定这是否是生成十六进制数的正确方法,尤其是与Mach-O对象文件中的十六进制数匹配的方法
1000007
也只有7个数字,所以不知道是应该“填充”它还是什么

无论如何,您会看到这个数字
示例。out
,就在魔法数字之后:

0700 0001
嗯,他们似乎有些关联:

看起来在
1000007
的末尾添加了一个
0
,并且它被颠倒了


问题:
在这一点上,我想问一个问题,已经花了几个小时来达到这一点。Mach-O头文件的结构如何映射到实际的Mach-O对象文件?您能否在上面的
示例.out
文件中显示标题的每个部分,并简要说明原因?

MAGIC
CIGAM
为您提供文件中使用的字节顺序提示。当您将前四个字节读取为
cffaedfe
时,这意味着您应该解释little endian中的任何4个字节。这意味着你先写数字,然后写第十个,以此类推。因此,当你读
07000001
时,它代表的是数字01000007,这正是你所等待的(1000007),除了前导的0。我可以建议您阅读有关字节排序的内容吗?

部分让您感到困惑的是。在这种情况下,报头以平台的本机格式存储。与英特尔兼容的平台是little endian系统,这意味着多字节值的最低有效字节位于字节序列的第一位

因此,字节序列
07 00 00 01
,当被解释为一个小的尾端32位值时,对应于
0x01000007

要解释结构,您需要知道的另一件事是每个字段的大小。所有的
uint32\t
字段都非常简单。它们是32位无符号整数

cpu\u类型\u t
cpu\u子类型\u t
ar
0700 0001
0700 0001
1000007
cffa edfe 0700 0001 0300 0080 0200 0000
1000 0000 1005 0000 8500 2000 0000 0000
struct mach_header_64 {
  uint32_t  magic;           cf fa ed fe -> 0xfeedfacf
  cpu_type_t  cputype;       07 00 00 01 -> 0x01000007
  cpu_subtype_t cpusubtype;  03 00 00 80 -> 0x80000003
  uint32_t  filetype;        02 00 00 00 -> 0x00000002
  uint32_t  ncmds;           10 00 00 00 -> 0x00000010
  uint32_t  sizeofcmds;      10 05 00 00 -> 0x00000510
  uint32_t  flags;           85 00 20 00 -> 0x00200085
  uint32_t  reserved;        00 00 00 00 -> 0x00000000
};