<0x01> 前言

本文涉及的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语言伪码的过程,该应用的注册校验算法则不做分析,有兴趣的读者可自行完成。

<0x02> IOS应用初探

原题提供的是一个ZIP压缩包,下载压缩并解压后得到一个Crack_Me.ipa文件。IPA文件和Android APK打包机制类似,实际上也是一个ZIP压缩包,其中包含了应用的二进制可执行文件、应用描述文件、静态资源等。

从AppStore下载的IOS应用是被Apple签名并加密的,不能直接被反汇编,而这个CrackMe没有通过AppStore发布,也就没有被加密,这给我节省了些许时间。

<0x03> 对应用进行反汇编

<0x03 0x01> Fat Mach-O格式简介

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文件内嵌在同一文件中。

<0x03 0x02> 反汇编工具的选择

如果读者有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.

<0x03 0x03> 分析应用头部信息

$ aarch64-apple-darwin-objdump ./Crack_Me -P header
In archive ./Crack_Me:armv5te:     file format mach-o-arm
Mach-O header:magic     : feedfacecputype   : 0000000c (arm)cpusubtype: 00000009filetype  : 00000002 (execute)ncmds     : 00000016 (22)sizeofcmds: 00000a14 (2580)flags     : 00200085 (noundefs+dyldlink+twolevel+pie)reserved  : 00000000aarch64:     file format mach-o-arm64
Mach-O header:magic     : feedfacfcputype   : 0100000c (arm64)cpusubtype: 00000000filetype  : 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   0xbebafecastruct 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_64name:       __PAGEZERO  nsects: 0  flags: 0  initprot: ---  maxprot: ---vmaddr: 0000000000000000   vmsize: 0000000100000000fileoff: 0000000000000000 filesize: 0000000000000000 endoff: 0000000000000000Load command #1  (size: 792, offset:  104): segment_64name:           __TEXT  nsects: 9  flags: 0  initprot: r-x  maxprot: r-xvmaddr: 0000000100000000   vmsize: 000000000000c000fileoff: 0000000000000000 filesize: 000000000000c000 endoff: 000000000000c000Section: __text           __TEXT           (bfdname: .text)addr: 000000010000775c size: 0000000000002b84 offset: 000000000000775calign: 2  nreloc: 0  reloff: 0000000000000000flags: 80000400 (type: regular attr: pure_instructions+some_instructions)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __stubs          __TEXT           (bfdname: __TEXT.__stubs)addr: 000000010000a2e0 size: 0000000000000270 offset: 000000000000a2e0align: 1  nreloc: 0  reloff: 0000000000000000flags: 80000408 (type: symbol_stubs attr: pure_instructions+some_instructions)first indirect sym: 0 (52 entries)  stub size: 12  reserved3: 0x0Section: __stub_helper    __TEXT           (bfdname: __TEXT.__stub_helper)addr: 000000010000a550 size: 0000000000000288 offset: 000000000000a550align: 2  nreloc: 0  reloff: 0000000000000000flags: 80000400 (type: regular attr: pure_instructions+some_instructions)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __objc_methname  __TEXT           (bfdname: __TEXT.__objc_methname)addr: 000000010000a7d8 size: 0000000000000be2 offset: 000000000000a7d8align: 0  nreloc: 0  reloff: 0000000000000000flags: 00000002 (type: cstring_literals attr: -)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __cstring        __TEXT           (bfdname: .cstring)addr: 000000010000b3ba size: 00000000000002d5 offset: 000000000000b3baalign: 0  nreloc: 0  reloff: 0000000000000000flags: 00000002 (type: cstring_literals attr: -)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __objc_classname __TEXT           (bfdname: __TEXT.__objc_classname)addr: 000000010000b68f size: 0000000000000067 offset: 000000000000b68falign: 0  nreloc: 0  reloff: 0000000000000000flags: 00000002 (type: cstring_literals attr: -)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __objc_methtype  __TEXT           (bfdname: __TEXT.__objc_methtype)addr: 000000010000b6f6 size: 000000000000081a offset: 000000000000b6f6align: 0  nreloc: 0  reloff: 0000000000000000flags: 00000002 (type: cstring_literals attr: -)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __const          __TEXT           (bfdname: .const)addr: 000000010000bf10 size: 0000000000000018 offset: 000000000000bf10align: 3  nreloc: 0  reloff: 0000000000000000flags: 00000000 (type: regular attr: -)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __unwind_info    __TEXT           (bfdname: __TEXT.__unwind_info)addr: 000000010000bf28 size: 00000000000000d4 offset: 000000000000bf28align: 2  nreloc: 0  reloff: 0000000000000000flags: 00000000 (type: regular attr: -)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Load command #2  (size: 1352, offset:  896): segment_64name:           __DATA  nsects: 16  flags: 0  initprot: rw-  maxprot: rw-vmaddr: 000000010000c000   vmsize: 0000000000004000fileoff: 000000000000c000 filesize: 0000000000004000 endoff: 0000000000010000Section: __got            __DATA           (bfdname: __DATA.__got)addr: 000000010000c000 size: 0000000000000080 offset: 000000000000c000align: 3  nreloc: 0  reloff: 0000000000000000flags: 00000006 (type: non_lazy_symbol_pointers attr: -)first indirect sym: 52 (16 entries)  reserved2: 0x0  reserved3: 0x0Section: __la_symbol_ptr  __DATA           (bfdname: __DATA.__la_symbol_ptr)addr: 000000010000c080 size: 00000000000001a0 offset: 000000000000c080align: 3  nreloc: 0  reloff: 0000000000000000flags: 00000007 (type: lazy_symbol_pointers attr: -)first indirect sym: 68 (52 entries)  reserved2: 0x0  reserved3: 0x0Section: __cfstring       __DATA           (bfdname: .cfstring)addr: 000000010000c220 size: 0000000000000060 offset: 000000000000c220align: 3  nreloc: 0  reloff: 0000000000000000flags: 00000000 (type: regular attr: -)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __objc_classlist __DATA           (bfdname: __DATA.__objc_classlist)addr: 000000010000c280 size: 0000000000000010 offset: 000000000000c280align: 3  nreloc: 0  reloff: 0000000000000000flags: 10000000 (type: regular attr: no_dead_strip)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __objc_nlclslist __DATA           (bfdname: __DATA.__objc_nlclslist)addr: 000000010000c290 size: 0000000000000008 offset: 000000000000c290align: 3  nreloc: 0  reloff: 0000000000000000flags: 10000000 (type: regular attr: no_dead_strip)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __objc_protolist __DATA           (bfdname: __DATA.__objc_protolist)addr: 000000010000c298 size: 0000000000000018 offset: 000000000000c298align: 3  nreloc: 0  reloff: 0000000000000000flags: 00000000 (type: regular attr: -)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __objc_imageinfo __DATA           (bfdname: __DATA.__objc_imageinfo)addr: 000000010000c2b0 size: 0000000000000008 offset: 000000000000c2b0align: 2  nreloc: 0  reloff: 0000000000000000flags: 00000000 (type: regular attr: -)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __objc_const     __DATA           (bfdname: __DATA.__objc_const)addr: 000000010000c2b8 size: 0000000000000db8 offset: 000000000000c2b8align: 3  nreloc: 0  reloff: 0000000000000000flags: 00000000 (type: regular attr: -)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __objc_selrefs   __DATA           (bfdname: __DATA.__objc_selrefs)addr: 000000010000d070 size: 00000000000000d8 offset: 000000000000d070align: 3  nreloc: 0  reloff: 0000000000000000flags: 10000005 (type: literal_pointers attr: no_dead_strip)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __objc_protorefs __DATA           (bfdname: __DATA.__objc_protorefs)addr: 000000010000d148 size: 0000000000000008 offset: 000000000000d148align: 3  nreloc: 0  reloff: 0000000000000000flags: 00000000 (type: regular attr: -)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __objc_classrefs __DATA           (bfdname: __DATA.__objc_classrefs)addr: 000000010000d150 size: 0000000000000010 offset: 000000000000d150align: 3  nreloc: 0  reloff: 0000000000000000flags: 10000000 (type: regular attr: no_dead_strip)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __objc_superrefs __DATA           (bfdname: __DATA.__objc_superrefs)addr: 000000010000d160 size: 0000000000000008 offset: 000000000000d160align: 3  nreloc: 0  reloff: 0000000000000000flags: 10000000 (type: regular attr: no_dead_strip)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __objc_ivar      __DATA           (bfdname: __DATA.__objc_ivar)addr: 000000010000d168 size: 000000000000000c offset: 000000000000d168align: 2  nreloc: 0  reloff: 0000000000000000flags: 00000000 (type: regular attr: -)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __objc_data      __DATA           (bfdname: __DATA.__objc_data)addr: 000000010000d178 size: 00000000000000f0 offset: 000000000000d178align: 3  nreloc: 0  reloff: 0000000000000000flags: 00000000 (type: regular attr: -)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __data           __DATA           (bfdname: .data)addr: 000000010000d268 size: 0000000000000148 offset: 000000000000d268align: 3  nreloc: 0  reloff: 0000000000000000flags: 00000000 (type: regular attr: -)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Section: __bss            __DATA           (bfdname: .bss)addr: 000000010000d3b0 size: 0000000000000178 offset: 0000000000000000align: 3  nreloc: 0  reloff: 0000000000000000flags: 00000001 (type: zerofill attr: -)reserved1: 0x0  reserved2: 0x0  reserved3: 0x0Load command #3  (size:  72, offset: 2248): segment_64name:       __LINKEDIT  nsects: 0  flags: 0  initprot: r--  maxprot: r--vmaddr: 0000000100010000   vmsize: 0000000000008000fileoff: 0000000000010000 filesize: 0000000000007a90 endoff: 0000000000017a90Load command #4  (size:  48, offset: 2320): dyld_inforebase: 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): symtabsymoff: 0x000109d0    nsyms:      464  (endoff: 0x000126d0)stroff: 0x000128b0  strsize:    10600  (endoff: 0x00015218)Load command #6  (size:  80, offset: 2392): dysymtablocal 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/dyldLoad command #8  (size:  24, offset: 2504): uuiddb 2a 50 9a 4e 08 38 a4 bc d5 46 24 ab 54 93 8cLoad command #9  (size:  16, offset: 2528): version_min_iphoneos8.0.0Load command #10 (size:  16, offset: 2544): source_versionversion a.b.c.d.e: 0.0.0.0.0Load command #11 (size:  24, offset: 2560): mainentry offset: 0x00000000000088bcstack size:   0x0000000000000000Load command #12 (size:  24, offset: 2584): encryption_info_64Load command #13 (size:  88, offset: 2608): load_dylibname: /System/Library/Frameworks/Foundation.framework/Foundationtime stamp: 0x00000002current version: 0x04da0c00comptibility version: 0x012c0000Load command #14 (size:  56, offset: 2696): load_dylibname: /usr/lib/libobjc.A.dylibtime stamp: 0x00000002current version: 0x00e40000comptibility version: 0x00010000Load command #15 (size:  56, offset: 2752): load_dylibname: /usr/lib/libSystem.B.dylibtime stamp: 0x00000002current version: 0x04ca0a01comptibility version: 0x00010000Load command #16 (size:  96, offset: 2808): load_dylibname: /System/Library/Frameworks/CoreFoundation.framework/CoreFoundationtime stamp: 0x00000002current version: 0x04da0d00comptibility version: 0x00960000Load command #17 (size:  80, offset: 2904): load_dylibname: /System/Library/Frameworks/UIKit.framework/UIKittime stamp: 0x00000002current version: 0x0db81e0ecomptibility version: 0x00010000Load command #18 (size:  40, offset: 2984): rpath@executable_path/FrameworksLoad command #19 (size:  16, offset: 3024): function_startsdataoff: 0x00010978  datasize: 0x00000058  (endoff: 0x000109d0)Load command #20 (size:  16, offset: 3040): data_in_codedataoff: 0x000109d0  datasize: 0x00000000  (endoff: 0x000109d0)Load command #21 (size:  16, offset: 3056): code_signaturedataoff: 0x00015220  datasize: 0x00002870  (endoff: 0x00017a90)

从Load Command #1中,可知__TEXT段的__text节将从偏移0x0000775c处加载到VMA 0x10000775c,该节的长度为0x00002b84。
请注意,__text节的偏移地址0x0000775c实际上是__text节相对于Mach-O Header的偏移,而不是在整个文件中的偏移。

<0x03 0x04> 反汇编并阅读代码

上节分析得到了__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>

<0x04> 将汇编代码中的地址解析为符号名

虽然有了以上的汇编代码,然而objdump对于程序中动态链接以及静态链接到非__text节的地址基本没有做任何到符号名的转换(谁让咱只用的起免费的开源软件呢),非常的难以分析。首先让我们把这些地址解析为可读的符号名。

<0x04 0x01> 解析动态链接地址为符号名

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节中保存着符号的地址。
下面两个例子解释了如何将动态链接地址解析到符号名。

<0x04 0x01 0x01> __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

<0x04 0x01 0x02> __stubs节地址解析

分析如下代码:

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();

<0x04 0x02> 解析静态链接地址为符号名

程序中静态链接到__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";

<0x04 0x03> 获取check:函数的原型

上节中我们借助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);

<0x05> 对汇编代码进行反编译

<0x05 0x01> 分析check:函数的栈帧

根据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,将用来保存函数的局部变量。

<0x05 0x02> 分析check:函数的局部变量

<0x05 0x02 0x01> 为局部变量添加助记符

局部变量在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;

<0x05 0x02 0x02> 使用助记符填充栈帧

对于给定的助记附,目前仅知道其在栈帧中的地址,然而这个地址占用多少空间还不能明确。为了解决这个问题,每增加一个新地址助记符,我们就将该助记符填入栈帧的对应地址,当栈帧最后被助记符填满时我们就能知道每个助记符所占的空间大小。根据上一节的分析,当前栈帧内容如下:

 0x00    :    stack bottom
-0x08    :    LR
-0x10    :    FP            <-FP
-0x18    :    x27
-0x20    :    x28
-0x28    :    var_x18
-0x1D0   :    var_x1C0
-0x1E0   :    var_x1D0
-0x270   :                  <-SP

<0x05 0x03> 分析check:的参数处理

<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如何被传递给另外一次函数调用。

<0x05 0x04> 使用代码回溯解析check:中的函数调用

在前面的分析中,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);

<0x05 0x05> 得到反编译结果

对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_guard100007840:  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_ivar100007854:  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 = 0x40100007880:  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_x228100007898:  mov x0, x8
x0 = x8 = var_x22810000789c:  ldr w11, [sp, #52]
w11 = var_x21C1000078a0:  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_x2181000078b8:  add x8, x8, x10
x8 = x8 + x10 = var_x1C0 + *var_x2181000078bc:  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_ivar100007918:  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_x1D81000079a0:  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_$_UIAlertView100007a94:  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:  retlable_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语言重新实现一下这个函数。

<0x06> Objective C的一些有趣特性

<0x06 0x01> 栈保护

研究check:代码会发现该函数在栈底和栈中部分别存放了一个*___stack_check_guard标志,函数在返回时检查栈底的栈保护标志是否被改写,如果被改写则表明栈下溢。一般来说,只要不超过栈顶和栈底,函数对当前栈帧中的空间是任意使用的,不会造成不利影响,因此在栈的中部设置栈保护标志是奇怪的实现。更吊诡的是,虽然在栈中部设置了栈保护标志,但是又没有对其进行检查。
研究应用的其他函数,发现只有两个函数进行了栈保护,通常来说栈保护机制是由开发者在编译时指定是否使能,不清楚IOS开发中如何决定何时使能栈保护。
对此有研究的读者可以给我留言。

<0x06 0x02> 可变参数函数的参数传递

研究该应用的过程中发现有两种不同的可变参数函数参数传递规范。
当程序调用使用可变参数的传统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_$_UIAlertView100007a94:  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。

逆向一个IOS CrackMe相关推荐

  1. [转] 一个iOS开发者的修真之路

    在微信上有童鞋问我iOS开发者的入门标准是神马?这个问题难到我了,而且贸然给一个答案出来的话,必定会有万千高手来喷. 凡人修仙,仙人修道,道人修真.当我们还是一个在青石板上蹲马步汗水涔涔的废柴时,或许 ...

  2. 一个iOS开发者的修真之路

    凡人修仙,仙人修道,道人修真.当我们还是一个在青石板上蹲马步汗水涔涔的废柴时,或许天空中偶尔会有御剑飞行的仙人路过.金色的阳光洒在仙人随风舞动的剑穗上,此时不禁会油然而生一种冲动,希望有一天能成为这样 ...

  3. iOS学习:一个iOS开发者的修真之路

    在微信上有童鞋问我iOS开发者的入门标准是神马?这个问题难到我了,而且贸然给一个答案出来的话,必定会有万千高手来喷. 凡人修仙,仙人修道,道人修真.当我们还是一个在青石板上蹲马步汗水涔涔的废柴时,或许 ...

  4. IOS开发:一个iOS开发者的修真之路

    在微信上有童鞋问我iOS开发者的入门标准是神马?这个问题难到我了,而且贸然给一个答案出来的话,必定会有万千高手来喷. 凡人修仙,仙人修道,道人修真.当我们还是一个在青石板上蹲马步汗水涔涔的废柴时,或许 ...

  5. 创建第一个IOS项目

    今天我们创建第一个IOS项目,并在模拟器上运行 首先进入Xcode 点击创建新项目后,接下来该给项目起一个名字啦 HelloWorld! 点击下一步,开始选择模板啦 我们选择SingleView Ap ...

  6. iOS 11开发教程(三)运行第一个iOS 11程序

    iOS 11开发教程(三)运行第一个iOS 11程序 运行iOS11程序 创建好项目之后,就可以运行这个项目中的程序了.单击运行按钮,如果程序没有任何问题的话,会看到如图1.6和1.7的运行效果. 图 ...

  7. iOS 11开发教程(二)编写第一个iOS 11应用

    iOS 11开发教程(二)编写第一个iOS 11应用 编写第一个iOS 11应用 本节将以一个iOS 11应用程序为例,为开发者讲解如何使用Xcode 9.0去创建项目,以及iOS模拟器的一些功能.编 ...

  8. Xamarin iOS开发实战第1章使用C#编写第一个iOS应用程序

    Xamarin iOS开发实战第1章使用C#编写第一个iOS应用程序 C#原本是用来编写Windows以及Windows Phone的应用程序.自从Xamarin问世后,C#的作用就发生了很大的变化. ...

  9. 如何用 React Native 创建一个iOS APP?(二)

    我们书接上文<如何用 React Native 创建一个iOS APP?>,继续来讲如何用 React Native 创建一个iOS APP.接下来,我们会涉及到很多控件. 1 AppRe ...

最新文章

  1. tensorflow中的梯度弥散与梯度爆炸
  2. 苹果裁撤自动驾驶项目员工200余人
  3. 神策数据胡士文:数据智能驱动业务实践
  4. Java | Python 流程控制对比
  5. javascript 对象的设计模式
  6. Linux 多线程开发-线程的属性-分离detached和连接joinable
  7. zabbix通过钉钉报警
  8. java 多态 优势
  9. Django中Form组件的使用
  10. Intel彻底封杀Skylake非黑盒版超频
  11. mysql homedir迁移
  12. imreadraw的注册
  13. 一、数据库应用系统分析及规划
  14. 企业为什么要做高端网站优化呢?
  15. 安卓3d游戏开发引擎_微信小游戏开发怎么选游戏引擎
  16. vim编辑器在Linux系统中的用法
  17. 【毕业设计】基于大数据的招聘与租房分析可视化系统
  18. 2017-4-18 CCCC决赛总结
  19. 1357. 路径总和 II
  20. 苏大计算机学院在哪,苏州大学计算机技术学院导师介绍:纪其进

热门文章

  1. 考研订房攻略出炉!这是你现在能为初试做的最重要的事!
  2. ICML/ICLR‘22 推荐系统论文梳理
  3. Python结合Redis开发热销排行功能
  4. GL_INTERFACE(转载)
  5. 实物期货业务流程(期货交易业务流程)
  6. 网络***检测初步探测方法
  7. 行车必备的酒精度测试仪电路设计(测试程序、论文)
  8. 分享86个NET源码,总有一款适合您
  9. 我们常用的软件就是这样耍流氓的
  10. android无线充电器支架,iWALK新款无线充电器,不仅可以无线充电还可以做支架