来自Unsplash
IOS的系统安全性比Android系统高有几个主要原因。
一是对应用安装源的限制,iOS 设备必须从App Store 上下载应用或者使用企业证书做分发,而Android 系统的设备可以安装任何安装包(Android Package,APK),这样会导致恶意应用可以很轻易地被安装到手机上。
二是iOS 上的应用有着严格的“沙盒”机制,每个应用都只能访问自己沙盒目录下的数据,没有公共的读写区域,而Android 系统存在公共读写区域,容易造成信息泄露。
三是iOS 应用被限制只能在前台运行,只要点击Home 键,应用的所有线程都会被挂起,只有一些必须运行在后台的服务才能被执行(如实时位置、播放音乐等),而Android 应用可以创建后台服务,即使应用被切换到后台,代码还是可以执行的,用户很难觉察到。
下面我们来详细了解一下iOS 的安全机制。
应用的安装源
App Store 是苹果的应用市场,苹果手机上使用的微信、QQ和支付宝等应用都是从App Store 上下载的。同样,如果你想开发一款应用并上架App Store,必须提交苹果公司进行审核,审核通过之后,应用才能在App Store 上被搜索、下载。除了从App Store 上下载应用外,还有其他安装应用的方法,其中一种方法是使用企业证书做分发,价格为每年299 美元。这种方法不限制安装设备的数量,但是安装完成后想要打开软件时,会出现“未受信任的企业级开发者”提示,如图1-1 所示。
图1-1 未信任的企业开发者
这时我们就需要点击“设置”→“通用”→“设备管理①”,信任安装的应用。还有一种方法是使用个人/公司证书进行开发及测试,价格为每年99 美元。这种方法首先需要将设备的UDID(Unique Device Identifier,设备唯一标识符,在8.1 节有更详细的介绍)添加到开发者账号中,最多可以添加100 台设备,然后下载配置文件,在Xcode 上添加配置文件并编译相应的程序,最后就能安装在这台设备上了。Xcode 9 有自动注册设备的功能,当我们使用Xcode 进行真机调试,连接一个新设备的时候会进行自动注册,如图1-2 所示。
图1-2 使用Xcode 9 注册设备
正是App Store 严格的审核以及应用安装源的限制,比较有效地控制了恶意程序的传播。
沙盒
沙盒(sandbox)是iOS 的一个防御机制,每个应用都会有一个自己的沙盒。应用只能在自己的沙盒目录下读写数据,应用A 不能访问应用B 的沙盒,它们之间是互相隔离的。正因如此,攻击者上传恶意程序后,即使侥幸通过了App Store 的审核,被安装到用户的手机之后也不能获取其他应用的数据。获取沙盒目录的方法如下:
-(void)getPath{ //获取沙盒根目录路径 NSString*homeDir = NSHomeDirectory(); NSLog(@"homedir: %@",homeDir); //获取Documents 目录路径 NSString*docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) firstObject]; NSLog(@"docDir: %@",docDir); //获取Library 目录路径 NSString*libDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask,YES) lastObject]; NSLog(@"libDir: %@",libDir); //获取cache 目录路径 NSString*cachesDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES) firstObject]; NSLog(@"cachesDir: %@",cachesDir); //获取tmp 目录路径 NSString*tmpDir =NSTemporaryDirectory(); NSLog(@"tmpDir: %@",tmpDir); //获取应用的自身xx.app 目录 NSBundle *bundle = [NSBundle mainBundle]; NSString *strAppPath = [bundle bundlePath]; NSLog(@"appDir: %@",strAppPath); }输出的结果如下:
homedir: /var/mobile/Containers/Data/Application/E24754D2-22F8-4E8E-8A6C-2B18561DB5AD docDir: /var/mobile/Containers/Data/Application/E24754D2-22F8-4E8E-8A6C-2B18561DB5AD/Documents libDir: /var/mobile/Containers/Data/Application/E24754D2-22F8-4E8E-8A6C-2B18561DB5AD/Library cachesDir: /var/mobile/Containers/Data/Application/E24754D2-22F8-4E8E-8A6C-2B18561DB5AD/Library/Caches tmpDir: /private/var/mobile/Containers/Data/Application/E24754D2-22F8-4E8E-8A6C-2B18561DB5AD/tmp/ appDir: /private/var/mobile/Containers/Bundle/Application/A5E6DC61-7AAA-467F-BC63-5BEDB8DDB113/沙盒机制限制了应用只能读写沙盒之内的文件,而我们在有些情况下需要访问一些公共资源(如通讯录、短信、照片和位置等),这些是存在沙盒之外的。苹果提供了公开的API 去访问公共资源,不过都会弹出申请权限提示框,用户必须允许才能操作成功,比如微信访问照片提示需要用户授权,如图1-3 所示。
代码签名
代码签名(code signing)是iOS 一个重要的安全机制,所有的二进制文件都必须经过签名才能被执行。在内存中,只有签名来源为自己的页才会被执行,这样应用就不能自我升级或者动态改变行为。我们可以使用codesign 命令来验证应用的签名信息是否被破坏。输入下面的命令后,如果没有输出信息,就表示签名信息没有被破坏:
codesign --verify /此外,使用下面的命令还可以查看签名的相关信息:
$ codesign -vv -d / Executable=/Users/exchen/Library/Developer/Xcode/DerivedData/testSandbox-fzalslvcpprgiodycqxzblqeztql/ Build/Products/Debug-iphoneos//testSandbox Identifier=net.exc Format=app bundle with Mach-O thin (arm64) CodeDirectory v=20200 size=694 flags=0x0(none) hashes=14+5 location=embedded Signature size=4701 Authority=iPhone Developer: exchen99@qq.com (248BRN4CNL) Authority=Apple Worldwide Developer Relations Certification Authority Authority=Apple Root CA Signed Time=2018 年4 月8 日 01:27:40 In entries=26 TeamIdentifier=QQ4RE63T4U Sealed Resources version=2 rules=13 files=7 Internal requirements count=1 size=188用户权限隔离
iOS 使用了用户权限进行隔离。系统中的浏览器、电话、短信以及从App Store 上下载的应用,这些都以mobile 用户权限运行,只有重要的系统进程才以root 用户权限运行。这样一来,即使恶意软件安装成功,也会由于存在权限限制,而使攻击的可能性变小。我们打开“电话”应用(MobilePhone),通过ps 命令查看相关的进程信息,可以看到它是以mobile 的身份运行的:
iPhone:~ root# ps aux | grep MobilePhone mobile 23754 0.0 0.9 661736 9152 ?? Ss 1:00AM 0:01.31 /Application root 23774 0.0 0.1 537356 572 s001 S+ 1:00AM 0:00.01 grep MobilePhone再打开“短信”应用(MobileSMS),同样通过ps 命令查看相关的进程信息,会发现它也是以mobile 用户的身份运行的:
iPhone:~ root# ps aux | grep MobileSMS mobile 23764 0.1 1.2 659024 12384 ?? Rs 1:00AM 0:01.82 /Application root 23776 0.0 0.1 537356 572 s001 S+ 1:01AM 0:00.01 grep MobileSMS数据执行保护
iOS 中存在数据执行保护(Data Execution Prevention,DEP)机制,这一机制能够区分内存中哪些是可执行的代码,哪些是数据。该机制不允许执行数据,只允许执行代码。在默认情况下,数据段的属性是可读、可写、不可执行的,如果我们通过vm_protect 函数把数据段的属性修改为可读、可写、可执行,就会打印错误信息。代码如下:
unsigned int data = 0x12345678; struct mach_header* image_addr = _dyld_get_image_header(0); //获取镜像地址 vm_address_t offset = image_addr + (int)0x8000; //数据段的偏移 kern_return_t err; mach_port_t port = mach_task_self(); err = vm_protect(port, (vm_address_t) offset, sizeof(data), NO, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); if (err != KERN_SUCCESS) { NSLog(@"prot error: %s \n", mach_error_string(err)); return; } vm_write(port, (vm_address_t) offset, (vm_address_t) & data, sizeof(data));运行之后打印的错误信息为:
2018-04-08 23:59:01.680009 vm_write[6222:265002] prot error: (os/kern) protection failure地址随机化
地址空间布局随机化(Address Space Layout Randomization,ASLR)能够让二进制文件、动态库文件、代码段以及数据段的内存地址在每次加载的时候都是随机的。使用_dyld_get_image_vmaddr_slide 函数可以获取模块的ASLR 偏移地址,使用_dyld_get_image_header 函数可以获取模块的基址。我们编写如下代码:
//获取第一个模块的基址 intptr_t slide_addr = _dyld_get_image_vmaddr_slide(0); struct mach_header *mh_addr = _dyld_get_image_header(0); printf("slide_addr: 0x%x\n", slide_addr); printf("mh_addr: 0x%x\n",mh_addr);可以发现,第一次运行时,ASLR 偏移地址是0x94000,基址是0x100094000。这也可以通过LLDB(Low Level Debugger,详见2.5 节)查看:
mh_addr: 0x100094000 (lldb) image list -o -f [ 0] 0x0000000000094000 /var/containers/Bundle/Application/9B2F1512-812E-4C3A-BBA7-A15DB63FFB1F/ ge(0x0000000100094000)我们退出程序再次运行,ASLR 偏移地址是0xe8000,基址是0x1000e8000。可以看出,程序每次运行,偏移地址和基址都是随机的,一般都不会和上次一样:
slide_addr: 0xe8000 mh_addr: 0x1000e8000 (lldb) image list -o -f [ 0] 0x00000000000e8000 /var/containers/Bundle/Application/9B2F1512-812E-4C3A-BBA7-A15DB63FFB1F/ ge(0x00000001000e8000)每次重启手机之后,在内核中模块的基地址也是随机的。
后台程序
Android 应用能够在后台创建执行代码的服务,这些服务可能会偷偷下载文件消耗流量,造成扣费的情况。iOS 应用不能在后台执行代码,只要用户点击Home 键,前台应用的所有线程都会被挂起,只有播放音乐、获取实时位置等必须在后台执行的操作才能执行。不过在iOS 中,也是有后台程序的,被称为daemon(守护程序)。比如在打开Safari(浏览器)上网时,突然有电话打进来,接听电话的界面就会显示在前台,如果没有后台程序,怎么处理来电呢?只是苹果并没有给开发者开放后台程序,只有越狱之后,才能编写后台程序。
iOS 的系统进程launchd 会在系统启动后检测/System/Library/LaunchDaemons 和/Library/LaunchDaemons 这两个目录中的.plist 文件,而.plist 文件描述了daemon 程序的路径。
下面我们写一个daemon 程序进行测试,程序的名称叫作daemonTest,其功能是每隔5 秒输出一条信息,具体代码如下:
#include <; #include <UIKi; int main(){ int i=0; while(1){ NSLog(@"Daemon test %d", i); i++; sleep(5); } return 0; }编译:
clang -arch armv7 -isysroot $(xcrun --sdk iphoneos -show-sdk-path) -framework Foundation -o daemonTest main.m签名:
codesign -s - --entitlements ~ -f daemonTest签名需要用到的ent.plist 文件的内容请参考2.5.1 节。然后我们编写用于描述daemonTest的.plist 文件,文件名称为net.exc,具体内容如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" ";> <plist version="1.0"> <dict> <key>KeepAlive</key> <true/> <key>Label</key> <string>net.exc;/string> <key>ProgramArguments</key> <array> <string>/usr/bin/daemonTest</string> </array> <key>RunAtLoad</key> <true/> <key>SessionCreate</key> <true/> <key>StandardErrorPath</key> <string>/dev/null</string> <key>inetdCompatibility</key> <dict> <key>Wait</key> <false/> </dict> </dict> </plist>将daemonTest 上传到/usr/bin 目录,再将net.exc 文件上传到/Library/LaunchDaemons/目录,接着设置相应的权限,命令如下:
chown root:wheel /usr/bin/daemonTest chmod 755 /usr/bin/daemonTest chown root:wheel /Library/LaunchDaemons/net.exc使用launchctl load 命令启动daemon:
launchctl load /Library/LaunchDaemons/net.exc我们打开控制台查看日志,就可以看到每隔5 秒会输出一条信息,并且在锁屏状态下代码也能执行,是真正的后台程序,如图1-4 所示。
图1-4 每隔5 秒输出信息
使用launchctl unload 命令可以停止daemon:
launchctl unload /Library/LaunchDaemons/net.exc——
如果你也对iOS安全感兴趣,下面这本“秘籍”一定会帮到你,《黑客防线》技术月刊原总编辑孙彬评价说,这是一本“拿到就能上手找饭吃”的iOS安全技术书。
本书主要讲了什么?
本书是一套完整的iOS安全知识体系。跟着本书能循序渐进、系统性地学习iOS 安全技术。
作者立足系统,着眼安全,涉及的内容不仅有iOS系统的安全机制解析、越狱及后期的开发、代码的注入与关键点拦截、隐私信息的类别及获取方法、应用的破解与保护,还揭密了刷单及刷量会用到的一些伎俩,同时包括一些基础知识的简明教程。
——
小编已试过水,整本书内容整理下来,逻辑主线清晰,语言通俗,感觉要是有点相关的开发经验,现在能立马转iOS安全工程师了。
截取自拉勾网
我能不能读这本书?
本书整体的深度和广度都相对适中,可以帮助对iOS系统安全有兴趣的广大研究人员从入门到进阶。
潜读其中,会让读者对业内一些常见的安全相关的技术手段有清晰、直观的了解,心领神会后暗道一声原来如此!
试读:刷量与作弊
封面新闻中报道:【全国累计有900多万人从事刷流量产业】近日有专家指出,各类刷量平台在我国已超过1000家,刷量产业的人员规模累计达到900多万。从地下某个刷量平台订单信息来看,在整个行业中,被刷最多的数据依次是:文章阅读量、电商评价和其他。刷量需求者的地域性比较明显,主要分布在互联网经济发达的沿海地区。其中,广东、北京、山东三地的刷量人员规模遥遥领先。
那App市场是怎么刷量和作弊的呢?点击阅读原文跳转到图灵社区阅读。