1. Mach-O小试牛刀
1.1 温习Mach-O
segment
是磁盘和内存中具有相同内存保护的分组。在一个分组中,segment
可以有零个或多个section
。
section
是在segment
中找到的子组件。他们对程序有专门的作用。例如,有一个特定的section
用于编译代码,另一个section
用于硬编码字符串。
section
和segment
由加载命令决定。加载命令位于可执行文件的开头,紧跟在Mach-O header
之后。
其中有几个重要的segment
:__TEXT
、__DATA
和__LINKEDIT
。
1.2 Mach-O sections
这里有一个名为MachOFun
的iOS项目。这是一个简单的基于UITabBarController
的应用程序,它分隔了我们将在这里实现的不同示例。
其中一个选项是带有一些占位符数据的UITableViewController
。首先构建一个数据源,在内存中查找所有硬编码的不安全HTTP URL,然后在UITableView
中将它们显示为“public shaming”。
另一个标签显示了一个相当丑陋的老虎机,实现赌博的目的。我们将使用Mach-O
知识欺骗系统,并始终获胜。
我们来看一下项目所有的section
。
(lldb) image dump sections MachOFun
Sections for '/Users/xxx/Library/Developer/Xcode/DerivedData/MachOFun-gsmnrawhxbagdmhjegfsuaqahrqq/Build/Products/Debug-iphonesimulator/MachOFun.app/MachOFun' (x86_64):
SectID Type Load Address Perm File Off. File Size Flags Section Name
---------- ---------------- --------------------------------------- ---- ---------- ---------- ---------- ----------------------------
0x00000100 container [0x0000000000000000-0x0000000100000000)* --- 0x00000000 0x00000000 0x00000000 MachOFun.__PAGEZERO
0x00000200 container [0x000000010947a000-0x0000000109489000) r-x 0x00000000 0x0000f000 0x00000000 MachOFun.__TEXT
0x00000001 code [0x000000010947b1a0-0x0000000109485cda) r-x 0x000011a0 0x0000ab3a 0x80000400 MachOFun.__TEXT.__text
0x00000002 code [0x0000000109485cda-0x0000000109485ef0) r-x 0x0000bcda 0x00000216 0x80000408 MachOFun.__TEXT.__stubs
0x00000003 code [0x0000000109485ef0-0x000000010948627a) r-x 0x0000bef0 0x0000038a 0x80000400 MachOFun.__TEXT.__stub_helper
0x00000004 regular [0x0000000109486280-0x0000000109486507) r-x 0x0000c280 0x00000287 0x00000000 MachOFun.__TEXT.__const
0x00000005 data-cstr [0x0000000109486507-0x00000001094873a2) r-x 0x0000c507 0x00000e9b 0x00000002 MachOFun.__TEXT.__objc_methname
0x00000006 data-cstr [0x00000001094873b0-0x00000001094888c3) r-x 0x0000d3b0 0x00001513 0x00000002 MachOFun.__TEXT.__cstring
0x00000007 regular [0x00000001094888c4-0x0000000109488a39) r-x 0x0000e8c4 0x00000175 0x00000000 MachOFun.__TEXT.__swift5_typeref
0x00000008 regular [0x0000000109488a3c-0x0000000109488b3c) r-x 0x0000ea3c 0x00000100 0x00000000 MachOFun.__TEXT.__swift5_capture
0x00000009 regular [0x0000000109488b40-0x0000000109488ba6) r-x 0x0000eb40 0x00000066 0x00000000 MachOFun.__TEXT.__swift5_reflstr
0x0000000a regular [0x0000000109488ba8-0x0000000109488c48) r-x 0x0000eba8 0x000000a0 0x00000000 MachOFun.__TEXT.__swift5_fieldmd
0x0000000b regular [0x0000000109488c48-0x0000000109488c58) r-x 0x0000ec48 0x00000010 0x00000000 MachOFun.__TEXT.__swift5_types
0x0000000c regular [0x0000000109488c58-0x0000000109488dd4) r-x 0x0000ec58 0x0000017c 0x00000000 MachOFun.__TEXT.__entitlements
0x0000000d compact-unwind [0x0000000109488dd4-0x0000000109489000) r-x 0x0000edd4 0x0000022c 0x00000000 MachOFun.__TEXT.__unwind_info
0x00000300 container [0x0000000109489000-0x000000010948c000) rw- 0x0000f000 0x00003000 0x00000000 MachOFun.__DATA
0x0000000e data-ptrs [0x0000000109489000-0x0000000109489008) rw- 0x0000f000 0x00000008 0x00000006 MachOFun.__DATA.__nl_symbol_ptr
0x0000000f data-ptrs [0x0000000109489008-0x00000001094890d0) rw- 0x0000f008 0x000000c8 0x00000006 MachOFun.__DATA.__got
0x00000010 data-ptrs [0x00000001094890d0-0x0000000109489398) rw- 0x0000f0d0 0x000002c8 0x00000007 MachOFun.__DATA.__la_symbol_ptr
0x00000011 data-ptrs [0x0000000109489398-0x00000001094893a0) rw- 0x0000f398 0x00000008 0x00000009 MachOFun.__DATA.__mod_init_func
0x00000012 regular [0x00000001094893a0-0x0000000109489688) rw- 0x0000f3a0 0x000002e8 0x00000000 MachOFun.__DATA.__const
0x00000013 data-ptrs [0x0000000109489688-0x00000001094896a8) rw- 0x0000f688 0x00000020 0x10000000 MachOFun.__DATA.__objc_classlist
0x00000014 regular [0x00000001094896a8-0x00000001094896b8) rw- 0x0000f6a8 0x00000010 0x10000000 MachOFun.__DATA.__objc_catlist
0x00000015 regular [0x00000001094896b8-0x00000001094896d8) rw- 0x0000f6b8 0x00000020 0x00000000 MachOFun.__DATA.__objc_protolist
0x00000016 regular [0x00000001094896d8-0x00000001094896e0) rw- 0x0000f6d8 0x00000008 0x00000000 MachOFun.__DATA.__objc_imageinfo
0x00000017 data-ptrs [0x00000001094896e0-0x000000010948b258) rw- 0x0000f6e0 0x00001b78 0x00000000 MachOFun.__DATA.__objc_const
0x00000018 data-cstr-ptr [0x000000010948b258-0x000000010948b338) rw- 0x00011258 0x000000e0 0x10000005 MachOFun.__DATA.__objc_selrefs
0x00000019 regular [0x000000010948b338-0x000000010948b358) rw- 0x00011338 0x00000020 0x00000000 MachOFun.__DATA.__objc_protorefs
0x0000001a data-ptrs [0x000000010948b358-0x000000010948b370) rw- 0x00011358 0x00000018 0x10000000 MachOFun.__DATA.__objc_classrefs
0x0000001b data-ptrs [0x000000010948b370-0x000000010948b620) rw- 0x00011370 0x000002b0 0x00000000 MachOFun.__DATA.__objc_data
0x0000001c data [0x000000010948b620-0x000000010948b7a5) rw- 0x00011620 0x00000185 0x00000000 MachOFun.__DATA.__data
0x0000001d regular [0x000000010948b7a8-0x000000010948b860) rw- 0x000117a8 0x000000b8 0x00000000 MachOFun.__DATA.__swift_hooks
0x0000001e zero-fill [0x000000010948b860-0x000000010948b8b8) rw- 0x00000000 0x00000000 0x00000001 MachOFun.__DATA.__bss
0x0000001f zero-fill [0x000000010948b8b8-0x000000010948b8c0) rw- 0x00000000 0x00000000 0x00000001 MachOFun.__DATA.__common
0x00000400 container [0x000000010948c000-0x00000001094a5000) r-- 0x00012000 0x00018de0 0x00000000 MachOFun.__LINKEDIT
我们可以看到一下这个section
。
0x00000001 code [0x000000010947b1a0-0x0000000109485cda) r-x 0x000011a0 0x0000ab3a 0x80000400 MachOFun.__TEXT.__text
- 分析这个输出,
0x00000001
是LLDB
标识section
的方法。 -
LLDB
已将内容标识为代码(code)。 - 括号中的地址是
section
在内存中的位置。 -
0x000011a0
是磁盘上的偏移量,而0x0000ab3a
是磁盘上section
的大小。 - 最后,
flag
值为0x80000400
,它是S_ATTR_SOME_INSTRUCTIONS
和S_ATTR_PURE_INSTRUCTIONS
。详情可以查看mach-o/loader.h
。
注意:磁盘上的
section
或segment
的大小可能与加载到内存时的大小不同。这将由Mach-O
加载命令决定的。例如,__PAGEZERO
段在磁盘上占用0字节。但当加载到内存中时,它占用64位进程中的前2^32位。使用otool -l /user/bin/ls | grep "Load command 0" -A11
可以验证这一点。filesize
变量为0,vmsize
变量为0x0000000100000000
或2^32
。
使用LLDB
,我们可以获取任何方法或函数,并找到它所在的部分。
(lldb) image lookup -n "-[UIViewController viewDidLoad]"
1 match found in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore:
Address: UIKitCore[0x0000000000438886] (UIKitCore.__TEXT.__text + 4414950)
Summary: UIKitCore`-[UIViewController viewDidLoad]
方法-[UIViewController viewDidLoad]
位于UIKitCore
中。上面的输出给出了UIKitCore
模块的完整路径。通过UIKitCore[0x0000000000438886]
显示磁盘上的偏移量;它包含在UIKitCore.__TEXT.__text
的偏移量4414950
处。
清除屏幕并再次运行image dump sections MachOFun LLDB命令。这一次,搜索MachOFun.文本。cstring部分。
下面我们看看MachOFun.__TEXT.__cstring
这个section
。
0x00000006 data-cstr [0x00000001094873b0-0x00000001094888c3) r-x 0x0000d3b0 0x00001513 0x00000002 MachOFun.__TEXT.__cstring
这是存储UTF-8
硬编码字符串的地方。用于打印语句、键值编码/观察或源代码中介于“”之间的任何其他内容。使用LLDB
,通过引用大小和开始加载地址来查看此部分中的内存。
memory read -r -fC -c 0x00001513 0x00000001094873b0
这将打印从地址0x00000001094873b0
开始的内存。将输出格式化为可打印字符-fC
,重复处理0x00001513
次,并强制-r
打印每次计数,因为LLDB
默认为1024
字节的上限。我们将在这里看到大量熟悉的字符串,例如"Unexpectedly found nil while unwrapping an Optional value"。
许多其他section
包含用于不同目的的UTF-8
字符串。例如__TEXT.__objc_methname
包含OC方法名由应用程序直接引用。__TEXT.__swift5_reflstr
包含对Swift映射项目的引用。Swift的其中一个运行时反射,将引用IBOutlet
或IBInspectable
变量。
注意:除了
__TEXT.__cstring section
,还有__TEXT.__ustring section
,存储UTF-16
编码字符串。这些字符串是UTF-16
编码的,所以需要使用大小适当的字符(char16_t *)
来查看它们。
1.3 查找HTTP字符串
既然已经知道硬编码的UTF-8
字符串存储在模块的__TEXT.__cstring
中。下面搜索进程中的每个模块,以查看是否有任何字符串以“http:”开头的字符串。
打开InsecureNetworkRequestsTableViewController.swift
。
import MachO
然后在setupDataSource
方法的内容替换为:
for i in 0..<_dyld_image_count() {
let imagePath = _dyld_get_image_name(i)!
let name = String(validatingUTF8: imagePath)!
let basenameString = (name as NSString).lastPathComponent
var module : InsecureHTTPRequestsData = (basenameString, [])
var rawDataSize: UInt = 0
guard let rawData = getsectdatafromFramework(basenameString,
"__TEXT",
"__cstring",
&rawDataSize) else { continue }
print("__TEXT.__cstring data: \(rawData), \(basenameString)")
}
getsectdatafromFramework
函数接受模块名、包含segment
名和section
名,并将指针指向section
在内存中的位置。此外,还有一个名为rawDataSize
的inout
变量,给出内存中section
的大小。
运行一下。我们将看到每个包含__TEXT.__cstring
的模块以及它们在模块中的加载地址。
__TEXT.__cstring data: 0x00000001021f85e0, dyld_sim
__TEXT.__cstring data: 0x000000010218f3b0, MachOFun
...
点击暂停。查询dyld_sim
的加载地址:
(lldb) image lookup -a 0x00000001021f85e0
Address: dyld_sim[0x000000000004a5e0] (dyld_sim.__TEXT.__cstring + 0)
Summary: "rebaseDyld"
dyld_sim[0x000000000004a5e0]
显示磁盘上__TEXT.__cstring
的偏移量。请记住,如果可执行文件是具有多个架构扇区的胖可执行文件,那么这可能不是磁盘上的最终偏移量。使用LLDB
,在dyld_sim.__TEXT.__cstring
打印出第一个字符串:
(lldb) x/s 0x00000001021f85e0
0x1021f85e0: "rebaseDyld"
编译到dyld_sim
模块中的第一个硬编码字符串是字符串rebaseDyld
。
我们已经找到了__TEXT.__cstring section
的起始地址。现在是解析整个内存缓冲区,搜索以“http:”开头的字符串的时候了。这个内存缓冲区是一堆UTF-8
C字符串。这意味着您需要一直解析字符串直到达到NULL
。
打开InsecureNetworkRequestsTableViewController.swift
并在setupDataSource
()中,删除先前的print
语句并替换为:
var index = 0
while index < rawDataSize {
let cur = rawData.advanced(by: index)
let length = strlen(cur)
index = index + length + 1
guard let str = String(utf8String: cur),
length > 0 else { continue }
if str.hasPrefix("http:") {
module.strings.append(str)
}
}
if module.strings.count > 0 {
dataSource.append(module)
}
这段代码将获取指向内存中一个__TEXT.__cstring section
的rawData
。while
循环执行几个检查,确保存在长度大于0的有效UTF-8
字符串。如果是,则检查字符串的开头是否包含字符“http:”。如果是,则将字符串添加到字符串数组中。最后,如果模块有任何包含“http:”的字符串,则该字符串将添加到dataSource变量中。
最后,在MachOFun
模块中设置一个受控测试,以确保这段代码能正确的工作。在viewDidLoad
中添加:
let _ = "https://www.baidu.com"
let _ = "http://www.weibo.com"
运行一下。
正如我们所看到的,
https://www.baidu.com
没在里面,http://www.weibo.com
在里面。并且,不仅在MachOFun
中有许多不安全的硬编码URL,像libxml2.2dylib
、GeoServices
、CFNetwork
等模块中也有。
1.4 __DATA segment中的sections
暂停MachOFun
应用程序,使用image dump sections MachOFun
查询data section
,找到__DATA.__objc_classlist
。
0x00000013 data-ptrs [0x000000010a3a0720-0x000000010a3a0740) rw- 0x00010720 0x00000020 0x10000000 MachOFun.__DATA.__objc_classlist
这个部分存储指向OC或Swift类的类指针。这个部分是一个类指针数组,指向存储在__DATA.__objc_data
中的实际类。把__DATA.__objc_data
看作是打包在一起的OC数据缓冲区。就像硬编码的UTF-8
字符串存储在__TEXT.__cstring
一样。
我们可以快速确定MachOFun
模块实现了4个类。segment
大小是0x00000020
除以64位进程中指针的大小(8字节),32/8=4个OC/Swift类。
(lldb) x/4gx 0x000000010a3a0720
0x10a3a0720: 0x000000010a3a2420 0x000000010a3a2518
0x10a3a0730: 0x000000010a3a25c8 0x000000010a3a2650
(lldb) cpo 0x000000010a3a2420
MachOFun.CasinoContainerView
(lldb) cpo 0x000000010a3a2518
MachOFun.CasinoViewController
(lldb) cpo 0x000000010a3a25c8
MachOFun.InsecureNetworkRequestsTableViewController
(lldb) cpo 0x000000010a3a2650
MachOFun.AppDelegate
__bss、__common和__const sections
有时模块需要保留对函数调用之后的数据的引用。比如在函数中声明一个常量let v=UIView()
,指针v
将存储在栈中,指向堆中分配的内存。但指令指针一离开函数,对v
变量的引用就消失得无影无踪了。这就是为什么在__DATA segment
中有几个部分被设计用来在进程的整个生命周期中存储变量。
当我们声明一个全局变量时,它通常会被放入__DATA.__common
。这个section
用来在整个模块,甚至模块之间共享信息。
如果开发人员想让变量在函数调用之间生存下来,但又不能让任何其他模块访问它,甚至不能从同一个模块中的其他源文件访问它,该怎么办呢?这通常通过在__DATA.__bss
存储这些变量来实现。C/Objective-C将通过对变量的static
声明来实现。在Swift中,这可以通过对Swift变量的private
声明来实现。
最后,有一些全局变量要声明为在程序生命周期内不变。我们可以在C/Objective-C中将它们标记为const
,在__DATA.__const
中存储这些变量。从开发人员的角度来看,Swift基本不需要接触__DATA.__const
的数据,因为let
关键字和在编译时检查变量的更改。
1.5 游戏作弊
__DATA segment
不仅存储对模块中数据的引用,还提供对未在模块中定义的外部变量、类、方法和函数的引用。
理论上,如果模块可以在任何地址进行加载,那么在调用该代码时,必须使用引用点来指示从何处开始查找。由于直到运行时才知道此位置,因此这个起始引用点必须可从调用模块写入。
这适用于外部C函数、Swift/Objective-C类、全局变量等。
__DATA.__la_symbol_ptr
是一个相当有趣的部分。因为它存储对外部函数的引用。这些外部函数在调用时在运行时被延迟解析。为了让这个复杂的过程正常工作,__DATA.__la_symbol_ptr
存储了一系列函数指针,这些指针指向调用模块__TEXT.__stub_helper
中的偏移量。当dyld
解析这个外部函数的位置的时候确定。我们只要知道外部函数在默认情况下是通过__DATA.__la_symbol_ptr
引用的。如果函数指针没有指向__TEXT.__stub_helper
中的地址,dyld
则会“解析”外部函数。
暂停MachOFun
程序,并在LLDB
中键入以下内容:
(lldb) exp -l objc -O -- [[NSBundle mainBundle] executablePath]
/Users/xxx/Library/Developer/CoreSimulator/Devices/85225EEE-8D5B-4091-A742-5BEBAE1C4906/data/Containers/Bundle/Application/44BBD366-CB9A-406B-B15E-EE5D666F4CFC/MachOFun.app/MachOFun
通过objdump
命令来查找MachOFun
可执行文件中所有懒绑定的外部函数。
(lldb) platform shell objdump -lazy-bind /Users/xxx/Library/Developer/CoreSimulator/Devices/85225EEE-8D5B-4091-A742-5BEBAE1C4906/data/Containers/Bundle/Application/44BBD366-CB9A-406B-B15E-EE5D666F4CFC/MachOFun.app/MachOFun | grep arc
__DATA __la_symbol_ptr 0x1000102F0 libSystem _arc4random_uniform
结果出来了,arc4random_uniform
在MachOFun
的代码中被调用了。这个0x1000102F0
是在没有ASLR
处理时的偏移量,它包括了__PAGEZERO
的偏移量(就是0x100000000
)以及磁盘上的实际实际偏移量0x102F0
。
我们要怎么把0x1000102F0
告诉给内存呢?我们可以通过_dyld_get_image_vmaddr_slide
这个接口来拿到地址。为了确保将正确的索引引用到模块中,通常索引1将指向主可执行文件。确保输出是MachOFun
之后,将索引1上与dyld_get_image_vmaddr_slide
一起使用,并将从objdump
命令检索到的值添加到后面:
(lldb) po (char *)_dyld_get_image_name(1)
"/Users/xxx/Library/Developer/CoreSimulator/Devices/85225EEE-8D5B-4091-A742-5BEBAE1C4906/data/Containers/Bundle/Application/44BBD366-CB9A-406B-B15E-EE5D666F4CFC/MachOFun.app/MachOFun"
(lldb) p/x (intptr_t)_dyld_get_image_vmaddr_slide(1) + 0x1000102F0
(long) $1 = 0x00000001097922f0
这里得到了0x00000001097922f0
。这个值是arc4random_uniform
解析到内存中外部引用位置的加载地址。解析这个地址的值:
(lldb) x/gx 0x00000001097922f0
0x1097922f0: 0x00007fff522f38b9
(lldb) image lookup -a 0x00007fff522f38b9
Address: libsystem_c.dylib[0x00000000000228b9] (libsystem_c.dylib.__TEXT.__text + 137321)
Summary: libsystem_c.dylib`arc4random_uniform
这意味着arc4random_uniform
函数已经被解析。因为函数指针在__DATA.__la_symbol_ptr
中指向arc4random_uniform
,而不是__TEXT.__stub_helper
中的偏移量。
哈哈,现在我们甚至不会在arc4random_uniform
上设置断点。因为我们非常确信这个老虎机正在调用arc4random_uniform
来生成随机。我们在内存中改变指针,看看会发生什么!
在LLDB
中创建一个返回5的全局函数。
(lldb) exp -l objc -p -- int lolzfunc() { return 5; }
(lldb) p/x lolzfunc
(int (*)()) $3 = 0x00000001108c9110 (0x00000001108c9110)
-p
选项表示在栈帧之外执行此代码。这是非常必要的。因为我们不能在其他C代码中声明这个函数。这意味着内存中有一个名为lolzfunc
的全局函数。
进攻计划现在应该很清楚了。我们将arc4random_uniform
的外部指针更改为新创建函数lolzfunc
的地址。
(lldb) memory write -s8 0x00007fff522f38b9 0x00000001108c9110
第一个指针是前面找到的arc4random_uniform
的原始地址。第二个指针是新的lolzfunc
地址。这告诉LLDB
在0x00007fff522f38b9
位置写入8字节(-s8),值为0x00000001108c9110。
取消断点,来试一试。
OC swizzling vs 函数插入
与Objective-C的method swizzling
不同,延迟指针加载是基于每个模块进行的。这意味着我们刚才执行的技巧只在MachOFun
模块调用arc4random_uniform
时有效。如果CFNetwork
先调用了arc4random_uniform
,它就不会工作。
回到MachoFun
应用程序,看到"Print a random number to console"按钮了吗?
该代码解析一个IBAction
方法,该方法调用SomeClassInAFramework.printARandomNumber()
。该方法是在一个AFramework
的framework
中实现的。在静态printARandomNumber()
函数中,调用arc4random_uniform
生成随机数。
点几次按钮,观察arc4random_uniform
是否正常工作。这意味着,如果要交换所有arc4random_uniform
存根,必须遍历每个模块。在内存中找到arc4random_uniform location
存根,并用新函数的地址替换它。
幸运的是,dyld
会在可执行文件中寻找一个特殊的__DATA section
,叫做__DATA.__interpose
。如果dyld
看到这个section
,之后加载的所有模块中的存根都会被这个新函数所替换。
在MachOFun
的target
中,有一个shell脚本被悄悄地隐藏起来。单击MachOFun
项目,选择MachOFun,选择Build Phases,然后展开Run Script。从那里,删除构建脚本顶部的exit 0
行。
echo """
#include <stdlib.h>
#define DYLD_INTERPOSE(_replacment,_replacee) \
__attribute__((used)) static struct{ const void* replacment; const void* replacee; } _interpose_##_replacee \
__attribute__ ((section (\"__DATA,__interpose\"))) = { (const void*)(unsigned long)&_replacment, (const void*)(unsigned long)&_replacee };
int dereksfunc() { return 7; }
DYLD_INTERPOSE(dereksfunc, arc4random_uniform)
""" > /tmp/lolz.c
clang /tmp/lolz.c -shared -fpic -isysroot `xcrun --show-sdk-path -sdk iphonesimulator` -o /tmp/lolz.dylib && codesign --force --sign - /tmp/lolz.dylib
此脚本将在/tmp/lolz.dylib
创建一个动态库,并使用特殊的代码签名对库进行签名。因为模拟器需要一个有效的签名,即使代码签名不匹配也没事。
这个动态库非常简单。它声明了一个名为dereksfunc
的函数,该函数返回整数7
。此函数的地址通过arc4random_uniform
插入,因此所有未来加载的模块都将替换arc4random_uniform
的存根。
使用强大的DYLD_INSERT_LIBRARIES
环境变量,可以在加载任何其他模块之前,首先加载这个lolz.dylib
模块,从而外部模块引用的每一个arc4random_uniform
将替换为dereksfunc
。
运行一下。导航到模拟器中的Casino
选项卡,让老虎机旋转几圈。现在你将一直获得🕶,因为arc4random_uniform
返回7
。即使是"Print a random number to console"按钮也将始终返回7
。
(lldb) image dump sections lolz.dylib
Sections for '/tmp/lolz.dylib' (x86_64):
SectID Type Load Address Perm File Off. File Size Flags Section Name
---------- ---------------- --------------------------------------- ---- ---------- ---------- ---------- ----------------------------
0x00000100 container [0x000000010ee79000-0x000000010ee7a000) r-x 0x00000000 0x00001000 0x00000000 lolz.dylib.__TEXT
0x00000001 code [0x000000010ee79fa0-0x000000010ee79fab) r-x 0x00000fa0 0x0000000b 0x80000400 lolz.dylib.__TEXT.__text
0x00000002 compact-unwind [0x000000010ee79fac-0x000000010ee79ff4) r-x 0x00000fac 0x00000048 0x00000000 lolz.dylib.__TEXT.__unwind_info
0x00000200 container [0x000000010ee7a000-0x000000010ee7b000) rw- 0x00001000 0x00001000 0x00000000 lolz.dylib.__DATA
0x00000003 regular [0x000000010ee7a000-0x000000010ee7a010) rw- 0x00001000 0x00000010 0x00000000 lolz.dylib.__DATA.__interpose
0x00000300 container [0x000000010ee7b000-0x000000010ee80000) r-- 0x00002000 0x00004890 0x00000000 lolz.dylib.__LINKEDIT
我们看到了大小足够放下两个函数指针的0x00000010
。我们用x/2gx
来查看一下0x000000010ee7a000
。
x/2gx 0x000000010ee7a000
0x10ee7a000: 0x000000010ee79fa0 0x00007fff522f38b9