先来个helloworld
int main(int argc, const char * argv[]) {
return 0;
}
注: 这里没有包含任何头文件,也没有printf函数,目的是为了写一个尽可能简单的helloWorld
然后我发现这个helloWorld
的大小是32K,神马什么都不干还要32k,吃屎吧你。
于是我决定扒掉它的内裤。我选择MachOView工具。
打头的当然是header了, magic number表明这是一个Mach-O文件,还有其他的类型比如weindows下的pe文件等等, 紧接着是cpu类型arm64,然后是具体的类型执行文件。
简单的说操作系统它需要知道你的文件类型结构,哦你是mach-o文件啊,那按照mach-o结构进行解析,再匹配下cpu类型,耶你是x86不好意思我们不合适,88.再看下具体是类型,你是执行文件啊,好吧我给你加载到内存里让你运行,咦你是目标文件啊 ,小伙子你还太年轻下次再来。
紧接着就是load commands,文件解析之后要加载到内存里面具体怎么加载,就看它们的了。
屏幕快照 2019-11-01 上午11.12.55.png先看看LC_Command 这个命令就是将指定的文件区域加载到指定的内存区域,(这里的内存指的都是虚拟内存)
FileSize 和 FileOffset是相对于文件而言,VM Size 和 VM Addr是相对于内存而言的,它们之间存在一定的联系,但并不是一一对应的。
看下这个 _pagezero段就知道了,
- FileSize = 0
- FileOffset = 0
- VM Size = 4294967296
- VM Addr = 0
- protection : 不可访问
这段能干嘛呢,其实这一段内存并不会真正的分配,这个就是表明0 ~4294967296这段内存是不能访问的,你现在知道为什么会弹“0x0该内存不能为read了吧”
接下来看一下LC_Command (__TEXT)段
LC_Command (__TEXT)
这一段是一段代码段,为什么这么说了,不单单是因为它叫text段,更重要的是
这段的Protection = VM_PROT_EXCUTE,表明是个可执行文件,而且它只能读不能写(VM_PROT_READ)。我以前就干过把编译好的二进制代码放到数据段,然后修改指针让其运行结果导致crash的傻事,只有被标明VM_PROT_EXCUTE的段才能被cpu执行,否则等着crash吧。
现在我们来想象一下被加载之后的内存场景:
- 0 ~ 4294967296 不可访问
- 4294967296 ~ 4294967296 + 32768 代码段可以被执行
好了 现在我们假设程序以及被正确了加载到内存里面,那么如何找到main函数入口呢?之所以是假设是因为还有一些细节暂时被我屏蔽了。
那就要用到LC_MAIN啦
屏幕快照 2019-11-01 上午11.43.17.pngLC_MAIN 包含了执行文件的入口信息,那么就能找到入口了啦。
附上一张MachO结构图
至于为什么这个文件有32k呢,是因为command结束到section的开始,中间留了很大一片的空间(约等于32k),至于为什么是这样具体还不太清楚。
网友评论