本文涉及的CrackMe是看雪IOS安全小组招募的开门题目。题目提供了一个IOS应用,要求给出该应用的注册机。作为开门题目,其控制逻辑比较简单,为了实现注册机,主要的工作量在于其注册校验算法的逆向。
招募帖地址为http://bbs.pediy.com/thread-212736.htm
CrackMe程序下载地址为(需要注册论坛账号才能下载)http://bbs.pediy.com/attach-download-108214.htm
本文通过逆向这个IOS应用探索了Fat Mach-O文件结构,介绍了手动将ARM AArch64汇编代码反编译为C语言伪码的过程,该应用的注册校验算法则不做分析,有兴趣的读者可自行完成。
原题提供的是一个ZIP压缩包,下载压缩并解压后得到一个Crack_Me.ipa文件。IPA文件和Android APK打包机制类似,实际上也是一个ZIP压缩包,其中包含了应用的二进制可执行文件、应用描述文件、静态资源等。
从AppStore下载的IOS应用是被Apple签名并加密的,不能直接被反汇编,而这个CrackMe没有通过AppStore发布,也就没有被加密,这给我节省了些许时间。
IOS应用以及OS X应用的二进制可执行文件是Fat Mach-O或者Mach-O格式。Mach-O是和Linux ELF或者Windows PE类似的存储二进制可执行代码的文件格式。
Mach-O文件一般由Mach-O Header、包含应用可执行代码的__TEXT段(Segment)、包含应用数据的__DATA段、用于ASLR和动态链接的__LINKEDIT段以及加载这些段的Load Commands组成,__TEXT段以及__DATA段还包含多个不同用途的节(Section)。而Fat Mach-O则可将多个支持不同CPU架构的Mach-O文件内嵌在同一文件中。
如果读者有OS X环境,MachOView/otool/class-dump是不错的选择。更好的选择是IDA Pro,IDA Pro 6.9支持将ARM AArch64汇编指令反编译到C语言伪码。
然而笔者作为一个买不起IDA Pro以及MAC又不想使用盗版软件的穷屌,选择了Linux + GNU Binutils。需要注意的是2.27版本之前的Binutils并不支持Mach-O-AArch64,我的Linux发行版上的Binutils版本是2.24,所以让我们下载最新版本并编译安装:
$ wget http://ftp.gnu.org/gnu/binutils/binutils-2.28.tar.gz
$ tar xvfzp binutils-2.28.tar.gz
$ cd binutils-2.28/
$ ./configure --target=aarch64-apple-darwin
$ make
$ sudo make install
$ aarch64-apple-darwin-objdump -v
GNU objdump (GNU Binutils) 2.28
Copyright (C) 2017 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) any later version.
This program has absolutely no warranty.
$ aarch64-apple-darwin-objdump ./Crack_Me -P header
In archive ./Crack_Me:
armv5te: file format mach-o-arm
Mach-O header:
magic : feedface
cputype : 0000000c (arm)
cpusubtype: 00000009
filetype : 00000002 (execute)
ncmds : 00000016 (22)
sizeofcmds: 00000a14 (2580)
flags : 00200085 (noundefs+dyldlink+twolevel+pie)
reserved : 00000000
aarch64: file format mach-o-arm64
Mach-O header:
magic : feedfacf
cputype : 0100000c (arm64)
cpusubtype: 00000000
filetype : 00000002 (execute)
ncmds : 00000016 (22)
sizeofcmds: 00000be0 (3040)
flags : 00200085 (noundefs+dyldlink+twolevel+pie)
reserved : 00000000
通过文件的头部信息可以知道这是一个Fat Mach-O文件,包含适用于ARMv5te以及AArch64两个架构的可执行代码。Fat Mach-O文件中包含的多个目标架构的二进制可执行代码从应用功能上来说是等价的,在进行逆向分析时可以任选一个架构的代码进行分析。
考虑32位ARM指令集使用多年,相关资料已经相当完备了,而ARMv8 AArch64架构的中文资料还相当匮乏,本文中笔者则选择了AArch64架构的目标代码进行分析,是为抛砖引玉。
为了对AArch64部分进一步分析,需要查看文件的Fat Header以确定该部分在文件中的偏移。
Fat Header的结构定义如下:
#define FAT_MAGIC 0xcafebabe
#define FAT_CIGAM 0xbebafeca
struct fat_header {
unsigned long magic; /* FAT_MAGIC */
unsigned long nfat_arch; /* number of structs that follow */
};
struct fat_arch {
cpu_type_t cputype; /* cpu specifier (int) */
cpu_subtype_t cpusubtype; /* machine specifier (int) */
unsigned long offset; /* file offset to this object file */
unsigned long size; /* size of this object file */
unsigned long align; /* alignment as a power of 2 */
};
使用十六进制编辑器打开Crack_Me,Fat Header在文件头:
由于Fat Header的魔数与上面定义的FAT_MAGIC相同,表明Fat Header是大字节序,如果魔数为FAT_CIGAM,则Fat Header为小字节序。
结合以上结构定义我们知道ARMv5te部分在文件中的偏移为0x00000400,而AArch64部分在文件中的偏移为0x00020000。0x00020000处即为AArch64部分的Mach-O Header,64位Mach-O Header的结构定义如下(请注意,32位Mach-O Header的魔数和结构与64位Mach-O Header的不同):
#define MACH_O_MH_MAGIC_64 0xfeedfacf
#define MACH_O_MH_CIGAM_64 0xcffaedfe
struct mach_o_header_64
{
unsigned char magic[4]; /* Magic number. */
unsigned char cputype[4]; /* CPU that this object is for. */
unsigned char cpusubtype[4]; /* CPU subtype. */
unsigned char filetype[4]; /* Type of file. */
unsigned char ncmds[4]; /* Number of load commands. */
unsigned char sizeofcmds[4]; /* Total size of load commands. */
unsigned char flags[4]; /* Flags for special featues. */
unsigned char reserved[4]; /* Reserved. Duh. */
};
根据Mach-O Header的魔数,AArch64部分的代码为小字节序,而在64位Mach-o Header之后有22(0x00000016)个Load Commands。
打印所有的Load Commands:
$ aarch64-apple-darwin-objdump ./Crack_Me -P load >> crack_me_load_cmds
AArch64部分的Load Commands如下:
aarch64: file format mach-o-arm64
Load command #0 (size: 72, offset: 32): segment_64
name: __PAGEZERO nsects: 0 flags: 0 initprot: --- maxprot: ---
vmaddr: 0000000000000000 vmsize: 0000000100000000
fileoff: 0000000000000000 filesize: 0000000000000000 endoff: 0000000000000000
Load command #1 (size: 792, offset: 104): segment_64
name: __TEXT nsects: 9 flags: 0 initprot: r-x maxprot: r-x
vmaddr: 0000000100000000 vmsize: 000000000000c000
fileoff: 0000000000000000 filesize: 000000000000c000 endoff: 000000000000c000
Section: __text __TEXT (bfdname: .text)
addr: 000000010000775c size: 0000000000002b84 offset: 000000000000775c
align: 2 nreloc: 0 reloff: 0000000000000000
flags: 80000400 (type: regular attr: pure_instructions+some_instructions)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __stubs __TEXT (bfdname: __TEXT.__stubs)
addr: 000000010000a2e0 size: 0000000000000270 offset: 000000000000a2e0
align: 1 nreloc: 0 reloff: 0000000000000000
flags: 80000408 (type: symbol_stubs attr: pure_instructions+some_instructions)
first indirect sym: 0 (52 entries) stub size: 12 reserved3: 0x0
Section: __stub_helper __TEXT (bfdname: __TEXT.__stub_helper)
addr: 000000010000a550 size: 0000000000000288 offset: 000000000000a550
align: 2 nreloc: 0 reloff: 0000000000000000
flags: 80000400 (type: regular attr: pure_instructions+some_instructions)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __objc_methname __TEXT (bfdname: __TEXT.__objc_methname)
addr: 000000010000a7d8 size: 0000000000000be2 offset: 000000000000a7d8
align: 0 nreloc: 0 reloff: 0000000000000000
flags: 00000002 (type: cstring_literals attr: -)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __cstring __TEXT (bfdname: .cstring)
addr: 000000010000b3ba size: 00000000000002d5 offset: 000000000000b3ba
align: 0 nreloc: 0 reloff: 0000000000000000
flags: 00000002 (type: cstring_literals attr: -)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __objc_classname __TEXT (bfdname: __TEXT.__objc_classname)
addr: 000000010000b68f size: 0000000000000067 offset: 000000000000b68f
align: 0 nreloc: 0 reloff: 0000000000000000
flags: 00000002 (type: cstring_literals attr: -)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __objc_methtype __TEXT (bfdname: __TEXT.__objc_methtype)
addr: 000000010000b6f6 size: 000000000000081a offset: 000000000000b6f6
align: 0 nreloc: 0 reloff: 0000000000000000
flags: 00000002 (type: cstring_literals attr: -)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __const __TEXT (bfdname: .const)
addr: 000000010000bf10 size: 0000000000000018 offset: 000000000000bf10
align: 3 nreloc: 0 reloff: 0000000000000000
flags: 00000000 (type: regular attr: -)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __unwind_info __TEXT (bfdname: __TEXT.__unwind_info)
addr: 000000010000bf28 size: 00000000000000d4 offset: 000000000000bf28
align: 2 nreloc: 0 reloff: 0000000000000000
flags: 00000000 (type: regular attr: -)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Load command #2 (size: 1352, offset: 896): segment_64
name: __DATA nsects: 16 flags: 0 initprot: rw- maxprot: rw-
vmaddr: 000000010000c000 vmsize: 0000000000004000
fileoff: 000000000000c000 filesize: 0000000000004000 endoff: 0000000000010000
Section: __got __DATA (bfdname: __DATA.__got)
addr: 000000010000c000 size: 0000000000000080 offset: 000000000000c000
align: 3 nreloc: 0 reloff: 0000000000000000
flags: 00000006 (type: non_lazy_symbol_pointers attr: -)
first indirect sym: 52 (16 entries) reserved2: 0x0 reserved3: 0x0
Section: __la_symbol_ptr __DATA (bfdname: __DATA.__la_symbol_ptr)
addr: 000000010000c080 size: 00000000000001a0 offset: 000000000000c080
align: 3 nreloc: 0 reloff: 0000000000000000
flags: 00000007 (type: lazy_symbol_pointers attr: -)
first indirect sym: 68 (52 entries) reserved2: 0x0 reserved3: 0x0
Section: __cfstring __DATA (bfdname: .cfstring)
addr: 000000010000c220 size: 0000000000000060 offset: 000000000000c220
align: 3 nreloc: 0 reloff: 0000000000000000
flags: 00000000 (type: regular attr: -)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __objc_classlist __DATA (bfdname: __DATA.__objc_classlist)
addr: 000000010000c280 size: 0000000000000010 offset: 000000000000c280
align: 3 nreloc: 0 reloff: 0000000000000000
flags: 10000000 (type: regular attr: no_dead_strip)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __objc_nlclslist __DATA (bfdname: __DATA.__objc_nlclslist)
addr: 000000010000c290 size: 0000000000000008 offset: 000000000000c290
align: 3 nreloc: 0 reloff: 0000000000000000
flags: 10000000 (type: regular attr: no_dead_strip)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __objc_protolist __DATA (bfdname: __DATA.__objc_protolist)
addr: 000000010000c298 size: 0000000000000018 offset: 000000000000c298
align: 3 nreloc: 0 reloff: 0000000000000000
flags: 00000000 (type: regular attr: -)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __objc_imageinfo __DATA (bfdname: __DATA.__objc_imageinfo)
addr: 000000010000c2b0 size: 0000000000000008 offset: 000000000000c2b0
align: 2 nreloc: 0 reloff: 0000000000000000
flags: 00000000 (type: regular attr: -)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __objc_const __DATA (bfdname: __DATA.__objc_const)
addr: 000000010000c2b8 size: 0000000000000db8 offset: 000000000000c2b8
align: 3 nreloc: 0 reloff: 0000000000000000
flags: 00000000 (type: regular attr: -)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __objc_selrefs __DATA (bfdname: __DATA.__objc_selrefs)
addr: 000000010000d070 size: 00000000000000d8 offset: 000000000000d070
align: 3 nreloc: 0 reloff: 0000000000000000
flags: 10000005 (type: literal_pointers attr: no_dead_strip)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __objc_protorefs __DATA (bfdname: __DATA.__objc_protorefs)
addr: 000000010000d148 size: 0000000000000008 offset: 000000000000d148
align: 3 nreloc: 0 reloff: 0000000000000000
flags: 00000000 (type: regular attr: -)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __objc_classrefs __DATA (bfdname: __DATA.__objc_classrefs)
addr: 000000010000d150 size: 0000000000000010 offset: 000000000000d150
align: 3 nreloc: 0 reloff: 0000000000000000
flags: 10000000 (type: regular attr: no_dead_strip)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __objc_superrefs __DATA (bfdname: __DATA.__objc_superrefs)
addr: 000000010000d160 size: 0000000000000008 offset: 000000000000d160
align: 3 nreloc: 0 reloff: 0000000000000000
flags: 10000000 (type: regular attr: no_dead_strip)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __objc_ivar __DATA (bfdname: __DATA.__objc_ivar)
addr: 000000010000d168 size: 000000000000000c offset: 000000000000d168
align: 2 nreloc: 0 reloff: 0000000000000000
flags: 00000000 (type: regular attr: -)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __objc_data __DATA (bfdname: __DATA.__objc_data)
addr: 000000010000d178 size: 00000000000000f0 offset: 000000000000d178
align: 3 nreloc: 0 reloff: 0000000000000000
flags: 00000000 (type: regular attr: -)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __data __DATA (bfdname: .data)
addr: 000000010000d268 size: 0000000000000148 offset: 000000000000d268
align: 3 nreloc: 0 reloff: 0000000000000000
flags: 00000000 (type: regular attr: -)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Section: __bss __DATA (bfdname: .bss)
addr: 000000010000d3b0 size: 0000000000000178 offset: 0000000000000000
align: 3 nreloc: 0 reloff: 0000000000000000
flags: 00000001 (type: zerofill attr: -)
reserved1: 0x0 reserved2: 0x0 reserved3: 0x0
Load command #3 (size: 72, offset: 2248): segment_64
name: __LINKEDIT nsects: 0 flags: 0 initprot: r-- maxprot: r--
vmaddr: 0000000100010000 vmsize: 0000000000008000
fileoff: 0000000000010000 filesize: 0000000000007a90 endoff: 0000000000017a90
Load command #4 (size: 48, offset: 2320): dyld_info
rebase: off: 0x00010000 size: 240 (endoff: 0x000100f0)
bind: off: 0x000100f0 size: 720 (endoff: 0x000103c0)
weak bind: off: 0x00000000 size: 0 (endoff: 0x00000000)
lazy bind: off: 0x000103c0 size: 1296 (endoff: 0x000108d0)
export: off: 0x000108d0 size: 168 (endoff: 0x00010978)
Load command #5 (size: 24, offset: 2368): symtab
symoff: 0x000109d0 nsyms: 464 (endoff: 0x000126d0)
stroff: 0x000128b0 strsize: 10600 (endoff: 0x00015218)
Load command #6 (size: 80, offset: 2392): dysymtab
local symbols: idx: 0 num: 387 (nxtidx: 387)
external symbols: idx: 387 num: 6 (nxtidx: 393)
undefined symbols: idx: 393 num: 71 (nxtidx: 464)
table of content: off: 0x00000000 num: 0 (endoff: 0x00000000)
module table: off: 0x00000000 num: 0 (endoff: 0x00000000)
external reference table: off: 0x00000000 num: 0 (endoff: 0x00000000)
indirect symbol table: off: 0x000126d0 num: 120 (endoff: 0x000128b0)
external relocation table: off: 0x00000000 num: 0 (endoff: 0x00000000)
local relocation table: off: 0x00000000 num: 0 (endoff: 0x00000000)
Load command #7 (size: 32, offset: 2472): load_dylinker
/usr/lib/dyld
Load command #8 (size: 24, offset: 2504): uuid
db 2a 50 9a 4e 08 38 a4 bc d5 46 24 ab 54 93 8c
Load command #9 (size: 16, offset: 2528): version_min_iphoneos
8.0.0
Load command #10 (size: 16, offset: 2544): source_version
version a.b.c.d.e: 0.0.0.0.0
Load command #11 (size: 24, offset: 2560): main
entry offset: 0x00000000000088bc
stack size: 0x0000000000000000
Load command #12 (size: 24, offset: 2584): encryption_info_64
Load command #13 (size: 88, offset: 2608): load_dylib
name: /System/Library/Frameworks/Foundation.framework/Foundation
time stamp: 0x00000002
current version: 0x04da0c00
comptibility version: 0x012c0000
Load command #14 (size: 56, offset: 2696): load_dylib
name: /usr/lib/libobjc.A.dylib
time stamp: 0x00000002
current version: 0x00e40000
comptibility version: 0x00010000
Load command #15 (size: 56, offset: 2752): load_dylib
name: /usr/lib/libSystem.B.dylib
time stamp: 0x00000002
current version: 0x04ca0a01
comptibility version: 0x00010000
Load command #16 (size: 96, offset: 2808): load_dylib
name: /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
time stamp: 0x00000002
current version: 0x04da0d00
comptibility version: 0x00960000
Load command #17 (size: 80, offset: 2904): load_dylib
name: /System/Library/Frameworks/UIKit.framework/UIKit
time stamp: 0x00000002
current version: 0x0db81e0e
comptibility version: 0x00010000
Load command #18 (size: 40, offset: 2984): rpath
@executable_path/Frameworks
Load command #19 (size: 16, offset: 3024): function_starts
dataoff: 0x00010978 datasize: 0x00000058 (endoff: 0x000109d0)
Load command #20 (size: 16, offset: 3040): data_in_code
dataoff: 0x000109d0 datasize: 0x00000000 (endoff: 0x000109d0)
Load command #21 (size: 16, offset: 3056): code_signature
dataoff: 0x00015220 datasize: 0x00002870 (endoff: 0x00017a90)
从Load Command #1中,可知__TEXT段的__text节将从偏移0x0000775c处加载到VMA 0x10000775c,该节的长度为0x00002b84。
请注意,__text节的偏移地址0x0000775c实际上是__text节相对于Mach-O Header的偏移,而不是在整个文件中的偏移。
上节分析得到了__text节的起始地址0x10000775c和结束地址0x10000a2e0(0x10000775c+0x2b84),__text节存储着应用的可执行代码。下面对__text进行反汇编:
$ aarch64-apple-darwin-objdump ./Crack_Me -d --start-address=0x10000775c --stop-address=0x10000a2e0 --no-show-raw-insn >> crack_me_text_dis
反汇编结果有两千多汇编指令,此处不全部列出。简单的浏览一下函数名,读者很快就会猜出该应用的核心函数为ViewController check:,熟悉Objective C的读者还能猜到check:函数是ViewController类的一个方法。该函数的汇编指令如下:
00000001000077fc <-[ViewController check:]>:
1000077fc: stp x28, x27, [sp, #-32]!
100007800: stp x29, x30, [sp, #16]
100007804: add x29, sp, #0x10
100007808: sub sp, sp, #0x250
10000780c: add x8, sp, #0x80
100007810: mov x9, #0x0 // #0
100007814: adrp x10, 10000c000 <__ZL17_load_method_type+0xff>
100007818: ldr x10, [x10, #8]
10000781c: ldr x10, [x10]
100007820: mov x3, x10
100007824: stur x10, [x29, #-24]
100007828: str x0, [sp, #144]
10000782c: str x1, [sp, #136]
100007830: str x9, [sp, #128]
100007834: mov x0, x8
100007838: mov x1, x2
10000783c: str x3, [sp, #88]
100007840: bl 10000a40c <__ZL20fixStringForCoreDataP11objc_object+0x1f8>
100007844: adrp x8, 10000d000 <__ZL27OBJC_CLASS_RO_$___ARCLite__+0x40>
100007848: add x8, x8, #0x80
10000784c: adrp x9, 10000d000 <__ZL27OBJC_CLASS_RO_$___ARCLite__+0x40>
100007850: add x9, x9, #0x168
100007854: mov w11, #0x0 // #0
100007858: mov x2, #0x140 // #320
10000785c: add x10, sp, #0xc8
100007860: orr x0, xzr, #0x40
100007864: sub x1, x29, #0x58
100007868: str x0, [sp, #80]
10000786c: mov x0, x1
100007870: uxtb w1, w11
100007874: ldr x3, [sp, #80]
100007878: str x2, [sp, #72]
10000787c: mov x2, x3
100007880: str x8, [sp, #64]
100007884: str x9, [sp, #56]
100007888: str w11, [sp, #52]
10000788c: str x10, [sp, #40]
100007890: bl 10000a4d8 <__ZL20fixStringForCoreDataP11objc_object+0x2c4>
100007894: ldr x8, [sp, #40]
100007898: mov x0, x8
10000789c: ldr w11, [sp, #52]
1000078a0: uxtb w1, w11
1000078a4: ldr x2, [sp, #72]
1000078a8: bl 10000a4d8 <__ZL20fixStringForCoreDataP11objc_object+0x2c4>
1000078ac: ldr x8, [sp, #144]
1000078b0: ldr x9, [sp, #56]
1000078b4: ldrsw x10, [x9]
1000078b8: add x8, x8, x10
1000078bc: ldr x8, [x8]
1000078c0: ldr x10, [sp, #64]
1000078c4: ldr x1, [x10]
1000078c8: mov x0, x8
1000078cc: bl 10000a3c4 <__ZL20fixStringForCoreDataP11objc_object+0x1b0>
1000078d0: mov x29, x29
1000078d4: bl 10000a400 <__ZL20fixStringForCoreDataP11objc_object+0x1ec>
1000078d8: mov x1, x0
1000078dc: str x0, [sp, #32]
1000078e0: mov x0, x1
1000078e4: bl 10000a3f4 <__ZL20fixStringForCoreDataP11objc_object+0x1e0>
1000078e8: adrp x8, 10000d000 <__ZL27OBJC_CLASS_RO_$___ARCLite__+0x40>
1000078ec: add x8, x8, #0x88
1000078f0: ldr x1, [x8]
1000078f4: bl 10000a3c4 <__ZL20fixStringForCoreDataP11objc_object+0x1b0>
1000078f8: ldr x1, [sp, #32]
1000078fc: str x0, [sp, #24]
100007900: mov x0, x1
100007904: bl 10000a3e8 <__ZL20fixStringForCoreDataP11objc_object+0x1d4>
100007908: adrp x8, 10000d000 <__ZL27OBJC_CLASS_RO_$___ARCLite__+0x40>
10000790c: add x8, x8, #0x80
100007910: adrp x9, 10000d000 <__ZL27OBJC_CLASS_RO_$___ARCLite__+0x40>
100007914: add x9, x9, #0x16c
100007918: ldr x10, [sp, #24]
10000791c: str x10, [sp, #120]
100007920: ldr x0, [sp, #144]
100007924: ldrsw x9, [x9]
100007928: add x9, x0, x9
10000792c: ldr x9, [x9]
100007930: ldr x1, [x8]
100007934: mov x0, x9
100007938: bl 10000a3c4 <__ZL20fixStringForCoreDataP11objc_object+0x1b0>
10000793c: mov x29, x29
100007940: bl 10000a400 <__ZL20fixStringForCoreDataP11objc_object+0x1ec>
100007944: mov x1, x0
100007948: str x0, [sp, #16]
10000794c: mov x0, x1
100007950: bl 10000a3f4 <__ZL20fixStringForCoreDataP11objc_object+0x1e0>
100007954: adrp x8, 10000d000 <__ZL27OBJC_CLASS_RO_$___ARCLite__+0x40>
100007958: add x8, x8, #0x88
10000795c: ldr x1, [x8]
100007960: bl 10000a3c4 <__ZL20fixStringForCoreDataP11objc_object+0x1b0>
100007964: ldr x1, [sp, #16]
100007968: str x0, [sp, #8]
10000796c: mov x0, x1
100007970: bl 10000a3e8 <__ZL20fixStringForCoreDataP11objc_object+0x1d4>
100007974: ldr x8, [sp, #8]
100007978: str x8, [sp, #112]
10000797c: ldr x0, [sp, #112]
100007980: bl 10000a520 <__ZL20fixStringForCoreDataP11objc_object+0x30c>
100007984: cmp x0, #0x8
100007988: b.ne 1000079ac <-[ViewController check:]+0x1b0> // b.any
10000798c: ldr x0, [sp, #120]
100007990: bl 10000a520 <__ZL20fixStringForCoreDataP11objc_object+0x30c>
100007994: cmp x0, #0x8
100007998: b.cc 1000079ac <-[ViewController check:]+0x1b0> // b.lo, b.ul, b.last
10000799c: ldr x0, [sp, #120]
1000079a0: bl 10000a520 <__ZL20fixStringForCoreDataP11objc_object+0x30c>
1000079a4: cmp x0, #0x14
1000079a8: b.ls 1000079b8 <-[ViewController check:]+0x1bc> // b.plast
1000079ac: orr w8, wzr, #0x1
1000079b0: str w8, [sp, #108]
1000079b4: b 100007b10 <-[ViewController check:]+0x314>
1000079b8: ldr x0, [sp, #120]
1000079bc: bl 100007b48 <_changehead>
1000079c0: sub x1, x29, #0x58
1000079c4: ldr x0, [sp, #120]
1000079c8: bl 100007dfc <_get_input>
1000079cc: add x1, sp, #0xc8
1000079d0: sub x0, x29, #0x58
1000079d4: bl 100007f44 <_get_extend_input>
1000079d8: add x1, sp, #0x9f
1000079dc: add x0, sp, #0xc8
1000079e0: bl 100008068 <_get_result>
1000079e4: ldrb w8, [sp, #163]
1000079e8: ldr x0, [sp, #112]
1000079ec: ldrsb w9, [x0]
1000079f0: cmp w8, w9
1000079f4: b.ne 100007b0c <-[ViewController check:]+0x310> // b.any
1000079f8: ldrb w8, [sp, #167]
1000079fc: ldr x9, [sp, #112]
100007a00: ldrsb w10, [x9, #1]
100007a04: cmp w8, w10
100007a08: b.ne 100007b0c <-[ViewController check:]+0x310> // b.any
100007a0c: ldrb w8, [sp, #174]
100007a10: ldr x9, [sp, #112]
100007a14: ldrsb w10, [x9, #2]
100007a18: cmp w8, w10
100007a1c: b.ne 100007b0c <-[ViewController check:]+0x310> // b.any
100007a20: ldrb w8, [sp, #178]
100007a24: ldr x9, [sp, #112]
100007a28: ldrsb w10, [x9, #3]
100007a2c: cmp w8, w10
100007a30: b.ne 100007b0c <-[ViewController check:]+0x310> // b.any
100007a34: ldrb w8, [sp, #182]
100007a38: ldr x9, [sp, #112]
100007a3c: ldrsb w10, [x9, #4]
100007a40: cmp w8, w10
100007a44: b.ne 100007b0c <-[ViewController check:]+0x310> // b.any
100007a48: ldrb w8, [sp, #186]
100007a4c: ldr x9, [sp, #112]
100007a50: ldrsb w10, [x9, #5]
100007a54: cmp w8, w10
100007a58: b.ne 100007b0c <-[ViewController check:]+0x310> // b.any
100007a5c: ldrb w8, [sp, #190]
100007a60: ldr x9, [sp, #112]
100007a64: ldrsb w10, [x9, #6]
100007a68: cmp w8, w10
100007a6c: b.ne 100007b0c <-[ViewController check:]+0x310> // b.any
100007a70: ldrb w8, [sp, #197]
100007a74: ldr x9, [sp, #112]
100007a78: ldrsb w10, [x9, #7]
100007a7c: cmp w8, w10
100007a80: b.ne 100007b0c <-[ViewController check:]+0x310> // b.any
100007a84: adrp x8, 10000d000 <__ZL27OBJC_CLASS_RO_$___ARCLite__+0x40>
100007a88: add x8, x8, #0x90
100007a8c: adrp x9, 10000d000 <__ZL27OBJC_CLASS_RO_$___ARCLite__+0x40>
100007a90: add x9, x9, #0x150
100007a94: ldr x9, [x9]
100007a98: ldr x1, [x8]
100007a9c: mov x0, x9
100007aa0: bl 10000a3c4 <__ZL20fixStringForCoreDataP11objc_object+0x1b0>
100007aa4: adrp x8, 10000d000 <__ZL27OBJC_CLASS_RO_$___ARCLite__+0x40>
100007aa8: add x8, x8, #0x98
100007aac: ldr x9, [sp, #144]
100007ab0: ldr x1, [x8]
100007ab4: adrp x2, 10000c000 <__ZL17_load_method_type+0xff>
100007ab8: add x2, x2, #0x220
100007abc: adrp x3, 10000c000 <__ZL17_load_method_type+0xff>
100007ac0: add x3, x3, #0x240
100007ac4: adrp x5, 10000c000 <__ZL17_load_method_type+0xff>
100007ac8: add x5, x5, #0x260
100007acc: mov x8, #0x0 // #0
100007ad0: mov x4, x9
100007ad4: mov x6, x8
100007ad8: bl 10000a3c4 <__ZL20fixStringForCoreDataP11objc_object+0x1b0>
100007adc: adrp x8, 10000d000 <__ZL27OBJC_CLASS_RO_$___ARCLite__+0x40>
100007ae0: add x8, x8, #0xa0
100007ae4: str x0, [sp, #96]
100007ae8: ldr x9, [sp, #96]
100007aec: ldr x1, [x8]
100007af0: mov x0, x9
100007af4: bl 10000a3c4 <__ZL20fixStringForCoreDataP11objc_object+0x1b0>
100007af8: mov x8, #0x0 // #0
100007afc: add x9, sp, #0x60
100007b00: mov x0, x9
100007b04: mov x1, x8
100007b08: bl 10000a40c <__ZL20fixStringForCoreDataP11objc_object+0x1f8>
100007b0c: str wzr, [sp, #108]
100007b10: add x0, sp, #0x80
100007b14: mov x1, #0x0 // #0
100007b18: bl 10000a40c <__ZL20fixStringForCoreDataP11objc_object+0x1f8>
100007b1c: adrp x8, 10000c000 <__ZL17_load_method_type+0xff>
100007b20: ldr x8, [x8, #8]
100007b24: ldr x8, [x8]
100007b28: ldur x9, [x29, #-24]
100007b2c: cmp x8, x9
100007b30: b.ne 100007b44 <-[ViewController check:]+0x348> // b.any
100007b34: sub sp, x29, #0x10
100007b38: ldp x29, x30, [sp, #16]
100007b3c: ldp x28, x27, [sp], #32
100007b40: ret
100007b44: bl 10000a454 <__ZL20fixStringForCoreDataP11objc_object+0x240>
虽然有了以上的汇编代码,然而objdump对于程序中动态链接以及静态链接到非__text节的地址基本没有做任何到符号名的转换(谁让咱只用的起免费的开源软件呢),非常的难以分析。首先让我们把这些地址解析为可读的符号名。
Mach-O文件中__TEXT段的__stubs节以及__DATA段的__got节用来处理动态链接,代码中需要动态链接的地址都在__stubs节或者__got节。
其中__stubs节是按需绑定(lazy binding),__stubs节在绑定发生之前保存着一段跳转代码,在绑定之后则被dyld_stub_binder改写为被绑定的符号地址。而__got节是立即绑定(non lazy binding),在dyld加载mach-o文件之后__got节中即保存着被绑定的符号地址。
如果不考虑按需绑定的细节,可以认为__stubs节和__got节中保存着符号的地址。
下面两个例子解释了如何将动态链接地址解析到符号名。
分析下面这段汇编代码:
100007814: adrp x10, 10000c000
100007818: ldr x10, [x10, #8]
10000781c: ldr x10, [x10]
翻译为C语言伪码:
x10 = 0x10000c000;
x10 = *((void *)(x10 + 8));
x10 = * x10;
等价于:
x10 = **((void *)0x10000c008);
通过上面<0x03 0x03>小节中的load commands我们知道0x10000c000-0x10000c080是__got节,0x10000c008正是该节的一部分。
objdump提供了dump动态链接符号表的功能:
$ aarch64-apple-darwin-objdump ./Crack_Me -P dysymtab >> crack_me_dysymtab
查看动态链接符号表有如下内容:
for section __DATA.__got:
...
000000010000c008 53: 0x00000195 ___stack_chk_guard
...
即地址0x10000c008保存着符号___stack_chk_guard的地址,故有:
x10 = *___stack_chk_guard
分析如下代码:
100007840: bl 10000a40c
这是一个函数调用,先让我们来看一下0x10000a40c对应的符号:
for section __TEXT.__stubs:
...
000000010000a40c 25: 0x000001c0 _objc_storeStrong
...
知道了符号名,我们就能找到该函数的原型:
void objc_storeStrong(id *object, id value);
由函数原型知道该函数有两个参数,没有返回值,函数原型信息可以帮助我们进行反编译,对于上面的汇编代码
100007840: bl 10000a40c
现在有了对应的符号名字和函数原型,可以翻译为对应的C语言伪码:
_objc_storeStrong(x0, x1)
上面伪码中x0, x1是ARM64寄存器名字。ARM寄存器x0-x7是AAPCS(Procedure Call Standard for ARM 64-bit Architecture,即AArch64过程调用规范)定义的参数传递寄存器。
根据AAPCS我们知道:
1、函数参数按照从左至右的顺序依次存放在x0-x7中,如果函数参数超过8个或者函数使用可变参数(如sprintf)则按从左至右的顺序将参数压栈;
2、如果函数有返回值,则x0中保存着函数的返回值
3、函数的参数个数以及返回值是由函数原型决定的,调用者在函数调用之前就知道这些信息,所以调用者在函数调用前可以正确的将参数填充至x0-x7和/或压栈,也可以在函数返回后正确的读取x0来获取返回值;
掌握了以上的规则后,我们能够将check:函数中的动态链接地址解析为可读符号名。结合汇编指令以及地址对应的符号名,我们将其翻译为相应的C语言伪码,详细如下:
100007814: adrp x10, 10000c000
100007818: ldr x10, [x10, #8]
10000781c: ldr x10, [x10]
x10 = *___stack_chk_guard;
100007840: bl 10000a40c
_objc_storeStrong(x0, x1);
100007890: bl 10000a4d8
_memset(x0, x1, x2);
1000078a8: bl 10000a4d8
_memset(x0, x1, x2);
1000078cc: bl 10000a3c4
x0 = _objc_msgSend(x0, x1);
1000078d4: bl 10000a400
x0 = _objc_retainAutoreleasedReturnValue(x0);
1000078e4: bl 10000a3f4
_objc_retainAutorelease(x0);
1000078f4: bl 10000a3c4
x0 = _objc_msgSend(x0, x1);
100007904: bl 10000a3e8
_objc_release(x0);
100007938: bl 10000a3c4
x0 = _objc_msgSend(x0, x1);
100007940: bl 10000a400
x0 = _objc_retainAutoreleasedReturnValue(x0);
100007950: bl 10000a3f4
_objc_retainAutorelease(x0);
100007960: bl 10000a3c4
x0 = _objc_msgSend(x0, x1);
100007970: bl 10000a3e8
_objc_release(x0);
100007980: bl 10000a520
x0 = _strlen(x0);
100007990: bl 10000a520
x0 = _strlen(x0);
1000079a0: bl 10000a520
x0 = _strlen(x0);
100007aa0: bl 10000a3c4
x0 = _objc_msgSend(x0, x1);
100007ad8: bl 10000a3c4
x0 = _objc_msgSend(x0, x1, x2, x3, x4, x5, x6);
100007af4: bl 10000a3c4
x0 = _objc_msgSend(x0, x1);
100007b08: bl 10000a40c
_objc_storeStrong(x0, x1);
100007b18: bl 10000a40c
_objc_storeStrong(x0, x1);
100007b1c: adrp x8, 10000c000
100007b20: ldr x8, [x8, #8]
100007b24: ldr x8, [x8]
x8 = *___stack_chk_guard;
100007b44: bl 10000a454
___stack_chk_fail();
程序中静态链接到__text节的地址(即函数入口地址)可以被objdump正确的解析为符号名,然而objdump对于链接到其他节或者__DATA段的地址解析并无意义。特别是对于Objective C相关的节,objdump没有任何支持。为了能正确的解析这些地址,笔者用Python开发了一个objc_class_dump工具,代码托管在github。
Objective C的class数据在Mach-O文件中以层次结构分散存储在不同的节中,如下图:
objc_class_dump可以分析Mach-O文件中的class结构并导出,同时也支持导出以下几个节的内容:
__cfstring
__objc_selrefs
__objc_classrefs
__objc_superrefs
__objc_ivar
有了objc_class_dump的支持,我们就能将程序中的静态链接地址解析为可读的符号名:
100007844: adrp x8, 10000d000
100007848: add x8, x8, #0x80
#__objc_selrefs -> __objc_methname -> "text"
x8 = &"text";
10000784c: adrp x9, 10000d000
100007850: add x9, x9, #0x168
#local symbol
x9 = _OBJC_IVAR_$_ViewController._name;
1000078e8: adrp x8, 10000d000
1000078ec: add x8, x8, #0x88
#__objc_selrefs -> __objc_methname -> "UTF8String"
x8 = &"UTF8String";
100007908: adrp x8, 10000d000
10000790c: add x8, x8, #0x80
#__objc_selrefs -> __objc_methname -> "text"
x8 = &"text";
100007910: adrp x9, 10000d000
100007914: add x9, x9, #0x16c
#local symbol
x9 = _OBJC_IVAR_$_ViewController._serialNumber;
100007954: adrp x8, 10000d000
100007958: add x8, x8, #0x88
#__objc_selrefs -> __objc_methname -> "UTF8String"
x8 = &"UTF8String";
100007a84: adrp x8, 10000d000
100007a88: add x8, x8, #0x90
#__objc_selrefs -> __objc_methname -> "alloc"
x8 = &"alloc";
100007a8c: adrp x9, 10000d000
100007a90: add x9, x9, #0x150
#__objc_classrefs -> imported symbol _OBJC_CLASS_$_UIAlertView
x9 = &_OBJC_CLASS_$_UIAlertView;
100007aa4: adrp x8, 10000d000
100007aa8: add x8, x8, #0x98
#__objc_selrefs -> __objc_methname -> "initWithTitle..."
x8 = &"initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:";
100007ab4: adrp x2, 10000c000
100007ab8: add x2, x2, #0x220
#__cfstring -> __cstring -> "Congratulations"
x2 = &CFString("Congratulations");
100007abc: adrp x3, 10000c000
100007ac0: add x3, x3, #0x240
#__cfstring -> __cstring -> "Welcome to Kanxue"
x3 = &CFString("Welcome to Kanxue");
100007ac4: adrp x5, 10000c000
100007ac8: add x5, x5, #0x260
#__cfstring -> __cstring -> "OK"
x5 = &CFString("OK");
100007adc: adrp x8, 10000d000
100007ae0: add x8, x8, #0xa0
#__objc_selrefs -> __objc_methname -> "show"
x8 = &"show";
上节中我们借助objc_class_dump工具可以获得Objective C class结构,check:函数作为ViewController的方法,其类型为”V24@0:8@16”。
“V”表明方法的返回值是void,”24”表明方法的参数共占用24字节,”@0”表明第一个参数类型是Objective C object(即Objective C隐含的self参数),”:8”表明第二个参数为method selector,“@16”表明第三个参数是一个Objective C object。check:的原型可推定如下:
void check:(struct ViewController *self, SEL selector, id value);
根据AAPCS,ARM使用满减栈,即SP指向栈顶,栈向低地址方向增长。分析下面代码:
1000077fc: stp x28, x27, [sp, #-32]!
100007800: stp x29, x30, [sp, #16]
100007804: add x29, sp, #0x10
100007808: sub sp, sp, #0x250
第一条指令将x27, x28压栈,SP增长32,栈帧变为
0x00 : stack bottom
-0x08 :
-0x10 :
-0x18 : x27
-0x20 : x28 <-SP
第二条指令将x30, x29压入栈底(SP不变),其中x30是ARM的连接寄存器(LR),保存着函数的返回地址,而x29是ARM的栈帧指针,其中保存着调用者的栈帧指针,执行该指令后栈帧变为
0x00 : stack bottom
-0x08 : LR
-0x10 : FP
-0x18 : x27
-0x20 : x28 <-SP
第三条指令修改FP,修改完成后FP保存着本函数的栈帧指针,执行该指令后栈帧变为:
0x00 : stack bottom
-0x08 : LR
-0x10 : FP <-FP
-0x18 : x27
-0x20 : x28 <-SP
第四条指令将栈帧扩大0x250字节(向低地址方向增长0x250字节),执行该指令后栈帧变为:
0x00 : stack bottom
-0x08 : LR
-0x10 : FP <-FP
-0x18 : x27
-0x20 : x28
...
-0x270 : <-SP
除去已压栈的x27,x28占用的空间,可用栈空间为0x250,将用来保存函数的局部变量。
局部变量在Mach-O文件中没有对应的符号名,汇编指令直接使用栈地址进行栈空间的存取,为了便于分析我们这里对汇编指令中引用的栈地址添加助记符,助记符相当于对应的局部变量名。
对于如下汇编指令:
10000780c: add x8, sp, #0x80
我们对SP+0x80添加助记符“local_var_n”,local_var_n可以被看作SP+0x80处保存的变量,而SP+0x80是local_var_n的地址,按此规则则有:
x8 = &loal_var_n;
本文中助记符的命名的规则为”var_x”+地址相对于FP的偏移(16进制表示),这样助记符不仅方便阅读分析,还能反映其在栈中的位置信息。
对于check:函数的栈帧,SP = FP - 0x250,SP+0x80则相当于FP-0x1D0,按照命名规则生成的助记符为“var_x1D0”,则有:
x8 = &var_x1D0;
继续分析汇编代码会发现,有些指令中引用的栈地址使用FP寄存器以及相对于FP的偏移来表达,有些指令则使用SP寄存器以及相对于SP的偏移来表达,这里都统一将被引用的地址转换为相对于FP的偏移并按上面提到的命名规则生成助记符。
见下面两个例子:
100007824: stur x10, [x29, #-24]
*(FP - 24) = x10;
*(FP - 0x18) = x10;
var_x18 = x10;
100007828: str x0, [sp, #144]
*(SP + 144) = x0;
*(SP + 0x90) = x0;
*(FP - 0x250 + 0x90) = x0;
*(FP - 0x1C0) = x0;
var_x1C0 = x0;
对于给定的助记附,目前仅知道其在栈帧中的地址,然而这个地址占用多少空间还不能明确。为了解决这个问题,每增加一个新地址助记符,我们就将该助记符填入栈帧的对应地址,当栈帧最后被助记符填满时我们就能知道每个助记符所占的空间大小。根据上一节的分析,当前栈帧内容如下:
0x00 : stack bottom
-0x08 : LR
-0x10 : FP <-FP
-0x18 : x27
-0x20 : x28
-0x28 : var_x18
-0x1D0 : var_x1C0
-0x1E0 : var_x1D0
-0x270 : <-SP
<0x04 0x03>节中我们从导出的Objective C的class结构中推导出check:函数的原型,我们知道该函数有三个参数,结合AAPCS规则,x0,x1,x2中分别保存着三个参数。
上节中已经分析了如下指令:
100007828: str x0, [sp, #144]
var_x1C0 = x0;
由于x0中保存在Objective C的self指针,则有
100007828: str x0, [sp, #144]
var_x1C0 = self;
接下来是selector和id两个参数的处理:
10000782c: str x1, [sp, #136]
var_x1C8 = selector;
...
100007838: mov x1, x2
x1 = x2;
x1 = value;
以上分析可知check:函数的第一个参数self被保存在var_x1C0中,第二个参数selector被保存在var_x1C8中,而保存在x2中的第三个参数value被移入x1,作为另一次函数调用的参数,下一节分析value如何被传递给另外一次函数调用。
在前面的分析中,check:有这样一个函数调用
100007840: bl 10000a40c
_objc_storeStrong(x0, x1);
为了明确x0,x1的内容,我们开始回溯代码:
...
10000780c: add x8, sp, #0x80
x8 = &var_x1D0;
100007810: mov x9, #0x0 // #0
x9 = 0;
...
100007830: str x9, [sp, #128]
var_x1D0 = x9 = 0;
100007834: mov x0, x8
x0 = x8 = &var_x1D0;
100007838: mov x1, x2
x1 = x2 = value;
...
100007840: bl 10000a40c
_objc_storeStrong(x0, x1);
通过代码回溯,我们可以将寄存器名字从这次函数调用中消去,得到如下的伪码:
var_x1D0 = 0;
_objc_storeStrong(&var_x1D0, value);
对check:的汇编代码运用<0x05 0x01>至<0x05 0x04>几个小结中提到的方法逐行进行翻译得到如下结果:
00000001000077fc <-[ViewController check:]>:
void check:(struct ViewController *self, SEL selector, id value)
1000077fc: stp x28, x27, [sp, #-32]!
100007800: stp x29, x30, [sp, #16]
100007804: add x29, sp, #0x10
100007808: sub sp, sp, #0x250
//Prepare stack frame
10000780c: add x8, sp, #0x80
x8 = &var_x1D0;
100007810: mov x9, #0x0 // #0
x9 = 0;
100007814: adrp x10, 10000c000
100007818: ldr x10, [x10, #8]
10000781c: ldr x10, [x10]
x10 = *___stack_chk_guard;
100007820: mov x3, x10
x3 = x10 = *___stack_chk_guard;
100007824: stur x10, [x29, #-24]
var_x18 = x10 = *___stack_chk_guard;
100007828: str x0, [sp, #144]
var_x1C0 = x0 = self;
10000782c: str x1, [sp, #136]
var_x1C8 = x1 = selector;
100007830: str x9, [sp, #128]
var_x1D0 = x9 = 0;
100007834: mov x0, x8
x0 = x8 = &var_x1D0;
100007838: mov x1, x2
x1 = x2 = value;
10000783c: str x3, [sp, #88]
var_x1F8 = x3 = *___stack_chk_guard
100007840: bl 10000a40c
_objc_storeStrong(&var_x1D0, value);
100007844: adrp x8, 10000d000
100007848: add x8, x8, #0x80
x8 = &"text";//__objc_selrefs -> __objc_methname -> "text"
10000784c: adrp x9, 10000d000
100007850: add x9, x9, #0x168
x9 = _OBJC_IVAR_$_ViewController._name;//__objc_ivar
100007854: mov w11, #0x0 // #0
w11 = 0;
100007858: mov x2, #0x140 // #320
x2 = 0x140;
10000785c: add x10, sp, #0xc8
x10 = &var_x188;
100007860: orr x0, xzr, #0x40
x0 = 0 | 0x40 = 0x40;
100007864: sub x1, x29, #0x58
x1 = &var_x58;
100007868: str x0, [sp, #80]
var_x200 = x0 = 0x40;
10000786c: mov x0, x1
x0 = x1 = &var_x58;
100007870: uxtb w1, w11
w1 = (unsigned int32)((unsigned char)w11) = 0;
100007874: ldr x3, [sp, #80]
x3 = var_x200 = 0x40;
100007878: str x2, [sp, #72]
var_x208 = x2 = 0x140;
10000787c: mov x2, x3
x2 = x3 = 0x40
100007880: str x8, [sp, #64]
var_x210 = x8 = &"text";
100007884: str x9, [sp, #56]
var_x218 = x9 = _OBJC_IVAR_$_ViewController._name;
100007888: str w11, [sp, #52]
var_x21C = w11 = 0;
10000788c: str x10, [sp, #40]
var_x228 = x10 = &var_x188;
100007890: bl 10000a4d8
_memset(&var_x58, 0, 0x40);
100007894: ldr x8, [sp, #40]
x8 = var_x228
100007898: mov x0, x8
x0 = x8 = var_x228
10000789c: ldr w11, [sp, #52]
w11 = var_x21C
1000078a0: uxtb w1, w11
w1 = (unsigned int32)((unsigned char)w11) = (unsigned int32)((unsigned char)var_x21C);
1000078a4: ldr x2, [sp, #72]
x2 = var_x208;
1000078a8: bl 10000a4d8
_memset(var_x228, var_x21C, var_x208);
1000078ac: ldr x8, [sp, #144]
x8 = var_x1C0;
1000078b0: ldr x9, [sp, #56]
x9 = var_x218;
1000078b4: ldrsw x10, [x9]
x10 = *x9 = *var_x218
1000078b8: add x8, x8, x10
x8 = x8 + x10 = var_x1C0 + *var_x218
1000078bc: ldr x8, [x8]
x8 = *x8 = *(var_x1C0 + *var_x218);
1000078c0: ldr x10, [sp, #64]
x10 = var_x210;
1000078c4: ldr x1, [x10]
x1 = *x10 = *var_x210;
1000078c8: mov x0, x8
x0 = x8 = *(var_x1C0 + *var_x218);
1000078cc: bl 10000a3c4
x0 = _objc_msgSend(*(var_x1C0 + *var_x218), *var_x210);
1000078d0: mov x29, x29
FP = FP;
1000078d4: bl 10000a400
x0 = _objc_retainAutoreleasedReturnValue(_objc_msgSend(*(var_x1C0 + *var_x218), *var_x210));
1000078d8: mov x1, x0
x1 = x0;
1000078dc: str x0, [sp, #32]
var_x230 = x0 = _objc_retainAutoreleasedReturnValue(_objc_msgSend(*(var_x1C0 + *var_x218), *var_x210));
1000078e0: mov x0, x1
x0 = x1;
1000078e4: bl 10000a3f4
_objc_retainAutorelease(var_x230 = _objc_retainAutoreleasedReturnValue(_objc_msgSend(*(var_x1C0 + *var_x218), *var_x210)));
1000078e8: adrp x8, 10000d000
1000078ec: add x8, x8, #0x88
x8 = &"UTF8String";//__objc_selrefs -> __objc_methname -> "UTF8String"
1000078f0: ldr x1, [x8]
x1 = *x8 = "UTF8String"
1000078f4: bl 10000a3c4
x0 = _objc_msgSend(_objc_retainAutorelease(var_x230 = _objc_retainAutoreleasedReturnValue(_objc_msgSend(*(var_x1C0 + *var_x218), *var_x210))), "UTF8String");
1000078f8: ldr x1, [sp, #32]
x1 = var_x230;
1000078fc: str x0, [sp, #24]
var_x238 = _objc_msgSend(_objc_retainAutorelease(var_x230 = _objc_retainAutoreleasedReturnValue(_objc_msgSend(*(var_x1C0 + *var_x218), *var_x210))), "UTF8String");
100007900: mov x0, x1
x0 = x1 = var_x230;
100007904: bl 10000a3e8
_objc_release(var_x230);
100007908: adrp x8, 10000d000
10000790c: add x8, x8, #0x80
x8 = &"text";//__objc_selrefs -> __objc_methname -> "text"
100007910: adrp x9, 10000d000
100007914: add x9, x9, #0x16c
x9 = _OBJC_IVAR_$_ViewController._serialNumber;//__objc_ivar
100007918: ldr x10, [sp, #24]
x10 = var_x238;
10000791c: str x10, [sp, #120]
var_x1D8 = x10 = var_x238;
100007920: ldr x0, [sp, #144]
x0 = var_x1C0;
100007924: ldrsw x9, [x9]
x9 = *x9 = *_OBJC_IVAR_$_ViewController._serialNumber;
100007928: add x9, x0, x9
x9 = x0 + x9 = var_x1C0 + *_OBJC_IVAR_$_ViewController._serialNumber;
10000792c: ldr x9, [x9]
x9 = *x9 = *(var_x1C0 + *_OBJC_IVAR_$_ViewController._serialNumber);
100007930: ldr x1, [x8]
x1 = *x8 = "text";
100007934: mov x0, x9
x0 = x9 = *(var_x1C0 + *_OBJC_IVAR_$_ViewController._serialNumber);
100007938: bl 10000a3c4
x0 = _objc_msgSend(*(var_x1C0 + *_OBJC_IVAR_$_ViewController._serialNumber), "text");
10000793c: mov x29, x29
FP=FP;
100007940: bl 10000a400
x0 = _objc_retainAutoreleasedReturnValue(_objc_msgSend(*(var_x1C0 + *_OBJC_IVAR_$_ViewController._serialNumber), "text"));
100007944: mov x1, x0
x1 = x0 = _objc_retainAutoreleasedReturnValue(_objc_msgSend(*(var_x1C0 + *_OBJC_IVAR_$_ViewController._serialNumber), "text"));
100007948: str x0, [sp, #16]
var_x240 = x0 = _objc_retainAutoreleasedReturnValue(_objc_msgSend(*(var_x1C0 + *_OBJC_IVAR_$_ViewController._serialNumber), "text"));
10000794c: mov x0, x1
x0 = x1 = _objc_retainAutoreleasedReturnValue(_objc_msgSend(*(var_x1C0 + *_OBJC_IVAR_$_ViewController._serialNumber), "text"));
100007950: bl 10000a3f4
x0 = _objc_retainAutorelease(var_x240 = _objc_retainAutoreleasedReturnValue(_objc_msgSend(*(var_x1C0 + *_OBJC_IVAR_$_ViewController._serialNumber), "text")));
100007954: adrp x8, 10000d000
100007958: add x8, x8, #0x88
x8 = &"UTF8String";//__objc_selrefs -> __objc_methname -> "UTF8String"
10000795c: ldr x1, [x8]
x1 = *x8 = "UTF8String";
100007960: bl 10000a3c4
x0 = _objc_msgSend(_objc_retainAutorelease(var_x240 = _objc_retainAutoreleasedReturnValue(_objc_msgSend(*(var_x1C0 + *_OBJC_IVAR_$_ViewController._serialNumber), "text"))), "UTF8String");
100007964: ldr x1, [sp, #16]
x1 = var_x240;
100007968: str x0, [sp, #8]
var_x248 = _objc_msgSend(_objc_retainAutorelease(var_x240 = _objc_retainAutoreleasedReturnValue(_objc_msgSend(*(var_x1C0 + *_OBJC_IVAR_$_ViewController._serialNumber), "text"))), "UTF8String");;
10000796c: mov x0, x1
x0 = x1 = var_x240;
100007970: bl 10000a3e8
_objc_release(var_x240);
100007974: ldr x8, [sp, #8]
x8 = var_x248;
100007978: str x8, [sp, #112]
var_x1E0 = x8 = var_x248;
10000797c: ldr x0, [sp, #112]
x0 = var_x1E0;
100007980: bl 10000a520
x0 = _strlen(var_x1E0);
100007984: cmp x0, #0x8
100007988: b.ne 1000079ac <-[ViewController check:]+0x1b0> // b.any
if (_strlen(var_x1E0) != 0x08)
goto lable_x1b0;
10000798c: ldr x0, [sp, #120]
x0 = var_x1D8;
100007990: bl 10000a520
x0 = _strlen(var_x1D8);
100007994: cmp x0, #0x8
100007998: b.cc 1000079ac <-[ViewController check:]+0x1b0> // b.lo, b.ul, b.last
if (_strlen(var_x1D8) < 0x08)
goto lable_x1b0;
10000799c: ldr x0, [sp, #120]
x0 = var_x1D8
1000079a0: bl 10000a520
x0 = _strlen(var_x1D8);
1000079a4: cmp x0, #0x14
1000079a8: b.ls 1000079b8 <-[ViewController check:]+0x1bc> // b.plast
if (_strlen(var_x1D8) <= 0x14)
goto lable_x1bc:
lable_x1b0:
1000079ac: orr w8, wzr, #0x1
w8 = 1;
1000079b0: str w8, [sp, #108]
var_x1E4 = w8 = 1;
1000079b4: b 100007b10 <-[ViewController check:]+0x314>
goto lable_x314;
lable_x1bc:
1000079b8: ldr x0, [sp, #120]
x0 = var_x1D8;
1000079bc: bl 100007b48 <_changehead>
_changehead(var_x1D8);
1000079c0: sub x1, x29, #0x58
x1 = &var_x58;
1000079c4: ldr x0, [sp, #120]
x0 = var_x1D8;
1000079c8: bl 100007dfc <_get_input>
_get_input(var_x1D8, &var_x58);
1000079cc: add x1, sp, #0xc8
x1 = &var_x188;
1000079d0: sub x0, x29, #0x58
x0 = &var_x58;
1000079d4: bl 100007f44 <_get_extend_input>
get_extend_input(&var_x58, &var_x188);
1000079d8: add x1, sp, #0x9f
x1 = &var_x1B1;
1000079dc: add x0, sp, #0xc8
x0 = &var_x188;
1000079e0: bl 100008068 <_get_result>
_get_result(&var_x188, &var_1B1);
1000079e4: ldrb w8, [sp, #163]
w8 = (unsigned char)var_x1AD;
1000079e8: ldr x0, [sp, #112]
x0 = var_x1E0;
1000079ec: ldrsb w9, [x0]
w9 = (char)*x0 = (char)*var_x1E0;
1000079f0: cmp w8, w9
1000079f4: b.ne 100007b0c <-[ViewController check:]+0x310> // b.any
if ((unsigned char)var_x1AD != (char)*var_x1E0)
goto lable_x310;
1000079f8: ldrb w8, [sp, #167]
w8 = (unsigned char)var_x1A9;
1000079fc: ldr x9, [sp, #112]
x9 = var_x1E0;
100007a00: ldrsb w10, [x9, #1]
w10 = (char)*(x9 + 1) = (char)*(var_x1E0 + 1);
100007a04: cmp w8, w10
100007a08: b.ne 100007b0c <-[ViewController check:]+0x310> // b.any
if ((unsigned char)var_x1A9 != (char)*(var_x1E0 + 1))
goto lable_x310;
100007a0c: ldrb w8, [sp, #174]
w8 = (unsigned char)var_x1A2;
100007a10: ldr x9, [sp, #112]
x9 = var_x1E0;
100007a14: ldrsb w10, [x9, #2]
w10 = (char)*(var_x1E0 + 2);
100007a18: cmp w8, w10
100007a1c: b.ne 100007b0c <-[ViewController check:]+0x310> // b.any
if ((unsigned char)var_x1A2 != (char)*(var_x1E0 + 2))
goto lable_x310;
100007a20: ldrb w8, [sp, #178]
w8 = (unsigned char)var_x19E;
100007a24: ldr x9, [sp, #112]
x9 = var_x1E0;
100007a28: ldrsb w10, [x9, #3]
w10 = (char)*(var_x1E0 + 3);
100007a2c: cmp w8, w10
100007a30: b.ne 100007b0c <-[ViewController check:]+0x310> // b.any
if ((unsigned char)var_x19E != (char)*(var_x1E0 + 3))
goto lable_x310;
100007a34: ldrb w8, [sp, #182]
w8 = (unsigned char)var_x19A;
100007a38: ldr x9, [sp, #112]
x9 = var_x1E0;
100007a3c: ldrsb w10, [x9, #4]
w10 = (char)*(var_x1E0 + 4);
100007a40: cmp w8, w10
100007a44: b.ne 100007b0c <-[ViewController check:]+0x310> // b.any
if ((unsigned char)var_x19A != (char)*(var_x1E0 + 4))
goto lable_x310;
100007a48: ldrb w8, [sp, #186]
w8 = (unsigned char)var_x196;
100007a4c: ldr x9, [sp, #112]
x9 = var_x1E0;
100007a50: ldrsb w10, [x9, #5]
w10 = (char)*(var_x1E0 + 5);
100007a54: cmp w8, w10
100007a58: b.ne 100007b0c <-[ViewController check:]+0x310> // b.any
if ((unsigned char)var_x196 != (char)*(var_x1E0 + 5))
goto lable_x310;
100007a5c: ldrb w8, [sp, #190]
w8 = (unsigned char)var_x192;
100007a60: ldr x9, [sp, #112]
x9 = var_x1E0;
100007a64: ldrsb w10, [x9, #6]
w10 = (char)*(var_x1E0 + 6);
100007a68: cmp w8, w10
100007a6c: b.ne 100007b0c <-[ViewController check:]+0x310> // b.any
if ((unsigned char)var_x192 != (char)*(var_x1E0 + 6))
goto lable_x310;
100007a70: ldrb w8, [sp, #197]
w8 = (unsigned char)var_x18B;
100007a74: ldr x9, [sp, #112]
x9 = var_x1E0;
100007a78: ldrsb w10, [x9, #7]
w10 = (char)*(var_x1E0 + 7);
100007a7c: cmp w8, w10
100007a80: b.ne 100007b0c <-[ViewController check:]+0x310> // b.any
if ((unsigned char)var_x18B != (char)*(var_x1E0 + 7))
goto lable_x310;
100007a84: adrp x8, 10000d000
100007a88: add x8, x8, #0x90
x8 = &"alloc";//__objc_selrefs -> __objc_methname -> "alloc"
100007a8c: adrp x9, 10000d000
100007a90: add x9, x9, #0x150
x9 = &_OBJC_CLASS_$_UIAlertView;//__objc_classrefs -> imported symbol _OBJC_CLASS_$_UIAlertView
100007a94: ldr x9, [x9]
x9 = *x9 = _OBJC_CLASS_$_UIAlertView;
100007a98: ldr x1, [x8]
x1 = *x8 = "alloc"
100007a9c: mov x0, x9
x0 = x9 = _OBJC_CLASS_$_UIAlertView;
100007aa0: bl 10000a3c4
x0 = _objc_msgSend(_OBJC_CLASS_$_UIAlertView, "alloc");
100007aa4: adrp x8, 10000d000
100007aa8: add x8, x8, #0x98
x8 = &"initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:";//__objc_selrefs -> __objc_methname -> "initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:"
100007aac: ldr x9, [sp, #144]
x9 = var_x1C0;
100007ab0: ldr x1, [x8]
x1 = *x8 = "initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:"
100007ab4: adrp x2, 10000c000
100007ab8: add x2, x2, #0x220
x2 = &CFString("Congratulations");//__cfstring -> __cstring -> "Congratulations"
100007abc: adrp x3, 10000c000
100007ac0: add x3, x3, #0x240
x3 = &CFString("Welcome to Kanxue");//__cfstring -> __cstring -> "Welcome to Kanxue"
100007ac4: adrp x5, 10000c000
100007ac8: add x5, x5, #0x260
x5 = &CFString("OK");//__cfstring -> __cstring -> "OK"
100007acc: mov x8, #0x0 // #0
x8 = 0;
100007ad0: mov x4, x9
x4 = x9 = var_x1C0;
100007ad4: mov x6, x8
x6 = x8 = 0;
100007ad8: bl 10000a3c4
x0 = _objc_msgSend(_objc_msgSend(_OBJC_CLASS_$_UIAlertView, "alloc"), "initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:", &CFString("Congratulations"), &CFString("Welcome to Kanxue"), var_x1C0, &CFString("OK"), 0);
100007adc: adrp x8, 10000d000
100007ae0: add x8, x8, #0xa0
x8 = &"show";//__objc_selrefs -> __objc_methname -> "show"
100007ae4: str x0, [sp, #96]
var_x1F0 = x0 = _objc_msgSend(_objc_msgSend(_OBJC_CLASS_$_UIAlertView, "alloc"), "initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:", &CFString("Congratulations"), &CFString("Welcome to Kanxue"), var_x1C0, &CFString("OK"), 0);
100007ae8: ldr x9, [sp, #96]
x9 = var_x1F0;
100007aec: ldr x1, [x8]
x1 = *x8 = "show";
100007af0: mov x0, x9
x0 = var_x1F0;
100007af4: bl 10000a3c4
x0 = _objc_msgSend(var_x1F0, "show");
100007af8: mov x8, #0x0 // #0
x8 = 0;
100007afc: add x9, sp, #0x60
x9 = &var_x1F0;
100007b00: mov x0, x9
x0 = &var_x1F0;
100007b04: mov x1, x8
x1 = 0;
100007b08: bl 10000a40c
_objc_storeStrong(&var_1F0, 0);
lable_x310:
100007b0c: str wzr, [sp, #108]
var_x1E4 = 0;
lable_x314:
100007b10: add x0, sp, #0x80
x0 = &var_x1D0;
100007b14: mov x1, #0x0 // #0
x1 = 0;
100007b18: bl 10000a40c
_objc_storeStrong(&var_x1D0, 0);
100007b1c: adrp x8, 10000c000
100007b20: ldr x8, [x8, #8]
100007b24: ldr x8, [x8]
x8 = *___stack_chk_guard;
100007b28: ldur x9, [x29, #-24]
x9 = var_x18;
100007b2c: cmp x8, x9
100007b30: b.ne 100007b44 <-[ViewController check:]+0x348> // b.any
if (*___stack_chk_guard != var_x18)
goto lable_x348;
//Clear stack frame and return
100007b34: sub sp, x29, #0x10
100007b38: ldp x29, x30, [sp, #16]
100007b3c: ldp x28, x27, [sp], #32
100007b40: ret
lable_x348:
100007b44: bl 10000a454
___stack_chk_fail();
根据以上结果从后向前回溯代码,可与将ARM寄存器名从代码中消去,得到近似C语言的伪码:
void check:(struct ViewController *self, SEL selector, id value)
{
var_x18 = *___stack_chk_guard;
var_x1C0 = self;
var_x1C8 = selector;
var_x1D0 = 0;
var_x1F8 = *___stack_chk_guard
_objc_storeStrong(&var_x1D0, value);
var_x208 = 0x140;
var_x210 = &"text";
var_x218 = _OBJC_IVAR_$_ViewController._name;
var_x21C = 0;
var_x228 = &var_x188;
_memset(&var_x58, 0, 0x40);
_memset(var_x228, var_x21C, var_x208);
var_x238 = _objc_msgSend(\
_objc_retainAutorelease(\
var_x230 = _objc_retainAutoreleasedReturnValue(\
_objc_msgSend(*(var_x1C0 + *var_x218), *var_x210)\
)\
),\
"UTF8String"\
);
_objc_release(var_x230);
var_x1D8 = var_x238;
var_x248 = _objc_msgSend(\
_objc_retainAutorelease(\
var_x240 = _objc_retainAutoreleasedReturnValue(\
_objc_msgSend(*(var_x1C0 + *_OBJC_IVAR_$_ViewController._serialNumber), "text")\
)\
),\
"UTF8String"\
);
_objc_release(var_x240);
var_x1E0 = var_x248;
if (_strlen(var_x1E0) != 0x08)
goto lable_x1b0;
if (_strlen(var_x1D8) < 0x08)
goto lable_x1b0;
if (_strlen(var_x1D8) <= 0x14)
goto lable_x1bc:
lable_x1b0:
var_x1E4 = 1;
goto lable_x314;
lable_x1bc:
_changehead(var_x1D8);
_get_input(var_x1D8, &var_x58);
_get_extend_input(&var_x58, &var_x188);
_get_result(&var_x188, &var_1B1);
if ((unsigned char)var_x1AD != (char)*var_x1E0)
goto lable_x310;
if ((unsigned char)var_x1A9 != (char)*(var_x1E0 + 1))
goto lable_x310;
if ((unsigned char)var_x1A2 != (char)*(var_x1E0 + 2))
goto lable_x310;
if ((unsigned char)var_x19E != (char)*(var_x1E0 + 3))
goto lable_x310;
if ((unsigned char)var_x19A != (char)*(var_x1E0 + 4))
goto lable_x310;
if ((unsigned char)var_x196 != (char)*(var_x1E0 + 5))
goto lable_x310;
if ((unsigned char)var_x192 != (char)*(var_x1E0 + 6))
goto lable_x310;
if ((unsigned char)var_x18B != (char)*(var_x1E0 + 7))
goto lable_x310;
var_x1F0 = _objc_msgSend(\
_objc_msgSend(_OBJC_CLASS_$_UIAlertView, "alloc"),\
"initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:",\
&CFString("Congratulations"),\
&CFString("Welcome to Kanxue"),\
var_x1C0,\
&CFString("OK"),\
0\
);
_objc_msgSend(var_x1F0, "show");
_objc_storeStrong(&var_1F0, 0);
lable_x310:
var_x1E4 = 0;
lable_x314:
_objc_storeStrong(&var_x1D0, 0);
if (*___stack_chk_guard != var_x18)
goto lable_x348;
return;
lable_x348:
___stack_chk_fail();
}
被填充的栈帧内容如下:
0x00 : stack bottom
-0x08 : LR
-0x10 : FP <-FP
-0x18 : x27
-0x20 : x28
-0x28 : var_x18 *___stack_chk_guard
-0x68 : var_x58 64b buffer hold input
-0x198 : var_x188 320B buffer hold extend input
-0x19B : var_x18B result + 38
-0x1A2 : var_x192 result + 31
-0x1A6 : var_x196 result + 27
-0x1AA : var_x19A result + 23
-0x1AE : var_x19E result + 19
-0x1B2 : var_x1A2 result + 15
-0x1B9 : var_x1A9 result + 8
-0x1BD : var_x1AD result + 4
-0x1C1 : var_x1B1 41B buffer holds result
-0x1D0 : var_x1C0 holds self
-0x1D8 : var_x1C8 holds selector
-0x1E0 : var_x1D0 holds 0
-0x1E8 : var_x1D8 holds var_x238
-0x1F0 : var_x1E0 holds var_x248
-0x1F4 : var_x1E4 holds unused flag?
-0x200 : var_x1F0 _OBJC_CLASS_$_UIAlertView handler
-0x208 : var_x1F8 *___stack_chk_guard
-0x218 : var_x208 0x140, size of extend input buffer
-0x220 : var_x210 objc method name "text"
-0x228 : var_x218 local symbol _OBJC_IVAR_$_ViewController._name
-0x22C : var_x21C 0, memset pattern
-0x238 : var_x228 points to extend input buffer
-0x240 : var_x230 text of self->_name
-0x248 : var_x238 UTF8String of var_x230
-0x250 : var_x240 text of self->_serialNumber
-0x258 : var_x248 UTF8String of var_x240
-0x270 : <-SP
至此我们有了将check:函数反编译为C语言的全部信息,有兴趣的读者可以自己尝试用C语言重新实现一下这个函数。
研究check:代码会发现该函数在栈底和栈中部分别存放了一个*___stack_check_guard标志,函数在返回时检查栈底的栈保护标志是否被改写,如果被改写则表明栈下溢。一般来说,只要不超过栈顶和栈底,函数对当前栈帧中的空间是任意使用的,不会造成不利影响,因此在栈的中部设置栈保护标志是奇怪的实现。更吊诡的是,虽然在栈中部设置了栈保护标志,但是又没有对其进行检查。
研究应用的其他函数,发现只有两个函数进行了栈保护,通常来说栈保护机制是由开发者在编译时指定是否使能,不清楚IOS开发中如何决定何时使能栈保护。
对此有研究的读者可以给我留言。
研究该应用的过程中发现有两种不同的可变参数函数参数传递规范。
当程序调用使用可变参数的传统C库函数(如sprintf)时,任然采用AAPCS定义的调用规范,即使用栈来传递可变参数。我猜测此类函数在IOS库中的实现任然采用了va_list/va_start/va_end宏来进行参数解析。
当程序调用Objective C可变参数函数时,则采用了不同的调用规范。以objc_MsgSend举例:
其原型为
void objc_msgSend(id, SEL, ...)
上面的check:函数中有多处objc_msgSend调用,我们来分析一个不使用可变参数的例子和一个使用可变参数的例子:
100007930: ldr x1, [x8]
x1 = *x8 = "text";
100007934: mov x0, x9
x0 = x9 = *(var_x1C0 + *_OBJC_IVAR_$_ViewController._serialNumber);
100007938: bl 10000a3c4
_objc_msgSend(*(var_x1C0 + *_OBJC_IVAR_$_ViewController._serialNumber), "text");
100007a84: adrp x8, 10000d000
100007a88: add x8, x8, #0x90
x8 = &"alloc";//__objc_selrefs -> __objc_methname -> "alloc"
100007a8c: adrp x9, 10000d000
100007a90: add x9, x9, #0x150
x9 = &_OBJC_CLASS_$_UIAlertView;//__objc_classrefs -> imported symbol _OBJC_CLASS_$_UIAlertView
100007a94: ldr x9, [x9]
x9 = *x9 = _OBJC_CLASS_$_UIAlertView;
100007a98: ldr x1, [x8]
x1 = *x8 = "alloc"
100007a9c: mov x0, x9
x0 = x9 = _OBJC_CLASS_$_UIAlertView;
100007aa0: bl 10000a3c4
x0 = _objc_msgSend(_OBJC_CLASS_$_UIAlertView, "alloc");
100007aa4: adrp x8, 10000d000
100007aa8: add x8, x8, #0x98
x8 = &"initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:";//__objc_selrefs -> __objc_methname -> "initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:"
100007aac: ldr x9, [sp, #144]
x9 = var_x1C0;
100007ab0: ldr x1, [x8]
x1 = *x8 = "initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:"
100007ab4: adrp x2, 10000c000
100007ab8: add x2, x2, #0x220
x2 = &CFString("Congratulations");//__cfstring -> __cstring -> "Congratulations"
100007abc: adrp x3, 10000c000
100007ac0: add x3, x3, #0x240
x3 = &CFString("Welcome to Kanxue");//__cfstring -> __cstring -> "Welcome to Kanxue"
100007ac4: adrp x5, 10000c000
100007ac8: add x5, x5, #0x260
x5 = &CFString("OK");//__cfstring -> __cstring -> "OK"
100007acc: mov x8, #0x0 // #0
x8 = 0;
100007ad0: mov x4, x9
x4 = x9 = var_x1C0;
100007ad4: mov x6, x8
x6 = x8 = 0;
100007ad8: bl 10000a3c4
x0 = _objc_msgSend(_objc_msgSend(_OBJC_CLASS_$_UIAlertView, "alloc"), "initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:", &CFString("Congratulations"), &CFString("Welcome to Kanxue"), var_x1C0, &CFString("OK"), 0);
对比这两次_objc_msgSend调用,当有可变参数时,使用第2个参数selector来传递剩余参数个数信息,可变的参数部分任然使用寄存器传递,而不是栈。
sprintf的实现在/usr/lib/libSystem.B.dylib,而objc_msgSend的实现在/usr/lib/libobjc.A.dylib,表明这两个库使用了不同的ABI。