Lake逆向之路
前提
无意中看到了一个很有意思的APP,传说是苹果2017设计奖。很多地方的控件都不知道如何实现的。于是有了想逆向能不能查看一些思路。
比如:
这个下面的选色控件:支持横向滚动改变颜色,纵向滚动改变明度。点击滑动到中间区域,还有各种动画。
咋眼一看感觉好像不是很难,但是在实际实现中遇到了很多困难。
于是开始了Lake
的逆向之旅。
越狱
工具
Electra官网:点此连接
越狱工具下载地址:点此连接(IPA文件)
越狱工具安装工具:点此连接(Cydia Impactor)
注意IPA版本。最新版本支持为iOS 11.2 – 11.3.1。我是11.1.2 在历史下载的Electra for 11.0-11.1.2
教程
1、下载Cydia Impactor后,解压运行“Impactor”。把之前下载好的IPA文件“Electra1131 ”拖进cydia Impactor里面(过程没有变化提示,会自动弹出输入苹果账号框);
2、然后根据提示输入自己的Apple ID账号与密码(需关闭双重验证,部分账号无法关闭双重验证,可以改用其他账号);
3、桌面出现名为 Electra 的App,打开“设置->通用->描述文件”找到描述文件,点击信任;
4、将iOS调至飞行模式,运行Electra App,点击“Jailbreak”按钮,主屏出现Cydia之后,即可关闭飞行模式,越狱成功。(实际好像不用飞行模式)
5、之后可以在桌面的出现Cydia
,越狱成功。
砸壳
最早的砸壳工具是stefanesser写的dumpdecrypted,通过手动注入然后启动应用程序在内存进行dump解密后的内存实现砸壳,这种砸壳只能砸主App可执行文件。
对于应用程序里面存在framework的情况可以使用conradev的dumpdecrypted,通过_dyld_register_func_for_add_image注册回调对每个模块进行dump解密。
但是这种还是需要拷贝dumpdecrypted.dylib,然后找路径什么的,还是挺麻烦的。所以笔者干脆放到MonkeyDev模板变成一个tweak的形式dumpdecrypted,这样填写目标bundle id然后看日志把文件拷贝出来就可以了。
但是还是很麻烦,需要拷贝文件自己还原ipa,然后有了KJCracks的Clutch通过posix_spawnp创建进程然后dump直接生成ipa包在设备,可以说是很方便了。这个是工具在使用的时候大部分应用会出报错,此外生成的包还需要自己拷贝。
准备工具
frida
安装Mac安装frida:
sudo pip install frida
手机安装frida:添加源
build.frida.re
,并且安装iproxy
安装可以查看之前一篇文章:iproxy-通过USB使用SSH连接iOS设备
frida-ios-dump
在
Github
根据README.md
下载并安装:Github地址。
过程
mac端在克隆frida-ios-dump
下运行
1 | sudo pip install -r requirements.txt --upgrade |
Mac终端通过USB连接iOS设备。
新建一个
iTerm
窗口输入iproxy 2222 22
,会得到waiting for connection
的反馈。说明等待连接了。然后再新建一个
iTerm
窗口输入ssh -p 2222 root@127.0.0.1
并填写默认密码alpine
。(建议使用passwd
命令输入2次新密码后修改ssh密码。)
如果修改端口,或者密码需要在
dump.py
中23行到36行修改为你修改的信息。
到此为止,已经成功连接上了设备,开始准备一键砸壳,感谢alonemonkey
大神带来了毫无体验之旅。
- 一键砸壳
回到步骤1那个窗口输入./dump.py -l
1 | 912 App Store com.apple.AppStore |
因为 Display name
又是中文,又是英文,又是空格的,个人建议是通过Bundle identifier
,这样准确点。
然后开始砸壳吧~~输入./dump.py com.lake.coloring
,其中的Bundle identifier
你是通过上面查到的。于是开始屌屌的砸壳过程中。
等待自动砸壳传输完成之后便会到当前目录生成一个解密后的ipa文件。砸壳之旅就这样结束了。
这个时候赶紧拖到MonkeyDev开始逆向之旅吧!
逆向
MonkeyDev安装
环境
使用工具前确保如下几点:
- 安装最新的theos
1 | sudo git clone --recursive https://github.com/theos/theos.git /opt/theos |
- 安装ldid(如安装theos过程安装了ldid,跳过)
1 | brew install ldid |
- 配置免密码登录越狱设备(如果没有越狱设备,跳过)
1 | ssh-keygen -t rsa -P '' |
或者安装sshpass
自己设置密码:
1 | brew install https://raw.githubusercontent.com/kadwanev/bigboybrew/master/Library/Formula/sshpass.rb |
安装
你可以通过以下命令选择指定的Xcode进行安装:
1 | sudo xcode-select -s /Applications/Xcode-beta.app |
默认安装的Xcode为:
1 | xcode-select -p |
执行安装命令:
1 | sudo /bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/AloneMonkey/MonkeyDev/master/bin/md-install)" |
卸载
1 | sudo /bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/AloneMonkey/MonkeyDev/master/bin/md-uninstall)" |
逆向学习
0 0 经过第一天的尝试,成功在本机运行上了lake
,并且添加了很多调试工具,也获class-dump
取到了头文件,类名,方法等。通过之前写的一个开源调试工具 BMDebu,很方便获可以获取到显示页面的视图层级,控制器,堆,各种类名等。然后。。。。就这样破解了内购~~~
觉得非常有兴趣,并且之前因为苹果相对于安卓生态来说更加的封闭,没有足够的重视应用安全这部分。既然这么轻松的lake
破解了,那应用的安全就引起了我的重视。于是买了一本《iOS应用逆向与安全》,准备系统的学习一下。(后面内容不会以lake
为例,毕竟商业项目,会选取一个小的商业App)
概述
流程
- 解密、导出应用程序、
class-dump
导出头文件。 - 从页面表现入手,获取当前界面布局及控制器。
hook
发现的一些相关类,记录输出调用顺序及参数- 找到关键函数,查看调用堆栈,
hook
测试效果。 - 静态分析加动态调试分析关键函数的实现逻辑。
- 模拟活或篡改函数调用逻辑。
- 制作插件,或者一直到非越狱机器。
学习逆向优点
- 促进正向开发,深入理解系统原理。
- 借鉴别人的设计和实现,实现自己的功能。
- 分析恶意软件,应用安全审计。
- 从逆向的角度实现安全保护
应用保护手段
数据加密
应用中一般都会储存很多敏感数据,例如加密和解密的key
、用户隐私信息,通讯传输的信息。针对数据加密,可以从下面3个方向入手:
- 静态字符串加密 (增加静态分析难度)
- 本地储存加密 (避免重要信息泄露)
- 网络传输加密 (避免中间人工具)
程序混淆
在程序设计中,一个类名和方法名定义往往是有具体含义并且一米了然,这反而非常有利于攻击者分析出该类,该方法的具体用途。也不可能为了避免攻击乱取类名。所以把类名和方法名替换成无意义的随机字符串。实施的方法一般是通过LLVM
在编译过程中对中间代码进行控制流程的混淆,代码扁平化,插入干扰代码等操作。
安全监测
为了保证应用运行时的安全,可以在应用中假如反调试代码,监测调试器,或者假如动态库注入监测、函数hook
监测、签名监测、完整性监测等,使得当监测到应用被侵入时,退出程序或者功能运行出现异常。
工具
效率
ITerm2
:代替默认的Terminal
,提供了很多高级设置,比如自动代码不全、高亮。oh-my-zsh
:可以自定义主题、git
显示、Tab补全等。Go2Shell
:从Finder
打开终端并自动切换到当前目录。autojump
:从终端快速进行目录跳转和切换
效率工具
Cakebrew
:HomeBrew
的界面管理工具。
libimobiledevice
:提供很多能与iOS交互的工具,列入端口映射查看日志。
tree
:用于查看当前目录结构树的命令行工具。
逆向工具
jtool
:查看文件结构、代码签名
iOS系统结构
文件目录
Application
:存放所有的系统App和来自Cydia
的App,不包括从App Store
下载的APP。Developer
:公开发者使用。Library
:系统资源,用户设置。列入,Logs
是系统日志,Ringtones
是系统自带铃声,Launch Daemons
是启动daemon
程序。其中比较重要的目录是/Library/MobileSubstrate
,里面存放了所有基于Cydia Substrate
的插件。System
:系统的重要组成部分。/System/Library/Carrier Bunles
里面是运营商的一些配置。/System/Library/Frameworks
和/System/Library/Private Frameworks
里面是系统中各种公开和未公开的Framework
。/System/Library/CoreServices/SpringBoard.app
是桌面管理器,是用户和系统直接交互的部分。/System/Library/PerferenceBundles
里面存放的事系统设置中的一些设置项。
User
:用户目录,实际指向/var/mobile
。/User/Media/
里面存放的是相册等。/User/Library/
里面存放的是短信、邮件等。
bin
:存放用户级二进制文件,例如mv
,ls
等。dev
:设备文件。每个设备在/dev
目录下都有一个对应的文件。etc
:存放系统监本、hosts
配置、SSH
配置文件等。知己指向/Private/etc
。sbin
:存放系统级二进制文件,例如reboot
、mount
等。usr
:用户工具和程序。/usr/include
中存放标准C头文件,/usr/lib
中存放库文件。var
:一些经常改动的文件,包括keychains
、临时文件、从App Store
下载的应用。
文件权限
在iOS系统中,每个文件都具有以下属性。
- 所有者权限:决定文件的所有者可以对文件进行的操作。
- 组权限:决定属于该组的成员对他所拥有的文件能够进行的操作。
- 其他人权限:标示其他人能够对该文件进行的操作。
iOS
系统用3个比特(bit)标示文件的操作权限,从高位到低位分别是读(r)、写(w)和执行(x)。
1 | BirdMichaelteki-iPhone:/ root# cd / |
drwxr-xr-x
即为:符号链接文件|读写执行|读-执行|读-执行
- 第1个子夫表示文件的类型,”l“是符号链接文件,”d“是文件夹,”-“是普通文件。
- 第2~4个字符表示文件所有者的权限。例如,”lrwxr-xr-x“,表示文件所有者拥有读(r)、写(w)、和执行(x)权限。
- 第5~7个字符标示文件所属组的权限。
- 读8~10个字符标示其他人的权限。
- root标示文件所属用户,
wheel
和admin
标示文件所属组。
r、w、x中的每一个都对应于二进制1或0,可以写成”111“。所以,可以用4代表读权限,用2代表写软线,用1代表执行权限。当然可以组合使用。例如”r-x”代表读和执行权限。即4(读)+1(执行)=5。
Cydia Substrate
Cydia SUbstrate
是一个框架,允许第三方开发者在越狱系统的方法里打一些运行时补丁和扩展一些方法,是越狱的基石。
Cydia Substrate
包含3个主要模块,分别是MobileHooker
、MobileLoader
、Safe mode
。
MobileHooker
主要用于替换系统和应用的方法。他提供MSHookMessageEx
来hook OC函数,提供MSHookFunction
来hook C函数。
MobileLoader
用于将第三方动态库加载到运行的目标应用。它首先通过环境变量DYLD_INSERT_LIBRARIES
吧他自己加载到目标应用里,然后插在/Library/MobileSubstrate/DynamicLibraries/
目录下的所有plist
文件,如果plist
文件里面的配置信息符合当前运行的应用,就会dlopen
函数打开对应dylib
文件。
plist
文件中有一些过滤条件,只有满足条件是,第三方动态库才会被加载。
CoreFoundationVersion
:只有CoreFoundation.framework
的版本高于某个值时才会被加载。Bundle
:只有应用的Bundle ID
在该列表中时才会被加载。Classes
:只有使用了某些特定的OC类时才会被加载。Executables
:只有应用的可执行文件的名字和该列表匹配时才会被加载。
Safe mode
在安全模式中,所有第三方插件都不会加载。
越狱必备工具
adv-cmds
在手机上经常会使用ps
命令来查看当前运行的进程ID及可执行文件的路径。
appsync
直接修改一个应用的结构或文件会破解应用本身的签名信息,所以,安装修改后的应用会出现
Failed to verify code signature of xxxxx
这样的错误。这时需要安装appsync
,让系统不在验证应用的签名。注意选择正确的版本。
App结构及构建
App结构
应用包中存在两种格式,ipa
和app
。
ipa
文件本身就是zip
压缩包。解压后主要包括如下几类文件。
Info.plist
文件:存储应用的相关设置、Bundle ideftifier
和Executable file
可执行文件名。- 可执行文件:
Info.plist
中Executable file
记录的名字所对应的文件。改文件主要用于分析。 Frameworks
:当前应用使用的第三方Framework
。PluIns
&Watch
:当前应用使用的Extesion
,以及和Watch
手表一起使用的应用。- 资源:其他文件,包括图片资源、配置文件、视频/音频资源,以及一些本地化相关的文件。
应用的编译构建过程
为了项目简单及纯粹,我们新建一个Xcode iOS App项目,然后按“Command + B”快捷编译项目,并且查看编译细节,构建过程如下。
- 编译信息写入辅助文件,创建文件架构 .app 文件
- 处理文件打包信息
- 执行 CocoaPod 编译前脚本,checkPods Manifest.lock
- 编译.m文件,使用 CompileC 和 clang 命令
- 链接需要的 Framework
- 编译 xib
- 拷贝 xib ,资源文件
- 编译 ImageAssets
- 处理 info.plist
- 执行 CocoaPod 脚本
- 拷贝标准库
- 复制
embedded.mobileprovision
:将本地描述文件复制到生成的App目录下面。 - 创建 .app 文件和签名
Clang 编译 .m 文件
我们查看编译细节中对ViewController.m
的细节了解一下。
首先对任务进行描述
1 | CompileC /Users/birdmichael/Library/Developer/Xcode/DerivedData/demoBuild-hkrzbqmagemxhgasjxvnblksqtjz/Build/Intermediates.noindex/demoBuild.build/Debug-iphoneos/demoBuild.build/Objects-normal/arm64/ViewController.o /Users/birdmichael/Downloads/demoBuild/demoBuild/ViewController.m normal arm64 objective-c com.apple.compilers.llvm.clang.1_0.compiler (in target: demoBuild) |
其中指令集说明:
- 模拟器32位处理器测试需要i386架构,
- 模拟器64位处理器测试需要x86_64架构,
- 真机32位处理器需要armv7,或者armv7s架构,
- 真机64位处理器需要arm64架构。(iPhone5S以上)
Xcode中指令集相关选项(Build Setting中):
- Architectures
指定工程被编译成可支持哪些指令集类型,而支持的指令集越多,就会编译出包含多个指令集代码的数据包,对应生成二进制包就越大,也就是ipa包会变大。 - Valid Architectures
限制可能被支持的指令集的范围,也就是Xcode编译出来的二进制包类型最终从这些类型产生,而编译出哪种指令集的包,将由Architectures与Valid Architectures(因此这个不能为空)的交集来确定 - Build Active Architecture Only
指定是否只对当前连接设备所支持的指令集编译当其值设置为YES,这个属性设置为yes,是为了debug的时候编译速度更快,它只编译当前的architecture版本,而设置为no时,会编译所有的版本。 编译出的版本是向下兼容的,连接的设备的指令集匹配是由高到低(arm64 > armv7s > armv7)依次匹配的。
更新工作路径
1 | cd /Users/birdmichael/Downloads/demoBuild |
实际的编译命令
1 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -x objective-c -arch arm64 -fmessage-length=0 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0 -std=gnu11 -fobjc-arc -fobjc-weak -fmodules -gmodules -fmodules-cache-path=/Users/birdmichael/Library/Developer/Xcode/DerivedData/ModuleCache.noindex -fmodules-prune-interval=86400 -fmodules-prune-after=345600 -fbuild-session-file=/Users/birdmichael/Library/Developer/Xcode/DerivedData/ModuleCache.noindex/Session.modulevalidation -fmodules-validate-once-per-build-session -Wnon-modular-include-in-framework-module -Werror=non-modular-include-in-framework-module -Wno-trigraphs -fpascal-strings -O0 -fno-common -Wno-missing-field-initializers -Wno-missing-prototypes -Werror=return-type -Wdocumentation -Wunreachable-code -Wno-implicit-atomic-properties -Werror=deprecated-objc-isa-usage -Wno-objc-interface-ivars -Werror=objc-root-class -Wno-arc-repeated-use-of-weak -Wimplicit-retain-self -Wduplicate-method-match -Wno-missing-braces -Wparentheses -Wswitch -Wunused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wconditional-uninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wno-float-conversion -Wnon-literal-null-conversion -Wobjc-literal-conversion -Wshorten-64-to-32 -Wpointer-sign -Wno-newline-eof -Wno-selector -Wno-strict-selector-match -Wundeclared-selector -Wdeprecated-implementations -DDEBUG=1 -DOBJC_OLD_DISPATCH_PROTOTYPES=0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.1.sdk -fstrict-aliasing -Wprotocol -Wdeprecated-declarations -miphoneos-version-min=12.1 -g -Wno-sign-conversion -Winfinite-recursion -Wcomma -Wblock-capture-autoreleasing -Wstrict-prototypes -Wno-semicolon-before-method-body -Wunguarded-availability -fembed-bitcode-marker -index-store-path /Users/birdmichael/Library/Developer/Xcode/DerivedData/demoBuild-hkrzbqmagemxhgasjxvnblksqtjz/Index/DataStore -iquote /Users/birdmichael/Library/Developer/Xcode/DerivedData/demoBuild-hkrzbqmagemxhgasjxvnblksqtjz/Build/Intermediates.noindex/demoBuild.build/Debug-iphoneos/demoBuild.build/demoBuild-generated-files.hmap -I/Users/birdmichael/Library/Developer/Xcode/DerivedData/demoBuild-hkrzbqmagemxhgasjxvnblksqtjz/Build/Intermediates.noindex/demoBuild.build/Debug-iphoneos/demoBuild.build/demoBuild-own-target-headers.hmap -I/Users/birdmichael/Library/Developer/Xcode/DerivedData/demoBuild-hkrzbqmagemxhgasjxvnblksqtjz/Build/Intermediates.noindex/demoBuild.build/Debug-iphoneos/demoBuild.build/demoBuild-all-target-headers.hmap -iquote /Users/birdmichael/Library/Developer/Xcode/DerivedData/demoBuild-hkrzbqmagemxhgasjxvnblksqtjz/Build/Intermediates.noindex/demoBuild.build/Debug-iphoneos/demoBuild.build/demoBuild-project-headers.hmap -I/Users/birdmichael/Library/Developer/Xcode/DerivedData/demoBuild-hkrzbqmagemxhgasjxvnblksqtjz/Build/Products/Debug-iphoneos/include -I/Users/birdmichael/Library/Developer/Xcode/DerivedData/demoBuild-hkrzbqmagemxhgasjxvnblksqtjz/Build/Intermediates.noindex/demoBuild.build/Debug-iphoneos/demoBuild.build/DerivedSources/arm64 -I/Users/birdmichael/Library/Developer/Xcode/DerivedData/demoBuild-hkrzbqmagemxhgasjxvnblksqtjz/Build/Intermediates.noindex/demoBuild.build/Debug-iphoneos/demoBuild.build/DerivedSources -F/Users/birdmichael/Library/Developer/Xcode/DerivedData/demoBuild-hkrzbqmagemxhgasjxvnblksqtjz/Build/Products/Debug-iphoneos -MMD -MT dependencies -MF /Users/birdmichael/Library/Developer/Xcode/DerivedData/demoBuild-hkrzbqmagemxhgasjxvnblksqtjz/Build/Intermediates.noindex/demoBuild.build/Debug-iphoneos/demoBuild.build/Objects-normal/arm64/ViewController.d --serialize-diagnostics /Users/birdmichael/Library/Developer/Xcode/DerivedData/demoBuild-hkrzbqmagemxhgasjxvnblksqtjz/Build/Intermediates.noindex/demoBuild.build/Debug-iphoneos/demoBuild.build/Objects-normal/arm64/ViewController.dia -c /Users/birdmichael/Downloads/demoBuild/demoBuild/ViewController.m -o /Users/birdmichael/Library/Developer/Xcode/DerivedData/demoBuild-hkrzbqmagemxhgasjxvnblksqtjz/Build/Intermediates.noindex/demoBuild.build/Debug-iphoneos/demoBuild.build/Objects-normal/arm64/ViewController.o |
省略掉路径相关,用下面的命令参数其实很清晰。clang 命令参数:
1 | -x 编译语言比如objective-c |
Target 在Build过程的控制
日常需要控制Build
过程通常都在 Xcode
的 Project editor
中的 Build Setting
,`Build Phases
和 Build Rules
能够控制编译的过程。
Build Phases
构建可执行文件的规则。指定 target 的依赖项目,在 target build 之前需要先 build 的依赖。在 Compile Source 中指定所有必须编译的文件,这些文件会根据 Build Setting 和 Build Rules 里的设置来处理。
在 Link Binary With Libraries 里会列出所有的静态库和动态库,它们会和编译生成的目标文件进行链接。
build phase 还会把静态资源拷贝到 bundle 里。
可以通过在 build phases 里添加自定义脚本来做些事情,比如像 CocoaPods 所做的那样。
小细节:Compile Source中是应该是有序的。之前在验证category重写时,移动文件顺序会影响代码顺序。(注:只是重复实验过几次,不保证绝对准确)
Build Rules
指定不同文件类型如何编译。每条 build rule 指定了该类型如何处理以及输出在哪。可以增加一条新规则对特定文件类型添加处理方法。
Build Settings
在 Build 的过程中各个阶段的选项的设置。
鸣谢: