<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 : 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 at