作为一个一致的Unix系统,OS X与所有Unix口味的众所周知的目录合作:
OS X将自己的特殊目录添加到系统根下的Unix树中:
从文件系统的角度来看,iOS与OS X非常相似,并具有以下差异:
通过将扩展名更改为.zip和Unipping,可以将带有.IPA扩展名的文件不压缩。
/iTunesArtwork
/iTunesArtwork@2x
/iTunesMetadata.plist
/WatchKitSupport/WK
/META-INF
/Payload/
/Payload/<Application>.app/
/Payload/<Application>.app/<Application> ← Apple FairPlay DRM Encrypted Executable
/Payload/<Application>.app/Info.plist A file that contains some of the application specific configurations
/Payload/<Application>.app/_CodeSignature/ Contains a plist file with a signature over all files in the bundle
/Payload/<Application>.app/Assets.car Another zipped archive that contains assets (icons)
/Payload/<Application>.app/Frameworks/ Contains the app native libraries as .dylib or .framework files
/Payload/<Application>.app/PlugIns/ May contain app extensions as .appex files
/Payload/<Application>.app/Core Data It is used to save permanent data for offline use and sync across iCloud devices
/Payload/<Application>.app/PkgInfo An alternate way to specify the type and creator codes of your application or bundle
/Payload/<Application>.app/en.lproj, etc Language packs that contains resources for those specific languages
/var/containers/Bundle/Application/<UUID> Bundle directory; tampering invalidates signature
/var/mobile/Containers/Data/<UUID> Application runtime data
/var/mobile/Containers/Data/<UUID>/Documents/ Contains all the user-generated data
/var/mobile/Containers/Data/<UUID>/Library/ Contains all files that aren't user-specific – caches, preferences, cookies, plist files
/var/mobile/Containers/Data/<UUID>/Library/Caches/
Contains semi-persistent cached files
/var/mobile/Containers/Data/<UUID>/Library/Application Support/
Contains persistent files necessary for running the app
/var/mobile/Containers/Data/<UUID>/Library/Preferences/<bundle id>.plist
Properties that can persist after an application is restarted. Contains NSUserDefaults
/var/mobile/Containers/Data/<UUID>/tmp/ Temporary files that do not need to persist between app launches
PLIST文件是结构化的XML文件,其中包含支持基本对象类型的键值对,例如字典,列表,数字和字符串。通常,顶级对象是字典。 PLIST可以是二进制文件, XML或JSON文件。
| 抽象类型 | XML元素 | 可可班 | 核心基础类型 |
|---|---|---|---|
| 大批 | <array> | NSArray | CFArray ( CFArrayRef ) |
| 字典 | <dict> | NSDictionary | CFDictionary ( CFDictionaryRef ) |
| 细绳 | <string> | NSString | CFString ( CFStringRef ) |
| 数据 | <data> | NSData | CFData ( CFDataRef ) |
| 日期 | <date> | NSDate | CFDate ( CFDateRef ) |
| 数字 - 整数 | <integer> | NSNumber ( intValue ) | CFNumber ( CFNumberRef ,整数值) |
| 数字 - 浮点 | <real> | NSNumber ( floatValue ) | CFNumber ( CFNumberRef ,浮点值) |
| 布尔 | <true/>或<false/> | NSNumber ( boolValue == YES或boolValue == NO ) | CFBoolean ( CFBooleanRef ; kCFBooleanTrue或kCFBooleanFalse ) |
标准info.plist包含以下条目:
plutil -convert xml1 binary_file.plist
plutil -convert xml1 data_file.json -o data_file.plist
https://docs.python.org/3/library/plistlib.html
应用程序可以在关键系统进程作为root运行时作为移动用户访问运行。但是,沙箱可以更好地控制过程和应用程序可以执行的操作。
例如,即使两个进程作为同一用户(移动)运行,也不允许它们访问或修改彼此的数据。
每个应用程序都安装在/var/mobile/Applications/<UUID>下。 UUID是随机的。安装后,应用程序对某些系统区域和功能的阅读访问有限(SMS,电话...)。如果应用程序要访问受保护区域,则会出现弹出式请求权限。
应用开发人员可以利用iOS数据保护API实现存储在闪存中的用户数据的细粒访问控制。 API建立在安全飞地处理器(SEP)的顶部。 SEP是一个协助程序,可为数据保护和密钥管理提供加密操作。设备特定的硬件键 -设备UID (唯一ID) -嵌入了安全的飞地中,即使操作系统内核被损害,也确保了数据保护的完整性。
当在磁盘上创建文件时,将在Secure Enclave的基于硬件的随机数生成器的帮助下生成新的256位AES密钥。然后,用生成的密钥对文件的内容进行加密。然后,该密钥与类ID一起保存使用类键,并在文件的元数据内加密两个数据。
为了解密文件,使用系统键解密元数据。然后使用类ID检索类键以解密每个文件密钥并解密文件。
可以将文件分配给四个不同的保护类之一,这些类别在iOS安全指南中更详细地进行了解释。
每个应用程序都有一个唯一的主目录并具有沙盒,因此它们无法访问系统或其他应用程序存储的受保护的系统资源或文件。这些限制是通过Sandbox策略(又称配置文件)实现的,该策略由信任的BSD(MAC)强制性访问控制框架通过内核扩展实施。
某些功能/权限可以由应用程序的开发人员(例如数据保护或钥匙扣共享)配置,并在安装后直接生效。但是,对于其他人来说,将在应用程序第一次尝试访问受保护资源时明确询问用户。
目的字符串或用法说明字符串是自定义文本,当请求访问受保护的数据或资源的权限时,在系统许可请求警报中提供的用户提供了自定义文本。
如果具有原始源代码,则可以验证Info.plist文件中包含的权限:
Info.plist文件,并搜索以"Privacy -"开头的密钥。您可以通过右键单击和选择“显示RAW KEYS/VALUES”(例如"Privacy - Location When In Use Usage Description" NSLocationWhenInUseUsageDescription来切换视图以显示原始值。
如果只有IPA:
Info.plist位于Payload/<appname>.app/Info.plist中。plutil -convert xml1 Info.plist ),如“ ios基本安全测试”,“ info.plist文件”部分所述。UsageDescription结尾: <plist version="1.0">
<dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your location is used to provide turn-by-turn directions to your destination.</string>
App Store使用设备功能,以确保仅列出兼容设备,因此允许下载应用程序。 They are specified in the Info.plist file of the app under the [UIRequiredDeviceCapabilities](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/iPhoneOSKeys.html#//apple_ref/doc/plist/info/UIRequiredDeviceCapabilities)钥匙。
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
通常,您会找到ARMV7功能,这意味着该应用仅用于ARMV7指令集,或者如果是32/64位通用应用程序。
例如,应用程序可能完全取决于NFC的工作(例如“ NFC标签读取器”应用程序)。根据存档的iOS设备兼容性参考,NFC仅在iPhone 7(和iOS 11)开始。开发人员可能希望通过设置nfc设备功能来排除所有不兼容的设备。
权利是关键价值对,已签名到应用程序中,并允许超出运行时因素(例如Unix用户ID)的身份验证。由于权利是数字签名的,因此无法更改。系统应用程序和守护程序将权利广泛使用,以执行特定的特权操作,否则该操作将需要该过程作为root运行。这大大降低了通过折衷的系统应用程序或守护程序的特权升级潜力。
例如,如果要设置“默认数据保护”功能,则需要转到XCode中的“功能”选项卡并启用数据保护。这是由Xcode直接编写的<appname>.entitlements文件,为com.apple.developer.default-data-protection witterlemt, NSFileProtectionComplete 。在IPA中,我们可能会在embedded.mobileprovision中找到它。MobileProvisionas:
<key>Entitlements</key>
<dict>
...
<key>com.apple.developer.default-data-protection</key>
<string>NSFileProtectionComplete</string>
</dict>
对于其他功能,例如HealthKit,必须要求用户获得许可,因此不足以添加权利,必须将特殊键和字符串添加到应用程序的Info.plist文件中。
安全飞地是用于数据保护,触摸ID和面部ID的A7和更新的SOC的一部分。安全飞地的目的是处理密钥和其他信息,例如生物识别技术,这些信息足够敏感,无法由应用程序处理器处理。它是用硬件过滤器隔离的,因此AP无法访问它。它与AP共享RAM,但其部分RAM -TZ0已加密。安全飞地本身是一个闪烁的4MB AKF处理器核心,称为安全飞地处理器(SEP)。所使用的技术类似于ARM的Trustzone/Securecore,但通常包含针对Apple KF核心的专有代码,通常是专门的。
每个设备中的SOC都有一个具有GID键和UID键的AES协处理器。
设备的唯一ID(UID)和设备组ID(GID)为AES 256位键(UID)或在制造过程中汇集(GID)中的应用程序处理器。没有任何软件或固件可以直接读取它们;他们只能看到使用它们执行的加密或解密操作的结果。 UID是每个设备独有的,并且没有由Apple或其任何供应商记录。 GID对于一类设备中的所有处理器都是常见的,在安装和还原过程中交付系统软件时,用作额外的保护。将这些钥匙集成到硅中有助于防止它们被篡改或绕过或在AES发动机外访问。
GID密钥(组ID密钥)是所有设备与同一应用程序处理器共享的256位AES密钥。 GID密钥是iOS在设备上加密软件的一部分。这是iOS安全系统的组成部分,其中还包括SHSH签名。每个Apple SOC模型都有不同的键。
到目前为止,GID密钥尚未从任何设备中提取,因此使用它的唯一方法是通过AES引擎本身。
但
GID可以通过昂贵的冷启动攻击程序(https://en.m.wikipedia.org/wiki/wiki/cold_boot_attack)和下一个不那么昂贵的程序,该程序可以使用电子束石板石器仪芯片搜索者(https://minateh.ru/equipmenty canser)扫描SOC的过程。这样的实验是不合理的昂贵且复杂的,因此除了私人实验室Cellebrite以外,任何人都从未想过尝试实施它。 Cellebrite不分享其研究。
UID密钥(设备的唯一ID密钥)是AES 256位硬件密钥,每个iPhone是独特的。
一些派生的键由boot的ioaesaccelerator内核计算。这些键是通过使用UID键(0x7D0标识符)或GID键(0x3E8标识符)加密静态值来生成的。
密钥0x835 - 由UID键加密生成0x01010101010101010101010101010101 。用于数据保护。
密钥0x836 - 由UID键加密生成0x00E5A0E6526FAE66C5C1C6D4F16D6180 。这是由内核在还原过程中计算出的,但在正常引导过程中被归零。它也由安全的引导加载程序计算,其唯一已知的用途是在NOR中解密LLB。像0x835一样,每个设备都不同。
密钥0x837 - 由加密生成0x345A2D6C5050D058780DA431F0710E15带有S5L8900 GID键,导致0x188458A6D15034DFE386F23B61D43774 。它用作IMG2文件的加密密钥。随着iPhone OS 2.0中IMG3的引入,现在使用KBAGS代替0x837密钥。因为iPhone OS版本1.X仅在iPhone和iPod触摸(都使用S5L8900)上使用,因此其他处理器的加密值无关紧要。
密钥0x838 - 由加密为0x8C8318A27D7F030717D2B8FC5514F8E1与UID键生成。另一个基于UID-AES键的密钥,用于加密除llb(iboot,devicetree,图片)中的LLB以外的所有内容。
密钥0x899 - 通过加密与UID-KEY生成0xD1E8FCB53937BF8DEFC74CD1D0F1D4B0 。用法未知。
键0x89a - 由加密0xDB1F5B33606C5F1C1934AA66589C0661与UID键一起生成,获得了特定于设备的键。在A4设备上使用。它用于加密设备上的SHSH斑点。
密钥0x89b - 由加密为0x183E99676BB03C546FA468F51C0CBD49与UID键生成。它用于加密数据分区密钥。
密钥0x8A3 - 通过加密0x568241656551e0cdf56ff84cc11a79ef与UID键(使用AES-256-CBC)生成。它在A12的软件升级期间使用,然后在Hashing成为NONCE之前对“生成器”值(使用AES-128-CBC)进行加密。
更多信息:https://css.csail.mit.edu/6.858/2020/readings/ios-security-may19.pdf
https://www.securitylab.ru/contest/428454.php
https://www.securitylab.ru/contest/429973.php
Objective-C模块文件具有“ .m”扩展名(如果使用了C ++和Objective-C的混合,则“ .mm”扩展名)。标题文件 - “ .H”。在Objective-C中创建的类的所有对象都必须分配成堆。因此,ID类型是指向任何类的对象的指针(实际上,void *),都具有特殊的意义。空指针称为常数零。因此,可以将任何类的指针投入到ID类型上。出现问题:如何找出隐藏在ID下的对象属于哪个类?这要归功于ISA不变性,该类别的任何对象都存在于继承特殊基类NsObject的类的任何对象中(NS前缀代表下一步)。 ISA不变性是保留类型类的。这种类型的对象使您可以找到自己的名称和基类的名称,一组类别不变的,以及该对象已实现的所有方法的原型及其地址(通过选择器的本地列表)。除C保留单词以外的所有Objective-C保留单词以 @符号开头(例如@protocol,@selector,@interface)。通常,范围范围的类别不变性(@private, @protected)的名称始于下划线。对于字符串,可可具有非常方便的NSString课程。该类的字符串常数写为 @“ Hello World”,而不是通常的C字符串常数“ Hello World”。 Bool类型(基本上是未签名的Char)可以将常量值为“是”和“否”。下面列出了所有客观-C特异性的保留单词(与C语言不同,并且在标题文件OBJC/OBJC.H中找到):
@interface开始声明类别或类别(类别是类扩展而无继承)
@end完成任何类,类别或协议的声明定义@private将类不变性的范围限制为类方法(类似于C ++)
@protected默认情况下。将类别不变性的范围限制为派生类的类方法和方法(类似于C ++)
@public删除范围限制(类似于C ++)
@try定义了一个块,并可能投掷例外(类似于C ++)
@throw抛出一个异常对象(类似于C ++)
@catch()处理前面的@try块中抛出的异常(类似于C ++)
@finally在 @try块之后定义了传递控制的块,而不管是否抛出了例外@class缩写的类声明形式(仅名称(类似于C ++))
@selector(method_name)返回方法名称name的编译选择器@protocol(protocol_name)返回一个协议类的实例,名为protocol_name
@encode(type_spec)初始化一个字符字符串,该字符串将用于加密类型type_spec的数据@synchronized()定义一个在任何给定时间点仅由一个线程执行的代码块@implementation开始定义类或类别@protocol开始协议声明(类似于由纯虚拟函数组成的C ++类)
要强制对象执行方法,您需要向其发送一条名为与所需方法相同的消息。此消息称为方法选择器。发送的语法如下:
[receiver method];
- (void) addObject: (id) otherObject;
如果您在方法原型的开头放置加号+ ,那么这种方法将被视为类方法,并且自然不会接受隐式自我参数(这类似于在C ++中声明静态方法)。而且,如果没有自我指向的对象的ISA不变,那么超级指针当然也无法正常工作。因此,任何方法的原型都被称为这样:
- | + (<return type>) mainMethodNamePart
[: (<type of first parameter>) nameOfFirstFormalParameter
[[optionalMethodNamePart]: (<type of second parameter>) secondFormalParameterName] ...
]
例如:
+ (Class)class;
+ (id)alloc;
- (id)init;
- (void)addObject: (id)anObject;
+ (NSString *)stringWithCString: (const char*)aCString usingUncoding: (enum NSStringEncoding)encoding;
- (NSString *)initStringWithFormat: (NSString *)format, ...;
苹果开发人员文档
https://developer.apple.com/library/archive/documentation/macosx/conceptual/osx_technology_overview/systemworks/systemworks/systemworks/systemprameworks.html
https://www.theiphonewiki.com/wiki//system/library/frameworks
框架存储在文件系统上的多个位置:
此外,应用程序可能包括其自己的框架。
苹果开发人员文档 - 网络
Apple开发人员文档 - NetWorkextension
Apple开发人员文档 - NetworkingDriverKit
您可能遇到的大多数应用程序连接到远程端点。甚至在执行任何动态分析(例如流量捕获和分析)之前,您也可以通过列举应与该应用程序通信的域获得一些初始输入或输入点。
通常,这些域将作为应用程序二进制中的字符串存在。可以通过使用rabin2 -zz <path_to_binary>或IDA Pro中检索字符串来提取域。后一个选项具有明显的优势:它可以为您提供上下文,因为您可以通过检查跨引用来查看每个域使用的上下文。
从这里,您可以使用此信息来获取更多的见解,这些见解可能在分析过程中可能会在以后使用,例如,您可以将域与固定证书匹配,或对域名进行进一步的侦察,以了解有关目标环境的更多信息。
安全连接的实施和验证可能是一个复杂的过程,需要考虑许多方面。例如,许多应用程序都使用除HTTP(例如XMPP或普通TCP数据包)外的其他协议,或者执行证书固定以试图阻止MITM攻击。
网络框架是在2018年在Apple Worldwide开发人员会议(WWDC)上引入的,是替代插座API的。这个低级网络框架提供了与内置的动态网络,安全性和性能支持一起发送和接收数据的类。
如果using: .tls使用:默认情况下,TLS 1.3将在网络框架中启用。这是与旧式安全运输框架相比的首选选项。
URLSession建立在网络框架上,并利用相同的运输服务。如果端点为HTTPS,则该类还使用TLS 1.3。
URLSession应用于HTTP和HTTPS连接,而不是直接利用网络框架。该类本质地支持两个URL方案,并针对此类连接进行了优化。它需要更少的样板代码,从而减少错误的倾向并默认确保安全连接。仅当有低级和/或高级网络要求时,才应使用网络框架。
Apple官方文档包括使用网络框架实现NetCat和URLSession将网站数据获取内存的示例。
https://developer.limneos.net/
https://www.theiphonewiki.com/wiki//system/library/privateframeworks
可以在此处找到设备的最新工具https://canijailbreak.com
这项研究基本上是为CheckRa1Ned Iphone 5s至X编写的,X带iOS 10-15,如果将发布新的Bootrom PWNage工具,则可以长期更新。检查一下https://www.theiphonewiki.com/wiki/bootrom#bootrom_exploits
https://repo.chariz.com )killall SpringBoard使用Filza文件管理器打开.deb/.ipa文件,然后按Install,如果您看到.deb安装的错误,请尝试查找和安装所有依赖关系
从https://cydia.akemi.ai/ repo安装AppSync
https://apt.bingner.com/
https://apt.thebigboss.org/repofiles/cydia/
https://cydia.saurik.com
https://repo.dynastic.co/
https://getdelta.co/
https://cokepokes.github.io/
https://cydia.akemi.ai/
https://nscake.github.io/
https://repo.chariz.com/
https://mrepo.org/
https://rejail.ru/
https://repo.hackyouriphone.org/
https://build.frida.re/ - 弗里达https://cydia.radare.org/ - radare2
在桌面上安装
brew install libimobiledevice ideviceinstaller libirecovery
sudo port install idevicerestore
idevice_id列表连接的设备或打印设备的名称idevicebackup创建或恢复设备idevicedebug遗产idevicecrashreport idevicebackup2创建或恢复运行iOS 4或更高版本的设备idevicedebugserverproxy备份 connection from a device for remote debugging idevicediagnostics Interact with the diagnostics interface of a device ideviceenterrecovery Make a device enter recovery mode ideviceimagemounter Mount disk images on the device ideviceinfo Show information about a connected device idevicename Display or set the device name idevicepair Manage host pairings with devices and usbmuxd idevicescreenshot Gets a screenshot from连接的设备idevicesetlocation模拟了连接设备的设备idevicesyslog RELAY SYSLOG上的位置
ideviceinstaller --list-apps
ideviceinstaller --install <Application.ipa>
ideviceinstaller --uninstall <bundle id>
idevicedebug -d run <bundle id>
irecovery --shell允许与iOS设备的iboot/ibss进行通信
idevicerestore --latest将新的固件还原到设备idevicerestore --erase --latest
擦除所有数据的力量恢复
inetcat实用程序可暴露与设备iproxy 2222:22绑定本地端口2222并将22的RAW连接到第一个USB设备的22
在设备Apple文件导管“ 2”上安装
使用iMazing或ifunbox访问文件系统
安装FILZA文件管理器
在设备上安装OpenSSH并在桌面上运行:
iproxy 2222:22
ssh -p 2222 root@localhost
root的默认iOS密码是alpine 。如果您的记忆不好,请不要更改它
从https://repo.chariz.com上安装在设备newterm 2上使用本地终端
cd /private/var/containers/Bundle/Application/<guid>/myapp.app
// Contains compiled code, statically linked files, compressed NIB files.
cd /private/var/mobile/Containers/Data/Application/
ls -lrt // Your freshly installed IPA is at the bottom of list
cd [app guid]/Documents/
cd [app guid]/Library/
/private/var/Keychains
TrustStore.sqlite3
keychain-2.db
pinningrules.sqlite3
// Extract IPA (whether App Store encrypted or not)
scp -r -P 2222 root@localhost:/var/containers/Bundle/Application/<app GUID>/hitme.app ~/hitme.app
// Different to SSH, the uppercase P for Port with SCP. Order important.
scp -P 2222 root@localhost:/var/root/overflow.c localfilename.c
// from Jailbroken device to local machine
// Caution:no space after the root@localhost: Otherwise you copy the entire filesystem!
scp -P 2222 root@localhost:/private/var/mobile/Containers/Data/Application/<App GUID>/Library/Caches/Snapshots/com.my.app
// from local machine to remote Jailbroken device
scp -P 2222 hello.txt root@localhost:/var/root/
从https://nscake.github.io/安装libflex和flexing
开放新月并运行killall SpringBoard
现在,您可以通过longpress在状态栏上加载任何应用程序中的flex
弗里达(Frida)是一种动态二进制仪器工具包,它使我们能够在先前锁定的软件中执行脚本。简而言之,弗里达(Frida)可让您将JavaScript的摘要注入Windows,Mac,Linux,iOS和Android上的本机应用程序。
将Frida Repo添加到Cydia - https://build.frida.re/
如果您没有Python 3:
brew install pyenv
pyenv install 3.9.0 (或最新可用的)
然后安装
pip3 install frida-tools
frida-ls-devices列出可用设备frida-ps -U列出了USB设备上所有运行的流程名称和PID
frida-ps -Uai列出了USB设备上所有已安装的应用程序frida-ps -Ua列出了USB设备上的所有运行应用程序frida-ls-devices列出了所有附加设备frida-ps -D 0216027d1d6d3a03将Frida连接到特定设备frida-discover工具,用于在过程中发现内部功能frida-trace -U Twitter -i "*URL*"跟踪本机Apis
frida-trace -U -f com.toyopagroup.picaboo -I "libcommonCrypto*"
启动应用程序并跟踪加密API调用frida-trace -U Twitter -m "-[NSURL* *HTTP*]"跟踪Objective-C API
frida -U -n Twitter -l inject.js通过repl在USB设备上注入脚本frida -n cat用名字连接到猫frida -f foobar Force Open Foobar
frida -U -f foobar --no-pause USB和力量开始。开始运行应用程序frida-ps -U | grep -i myapp从USB连接的设备获取目标应用程序的流程ID
frida -U -f foobar --no-pause -q --eval 'console.log("Hi Frida");'
运行脚本并退出弗里达
在这一点上,我们将本NativeFunction存储在play_sound变量中。称其为常规函数play_sound() ,也记得给出( int )输入参数: play_sound(1007)
将所有这些放在一起:
var address = Module.findExportByName('AudioToolbox', 'AudioServicesPlaySystemSound')
var play_sound = new NativeFunction(address, 'void', ['int'])
play_sound(1007)
您必须先获得对象的实例:
var instance = ObjC.classes.ClassName.alloc().init();ObjC.choose , like - if you know there's only one instance already created somewhere on the heap - you can to something like var instance = ObjC.chooseSync(ObjC.classes.ClassName)[0];var instance = ObjC.classes.MySingleton.getInstance().myInterestingInstance();and then call the method on the instance:
instance.setSomething();
or, if the method signature takes an argument, like - setSomething: , you can also pass the argument (just remember to put a _ instead of ObjC's : ):
instance.setSomething_(argument);
frida -U "My App" // Attach Frida to app over USB
Process.id
419
Process.getCurrentThreadId()
3843
var b = "hello frida"
console.log(b)
"hello frida"
c = Memory.allocUtf8String(b)
"0x1067ec510"
Memory.readUtf8String(c)
"hello frida"
console.log(c)
0x1067ec510
console.log(c.readUtf8String(5))
hello
console.log(c.readUtf8String(11))
hello frida
ptrToC = new NativePointer(c);
"0x1067ec510"
console.log(ptrToC)
0x1067ec510
console.log(ptrToC.readCString(8))
hello fr
Memory.readUtf8String(ptrToC)
"hello frida"
Objective-C's syntax includes the : and @ characters. These characters were not used in the Frida Javascript API .
// Attach to playground process ID
frida -p $(ps -ax | grep -i -m1 playground |awk '{print $1}')
ObjC.available
true
ObjC.classes.UIDevice.currentDevice().systemVersion().toString()
"11.1"
ObjC.classes.NSBundle.mainBundle().executablePath().UTF8String()
ObjC.classes.UIWindow.keyWindow().toString()
RET: <WKNavigation: 0x106e165c0>
// shows Static Methods and Instance Methods
ObjC.classes.NSString.$ownMethods
ObjC.classes.NSString.$ivars
var myDate = ObjC.classes.NSDate.alloc().init()
console.log(myDate)
2019-04-19 19:03:46 +0000
myDate.timeIntervalSince1970()
1555700626.021566
myDate.description().toString()
"2019-04-19 19:03:46 +0000"
var a = ObjC.classes.NSUUID.alloc().init()
console.log(a)
4645BFD2-94EE-413D-9CE5-8982D41ED6AE
a.UUIDString()
{
"handle": "0x7ff3b2403b20"
}
a.UUIDString().toString()
"4645BFD2-94EE-413D-9CE5-8982D41ED6AE"
var b = ObjC.classes.NSString.stringWithString_("foo");
b.isKindOfClass_(ObjC.classes.NSString)
true
b.isKindOfClass_(ObjC.classes.NSUUID)
false
b.isEqualToString_("foo")
true
b.description().toString()
"foo"
var c = ObjC.classes.NSString.stringWithFormat_('foo ' + 'bar ' + 'lives');
console.log(c)
foo bar lives
var url = ObjC.classes.NSURL.URLWithString_('www.foobar.com')
console.log(url)
www.foobar.com
url.isKindOfClass_(ObjC.classes.NSURL)
true
console.log(url.$class)
NSURL
var b = ObjC.classes.NSString.stringWithString_("foo");
var d = ObjC.classes.NSData
d = b.dataUsingEncoding_(1) // NSASCIIStringEncoding = 1, NSUTF8StringEncoding = 4,
console.log(d)
<666f6f> // This prints the Hex value "666f6f = foo"
d.$className
"NSConcreteMutableData"
var x = d.CKHexString() // Get you the Byte array as a Hex string
console.log(x)
666f6f
x.$className
"NSTaggedPointerString"
var newStr = ObjC.classes.NSString.stringWithUTF8String_[d.bytes]
// demoapp is the iOS app name
myapp=$(ps x | grep -i -m1 demoapp | awk '{print $1}')
frida-trace -i "getfsent*" -p $myapp
// Connect to process with Frida script
frida --codeshare mrmacete/objc-method-observer -p 85974
Process.enumerateModules()
// this will print all loaded Modules
Process.findModuleByName("libboringssl.dylib")
{
"base": "0x1861e2000",
"name": "libboringssl.dylib",
"path": "/usr/lib/libboringssl.dylib",
"size": 712704
}
Process.findModuleByAddress("0x1c1c4645c")
{
"base": "0x1c1c2a000",
"name": "libsystem_kernel.dylib",
"path": "/usr/lib/system/libsystem_kernel.dylib",
"size": 200704
}
DebugSymbol.fromAddress(Module.findExportByName(null, 'strstr'))
{
"address": "0x183cb81e8",
"fileName": "",
"lineNumber": 0,
"moduleName": "libsystem_c.dylib",
"name": "strstr"
}
Module.findExportByName(null, 'strstr')
"0x183cb81e8"
Module.getExportByName(null,'strstr')
"0x183cb81e8"
Process.findModuleByAddress("0x183cb81e8")
{
"base": "0x183cb6000",
"name": "libsystem_c.dylib",
"path": "/usr/lib/system/libsystem_c.dylib",
"size": 516096
}
a = Process.findModuleByName("Reachability")
a.enumerateExports()
....
{
"address": "0x102fab020",
"name": "ReachabilityVersionString",
"type": "variable"
},
{
"address": "0x102fab058",
"name": "ReachabilityVersionNumber",
"type": "variable"
}
....
...
..
frida -U -f funky-chicken.debugger-challenge --no-pause -q --eval 'var x={};Process.enumerateModulesSync().forEach(function(m){x[m.name] = Module.enumerateExportsSync(m.name)});' | grep -B 1 -A 1 task_threads
"address": "0x1c1c4645c",
"name": "task_threads",
"type": "function"
frida -U -f funky-chicken.debugger-challenge --no-pause -q --eval 'var x={};Process.findModuleByAddress("0x1c1c4645c");'
{
"base": "0x1c1c2a000",
"name": "libsystem_kernel.dylib",
"path": "/usr/lib/system/libsystem_kernel.dylib",
"size": 200704
}
[objc_playground]-> var a = ObjC.classes.NSString.stringWithString_("foo");
[objc_playground]-> a.superclass().toString()
"NSString"
[objc_playground]-> a.class().toString()
"NSTaggedPointerString"
// PASTE THIS CODE INTO THE FRIDA INTERFACE...
Interceptor.attach(ObjC.classes.NSTaggedPointerString['- isEqualToString:'].implementation, {
onEnter: function (args) {
var str = new ObjC.Object(ptr(args[2])).toString()
console.log('[+] Hooked NSTaggedPointerString[- isEqualToString:] ->' , str);
}
});
// TRIGGER YOUR INTERCEPTOR
[objc_playground_2]-> a.isEqualToString_("foo")
[+] Hooked NSTaggedPointerString[- isEqualToString:] -> foo
1 // TRUE
[objc_playground_2]-> a.isEqualToString_("bar")
[+] Hooked NSTaggedPointerString[- isEqualToString:] -> bar
0 // FALSE
// frida -U -l open.js --no-pause -f com.yd.demoapp
// the below javascript code is the contents of open.js
var targetFunction = Module.findExportByName("libsystem_kernel.dylib", "open");
Interceptor.attach(targetFunction, {
onEnter: function (args) {
const path = Memory.readUtf8String(this.context.x0);
console.log("[+] " + path)
}
});
frida-trace --v Check it works frida-trace --help Excellent place to read about Flags frida-trace -f objc_playground Spawn and NO trace frida-trace -m "+[NSUUID UUID]" -U "Debug CrackMe" Trace ObjC UUID static Class Method frida-trace -m "*[ComVendorDebugger* *]" -U -f com.robot.demo.app ObjC wildcard trace on Classes frida-trace -m "*[YDDummyApp.UserProfileMngr *]" -U -f com.robot.demo.app Trace mangled Swift functions frida-trace -i "getaddrinfo" -i "SSLSetSessionOption" -U -f com.robot.demo Trace C function on iOS frida-trace -m "*[*URLProtection* *]" -U -f com.robot.demo For https challenge information frida-trace -m "*[NSURLSession* *didReceiveChallenge*]" -U -f com.robot.demo Check whether https check delegate used frida-trace -U -f com.robot.demo.app -I libsystem_c.dylib Trace entire Module. frida-trace -p $myapp -I UIKit Trace UIKit Module. frida-trace -f objc_playground -I CoreFoundation Trace CoreFoundation Module. frida-trace -I YDRustyKit -U -f com.yd.mobile Trace my own module. frida-trace -m "-[NSURLRequest initWithURL:]" -U -f com.robot.demo Get app files and APIs frida-trace -m "-[NSURL initWithString:]" -U -f com.robot.demo Find the API endpoints frida-trace -m "*[NSURL absoluteString]" -U -f com.robot.demo My favorite of these
frida-trace -i "*strcpy" -f hitme aaaa bbbb
Instrumenting functions...
_platform_strcpy: Loaded handler at "/.../__handlers__/libSystem.B.dylib/_platform_strcpy.js"
Started tracing 1 function. Press Ctrl+C to stop.
Edit the auto-generated, template Javascript file.
-----------
onEnter: function (log, args, state) {
// strcpy() arg1 is the Source. arg0 is the Destination.
console.log('n[+] _platform_strcpy()');
var src_ptr = args[1].toString()
var src_string = Memory.readCString(args[1]);
var src_byte_array = Memory.readByteArray(args[1],4);
var textDecoder = new TextDecoder("utf-8");
var decoded = textDecoder.decode(src_byte_array);
console.log('[+] src_ptrt-> ' , src_ptr);
console.log('[+] src_stringt-> ' + src_string);
console.log('[+] src_byte_arrayt-> ' + src_byte_array);
console.log('[+] src_byte_array sizet-> ' + src_byte_array.byteLength);
console.log('[+] src_byte_array decodedt-> ' + decoded);
},
结果:
[+] _platform_strcpy()
[+] src_ptr -> 0x7ffeefbffaa6
[+] src_string -> aaaa
[+] src_byte_array -> [object ArrayBuffer]
[+] src_byte_array size -> 4
[+] decoded -> aaaa
[+] _platform_strcpy()
[+] src_ptr -> 0x7ffeefbffaab
[+] src_string -> bbbb
[+] src_byte_array -> [object ArrayBuffer]
[+] src_byte_array size -> 4
[+] decoded -> bbbb
frida-ps -Uai // get your bundle ID
frida --codeshare mrmacete/objc-method-observer -U -f funky-chicken.push-demo
[+] At the Frida prompt...
observeSomething('*[ABC* *]'); // any Class beginning with ABC, regardless of instance or static class
observeSomething('-[WKWebsiteDataStore httpCookieStore]');
observeSomething('-[WKWebAllowDenyPolicyListener *]');
observeSomething('-[WKWebView loadRequest:]'); // dump the URL to hit
observeSomething('-[WKWebView load*]'); // you get all HTML, js, css, etc
observeSomething('-[WKWebView loadHTMLString:baseURL:]') // really effective; see the entire request
observeSomething('-[WKWebView *Agent]'); // try to see if somebody set a custom UserAgent
observeSomething('*[* isEqualToString*]'); // watch string compares
bash -c "exec -a YDFooBar ./frida-server &"
frida-server -l 0.0.0.0:19999 &
frida-ps -ai -H 192.168.0.38:19999
frida-trace -m "*[NSURLSession* *didReceiveChallenge*]" -H 192.168.0.38:19999 -f com.youdog.rusty.tinyDormant
Objection is a runtime mobile exploration toolkit powered by Frida to assess the security posture of mobile applications without needing to write scripts .
pip3 install objection
objection device_type Get information about an attached device
objection explore Start the objection exploration REPL
objection explore --startup-command 'ios jailbreak simulate'
objection explore --startup-command 'ios jailbreak disable'
Early Instrumentation
ls
env This will print out the locations of the applications Library, Caches and Documents directories
!<shell command> Run OS command
file download <remote path> [<local path>]
file upload <local path> [<remote path>]
Upload/Download
file cat <file> View file
memory dump all <local destination> memory dump from_base <base_address> <size_to_dump> <local_destination>
Dump all memory/Dump part
memory list modules List loaded modules in memory
memory list exports <module_name> Exports of a loaded module
memory search "<pattern eg: 41 41 41 ?? 41>" (--string) (--offsets-only)
memory write "<address>" "<pattern eg: 41 41 41 41>" (--string)
Search/Write
sqlite connect pewpew.sqlite Query the sqlite database
sqlite execute schema Have a look at the table structure
sqlite execute query select * from data;
Execute any query
import <local path frida-script> Import frida script
jobs list List running scripts/jobs
jobs kill <job id> Kill script/job
ios plist cat credentials.plist Read plist file
ios info binary Inspect binary info
ios sslpinning disable --quiet Disable SSL pinning
ios jailbreak simulate Simulate a jailbroken environment to understand how an application behaves
ios jailbreak disable Jailbreak detection bypass
ios nsuserdefaults get Dump NSUserDefaults
ios nsurlcredentialstorage dump Dump NSURLCredentialStorage
ios keychain dump Dump app keychain
ios cookies get Get secure flags and sensitive data stored in cookies
ios monitor crypto monitor Hooks CommonCrypto to output information about cryptographic operation
ios ui dump Dump UI hierarchy
ios ui alert "<message>" Show alert
env Local app paths
ios bundles list_bundles List bundles of the application
ios bundles list_frameworks List external frameworks used by the application
ios hooking list classes List classes of the app
ios hooking search classes <str> Search a class that contains a string
ios hooking list class_methods List methods of a specific class
ios hooking search methods <str> Search a method that contains a string
ios hooking watch class <class_name>
Hook all the methods of a class, dump all the initial parameters and returns
ios hooking watch method "-[<class_name> <method_name>]" --dump-args --dump-return --dump-backtrace
Hook an specific method of a class dumping the parameters, backtraces and returns
ios hooking set return_value "-[<class_name> <method_name>]" false
This will make the selected method return the indicated boolean
ios hooking generate simple <class_name>
Generate hooking template.
r2 frida://device-id/Snapchat Attach to a running app using the display name.
r2 frida://attach/usb//Gadget Attach to the Frida Gadget
r2 frida://device-id//com.snapchat.android Spawn an app using two // and the package name.
r2 frida://spawn/usb/device-id/com.android.app Or explicitly using the word spawn
r2 frida://spawn/usb//com.android.app Or without entering the device-id
=!? Get the list of commands
=!?~^i :
i Show target information
ii[*] List imports
il List libraries
is[*] <lib> List symbols of lib (local and global ones)
iE[*] <lib> Same as is, but only for the export global ones
iEa[*] (<lib>) <sym> Show address of export symbol
isa[*] (<lib>) <sym> Show address of symbol
ic <class> List Objective-C classes or methods of <class>
ip <protocol> List Objective-C protocols or methods of <protocol>
=!i Shows target information
=!i* Shows target information in r2 form
.=!i* Radare2 imports all the dynamic binary data from Frida. Eg: which architecture, endianness, pointer size, etc...
.=!iE* Radare2 imports all the dynamic export data from Frida for all the dynamic libraries.
.=!iE* <lib> Radare2 imports all the dynamic export data from Frida for only one specific library.
.=!ii* Radare2 imports all the dynamic import data from Frida.
=!ii <lib> List imports. Commonly used with the symbol ~ , which is the internal grep of r2 .
=!ii* <lib> List imports in r2 form.
=!il List libraries. Commonly used with the symbol ~ , which is the internal grep of r2.
=!iE <lib> List exports of library(ies)
=!iEa (<lib>) <sym> Show address of export symbol
=!iEa* (<lib>) <sym> Show address of export symbol in r2 format
=!isa[*] (<lib>) <sym> Show address of symbol
=!ic List classes
=!/ keyword Search hex/string pattern in memory ranges (see search.in=?)
> =!?~^/ :
/[x][j] <string|hexpairs> Search hex/string pattern in memory ranges (see search.in=?)
/w[j] string Search wide string
/v[1248][j] value Search for a value honoring e cfg.bigendian of given width
> =!?~^d :
db (<addr>|<sym>) List or place breakpoint
db- (<addr>|<sym>)|* Remove breakpoint(s)
dc Continue breakpoints or resume a spawned process
dd[-][fd] ([newfd]) List, dup2 or close filedescriptors
dm[.|j|*] Show memory regions
dma <size> Allocate bytes on the heap, address is returned
dmas <string> Allocate a string inited with on the heap
dmad <addr> <size> Allocate bytes on the heap, copy contents from
dmal List live heap allocations created with dma[s]
dma- (<addr>...) Kill the allocations at (or all of them without param)
dmp <addr> <size> <perms> Change page at
dmm List all named squashed mapsdmh List all heap allocated chunksdmhj List all heap allocated chunks in JSONdmh* Export heap chunks and regions as r2 flagsdmhm Show which maps are used to allocate heap chunksdp Show current piddpt Show threadsdr Show thread registers (see dpt)dl libname Dlopen a librarydl2 libname [main] Inject library using Frida's >= 8.2 new APIdt (<addr>|<sym>) ... Trace list of addresses or symbolsdth (<addr>|<sym>) (x y..) Define function header (z=str,i=int,v=hex barray,s=barray)dt- Clear all tracingdtr <addr> (<regs>...) Trace register valuesdtf <addr> [fmt] Trace address with format (^ixzO) (see dtf?)dtSf[*j] [sym|addr] Trace address or symbol using the stalker (Frida >= 10.3.13)dtS[*j] seconds Trace all threads for given seconds using the stalkerdi[0,1,-1] [addr] Intercept and replace return value of addressdx [hexpairs] Inject code and execute it (TODO)dxc [sym|addr] [args..] Call the target symbol with given args e[?] [a[=b]] List/get/set config evaluable vars
[0x00000000] > = ! e
e patch.code=true
e search.in=perm:r--
e search.quiet=false
e stalker.event=compile
e stalker.timeout=300
e stalker.in=raw =!. script.js
=!ic List iOS classes
More info: https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06c-reverse-engineering-and-tampering#tampering-and-runtime-instrumentation
Frida GUI.
Frida GUI.
Frida GUI. https://github.com/FuzzySecurity/Fermion
更多信息:
https://frida.re/docs/examples/ios/
https://frida.re/docs/frida-trace/
https://frida.re/docs/examples/ios/
https://github.com/sensepost/objection/wiki/Using-objection
Apple's Entitlements Troubleshooting – https://developer.apple.com/library/content/technotes/tn2415/_index.html
Apple's Code Signing – https://developer.apple.com/support/code-signing/
Cycript Manual – http://www.cycript.org/manual/
Frida iOS Tutorial – https://www.frida.re/docs/ios/
Frida iOS Examples – https://www.frida.re/docs/examples/ios/
r2frida Wiki – https://github.com/enovella/r2frida-wiki/blob/master/README.md
Charlie Miller, Dino Dai Zovi. The iOS Hacker's Handbook. Wiley, 2012 – https://www.wiley.com/en-us/iOS+Hacker's+Handbook-p-9781118204122
Jonathan Levin. Mac OS X and iOS Internals: To the Apple's Core. Wiley, 2013 – http://newosxbook.com/MOXiI.pdf
Install SSL Kill Switch 2 from https://github.com/nabla-c0d3/ssl-kill-switch2/releases/
Open your settings and enable SSL Kill Switch 2
Run Charles on PC.
Install Charles Root Certificate on iOS device:
Help → SSL Proxing → Install Charles Root Certificate on Mobile Device or Remote Browser.
The following window will appear:
Depending on your network architecture the IP address Charles is running on may differ.
Proxy → SSL Proxying Settings... → Add (Include) → Host: * ;港口: *
Since all binary files inside an .ipa are encrypted with AES and being decrypted with a private key by Secure Enclave Processor at the runtime there is a few ways to decrypt it:
If you don't have Node.js:
brew install nvm
nvm install node
To dump decrypted ipa using bagbak utility install it on desktop:
sudo npm install -g bagbak
Then download your application from the App Store and dump:
bagbak <bundle id or name> --uuid <uuid> --output <output>
There are several ways to run the hardware AES engine:
Patch iBoot to jump to aes_crypto_cmd
Use OpenIBoot
Use XPwn with a kernel patch
Use Greenpois0n console:
ideviceenterrecovery
irecovery --shell
go aes dec <file>
Use ipwndfu
Use checkra1n
Run checkra1n with -p to run into pongoOS (https://github.com/checkra1n/pongoOS) and use the aes command over USB
If you want to disassemble an application from the App Store, remove the FairPlay DRM first.
After decrypting .ipa file open app binary in disassembler like IDA Pro .
In this section the term "app binary" refers to the Macho-O file in the application bundle which contains the compiled code, and should not be confused with the application bundle - the IPA file.
If you have a license for IDA Pro, you can analyze the app binary using IDA Pro as well.
To get started, simply open the app binary in IDA Pro.
Upon opening the file, IDA Pro will perform auto-analysis, which can take a while depending on the size of the binary. Once the auto-analysis is completed you can browse the disassembly in the IDA View (Disassembly) window and explore functions in the Functions window, both shown in the screenshot below.
https://github.com/ChiChou/IDA-ObjCExplorer/blob/master/ObjCExplore.py – Obj-C Classes Explorer for IDA Pro. Just press Ctrl + Shift + E .
https://github.com/avast/retdec-idaplugin – RetDec decompiler for IDA Pro. Just press Ctrl + D .
https://github.com/zynamics/objc-helper-plugin-ida – zynamics Objective-C helper script.
https://github.com/techbliss/Frida_For_Ida_Pro – Connect frida.
https://github.com/vadimszzz/idapython/blob/master/cortex_m_firmware.py – IDA Python module for loading ARM Cortex M firmware.
https://github.com/saelo/ida_scripts/blob/master/kernelcache.py – Identify and rename function stubs in an iOS kernelcache.
https://github.com/luismiras/IDA-iOS-scripts/blob/master/find_iOS_syscalls.py – Find iOS syscalls.
https://github.com/stefanesser/IDA-IOS-Toolkit/blob/master/listAllKEXT.py – List all Kexts.
https://github.com/stefanesser/IDA-IOS-Toolkit/blob/master/findSyscallTable.py – This script searches the iOS syscall table within the iOS kernelcache.
https://github.com/stefanesser/IDA-IOS-Toolkit/blob/master/fixupSysctlSet.py – This script ensures that all sysctl_oid structures referenced by the sysctl_set segment are marked correctly.
https://github.com/bazad/ida_kernelcache – An IDA Toolkit for analyzing iOS kernelcaches.
You can use class-dump to get information about methods in the application's source code.
Note the architectures: armv7 (which is 32-bit) and arm64 . This design of a fat binary allows an application to be deployed on all devices. To analyze the application with class-dump, we must create a so-called thin binary, which contains one architecture only:
iOS8-jailbreak:~ root# lipo -thin armv7 DamnVulnerableIOSApp -output DVIA32
And then we can proceed to performing class-dump:
iOS8-jailbreak: ~ root# class-dump DVIA32
@interface FlurryUtil : ./DVIA/DVIA/DamnVulnerableIOSApp/DamnVulnerableIOSApp/YapDatabase/Extensions/Views/Internal/
{
}
+ (BOOL)appIsCracked ;
+ (BOOL)deviceIsJailbroken ; Note the plus sign, which means that this is a class method that returns a BOOL type. A minus sign would mean that this is an instance method. Refer to later sections to understand the practical difference between these.
Strings are always a good starting point while analyzing a binary, as they provide context to the associated code. For instance, an error log string such as "Cryptogram generation failed" gives us a hint that the adjoining code might be responsible for the generation of a cryptogram.
In order to extract strings from an iOS binary, you can use GUI tools such as Ghidra or Cutter or rely on CLI-based tools such as the strings Unix utility strings <path_to_binary> or radare2's rabin2 rabin2 -zz <path_to_binary> . When using the CLI-based ones you can take advantage of other tools such as grep (eg in conjunction with regular expressions) to further filter and analyze the results.
NM
nm libprogressbar.a | less
rabin2
rabin2 -s file
radare2
is~FUNC
Check URLs:
strings <binary inside app bundle> | grep -E 'session|https'
strings <binary inside app bundle> | grep -E 'pinning'
rabin2 -qz <binary inside app bundle> // in Data Section
rabin2 -qzz <binary inside app bundle> // ALL strings in binary
jtool -dA __TEXT.__cstring c_playground
Dumping C-Strings from address 0x100000f7c (Segment: __TEXT.__cstring)..
Address : 0x100000f7c = Offset 0xf7c
0x100000f7c: and we have a winner @ %ldr
0x100000f98: and that's a wrap folks!r
IDA Pro can be used for obtaining cross references by right clicking the desired function and selecting Show xrefs .
w0 = 32-bitx0 = 64-bitwzr or xzr . Write to = discard, read from = 0 .sp - unlike other instruction sets, never modified implicitly (eg no push / pop ).pc , not modifiable directly.str ).mov to copy one register to another, eg mov x0, x1 -> x0 = x1 .0 loaded from wzr / xzr .orr x0, xzr, 5 .movz + movk , eg: movz x0 , 0x1234 , lsl 32
movk x0 , 0x5678 , lsl 16
movk x0 , 0x9abcx0 = 0x123456789abc .movn for negative values, eg movn x0, 1 -> x0 = -1 .lsl and lsr instructions = logic-shift-left and logic-shift-right, eg lsl x0, x0, 8 -> x0 <<= 8 .lsl and lsr not only used as instructions, but also as operands to other instructions (see movz above).asl for arithmetic shift also exists, but less frequently used.ldr and str with multiple variations and addressing modes:ldr x0, [x1] -> x0 = *x1str x0, [x1] -> *x1 = x0ldr x0, [x1, 0x10] -> x0 = *(x1 + 0x10)ldp / stp to load/store two registers at once behind each other, eg:stp x0, x1, [x2] -> *x2 = x0; *(x2 + 8) = x1;xN for 64-bit, wN for 32-bitldrh / srth for 16-bitldrb / strb for 8-bitldrsw x0, [x1] -> load 32-bit int, sign extend to 64-bitldrsh x0, [x1] -> load 16-bit int, sign extend to 64-bitldrsb x0, [x1] -> load 8-bit int, sign extend to 64-bitstr instructions)ldr x0, [x1, 0x10]ldr x0, [x1, 0x10]! (notice the ! ) -> x1 += 0x10; x0 = *x1;ldr x0, [x1], 0x10 -> x0 = *x1; x1 += 0x10;adr x0, 0x12345 (only works for small offset from PC)adrp + add : adrp x0 , 0xffffff8012345000 ; "address of page", last 12 bits are always zero
add x0 , x0 , 0x678ldr .Note: Only dealing with integral types here. The rules change when floating-point is involved.
x0 - x7 first 8 arguments, rest on the stack (low address to high) with natural alignment (as if they were members of a struct)x8 pointer to where to write the return value if >128 bits, otherwise scratch registerx9 - x17 scratch registersx18 platform register (reserved, periodically zeroed by XNU)x19 - x28 callee-savedx29 frame pointer (basically also just callee-saved)x30 return addressx19 - x28 usually start like this: stp x24 , x23 , [ sp , - 0x40 ] !
stp x22 , x21 , [ sp , 0x10 ]
stp x20 , x19 , [ sp , 0x20 ]
stp x29 , x30 , [ sp , 0x30 ]
add x29 , sp , 0x30 ldp x29 , x30 , [ sp , 0x30 ]
ldp x20 , x19 , [ sp , 0x20 ]
ldp x22 , x21 , [ sp , 0x10 ]
ldp x24 , x23 , [ sp ], 0x40
retadd sp, sp, 0x... and sub sp, sp, 0x... .x0 - x7 come before variadic arguments on the stack, naturally aligned.x0 .x0 , the second/upper half in x1 .x8 to where the result is written.nzcv holds condition flags (Negative, Zero, Carry, oVerflow).instr.cond ), others as source operands ( instr ..., cond ). List of condition codes:eq / ne = equal/not equallt / le / gt / ge = less than/less or equal/greater than/greater or equal (signed)lo / ls / hi / hs = lower/lower or same/higher/higher or same (unsigned)cs / cc = carry set/carry clear are aliases of hs / lo .cmp = most common/basic compare instruction, sets condition flags.示例: cmp x0 , x1
cmp x0 , 3cmn = compare negativetst = bitwise testadds / adcs = add/add with carrysubs / sbcs = subtract/subtract with carrynegs / ngcs = negate/negate with carrycset = conditional set, eg: cmp x0 , 3
cset x0 , lox0 = (x0 < 3)csel = conditional select, eg: cmp x0 , 3
csel x0 , x1 , x2 , lox0 = (x0 < 3) ? x1 : x2ccmp = conditional compare, eg: cmp x0 , 3
ccmp x0 , 7 , 2 , hs
b.hi 0xffffff8012345678hi condition will be true if x0 < 3 || x0 > 7 (third ccmp operand is raw nzcv data).b = simple branch, jump to PC-relative address. b 0xffffff8012345678 cmp x0 , 3
b.lo 0xffffff8012345678 ; jump to 0xffffff8012345678 if x < 3cbz / cbnz = compare-branch-zero and compare-branch-non-zero. cmp xN , 0
b.eq 0x... cmp xN , 0
b.ne 0x...if(x) or if(!x) .)tbz / tbnz = test single bit and branch if zero/non-zero.tbz x0, 3, ... translates to if((x0 & (1 << 3)) == 0) goto ... .bl = branch-and-link (eg bl 0xffffff8012345678 )x30 and jump to PC-relative address. Used for static function calls.blr = branch-and-link to register (eg blr x8 )x30 and jump to address in x8 . Used for calls with function pointers or C++ virtual methods.br = branch to register (eg br x8 )x8 . Used for tail calls.ret = return to address in register, default: x30x30 (eg ret x8 ), but compiler doesn't usually generate that.nop = do nothingsvc = make a system call using an immediate value (eg svc 0x80 ). Note that the immediate value is separate from the syscall number. XNU ignores the immediate and expects the syscall number in x16 .. = special symbol that refers to the address of the instruction it is used in (eg adr x0, . )Install the following prerequisites:
brew install ldid xz
Set up the THEOS environment variable:
echo "export THEOS=~/theos" >> ~/.zshrc
source ~/.zshrc
Clone Theos:
git clone --recursive https://github.com/theos/theos.git $THEOS
Get the toolchain:
Xcode contains the toolchain.
Get an iOS SDK:
Xcode always provides the latest iOS SDK, but as of Xcode 7.3, it no longer includes private frameworks you can link against. This may be an issue when developing tweaks. You can get patched SDKs from our SDKs repo.
curl -LO https://github.com/theos/sdks/archive/master.zip
TMP=$(mktemp -d)
unzip master.zip -d $TMP
mv $TMP/sdks-master/*.sdk $THEOS/sdks
rm -r master.zip $TMP
Logos is a Perl regex-based preprocessor that simplifies the boilerplate code needed to create hooks for Objective-C methods and C functions with an elegant Objective-C-like syntax. It's most commonly used along with the Theos build system, which was originally developed to create jailbreak tweaks. Logos was once integrated in the same Git repo as Theos, but now has been decoupled from Theos to its own repo.
Logos aims to provide an interface for Cydia Substrate by default, but can be configured to directly use the Objective-C runtime.
Logos is a component of the Theos development suite.
%hookf(return type, functionName, arguments list...) {
/* body */
}
Generate a function hook for the function named functionName . Set functionName in %init to an expression if the symbol should be dynamically looked up.
例子:
// Given the function prototype (only add it yourself if it's not declared in an included/imported header)
FILE *fopen(const char *path, const char *mode);
// The hook is thus made
%hookf(FILE *, fopen, const char *path, const char *mode) {
puts("Hey, we're hooking fopen to deny relative paths!");
if (path[0] != '/') {
return NULL;
}
return %orig; // Call the original implementation of this function
}
// functions can also be looked up at runtime, if, for example, the function is in a private framework
%hookf(BOOL, MGGetBoolAnswer, CFStringRef string) {
if (CFEqual(string, CFSTR("StarkCapability"))) {
return YES;
}
return %orig;
}
%ctor() {
%init(MGGetBoolAnswer = MSFindSymbol(NULL, "_MGGetBoolAnswer"));
}
%ctor {
/* body */
}
Generate an anonymous constructor (of default priority). This function is executed after the binary is loaded into memory. argc , argv , and envp are implicit arguments so they can be used as they would be in a main function.
%dtor {
/* body */
}
Generate an anonymous deconstructor (of default priority). This function is executed before the binary is unloaded from memory. argc , argv , and envp are implicit arguments so they can be used as they would be in a main function.
The directives in this category open a block of code which must be closed by an %end directive (shown below). These should not exist within functions or methods.
%group GroupName
/* %hooks */
%end
Generate a hook group with the name GroupName . Groups can be used for conditional initialization or code organization. All ungrouped hooks are in the default group, initializable via %init without arguments.
Cannot be inside another %group block.
Grouping can be used to manage backwards compatibility with older code.
例子:
%group iOS8
%hook IOS8_SPECIFIC_CLASS
// your code here
%end // end hook
%end // end group ios8
%group iOS9
%hook IOS9_SPECIFIC_CLASS
// your code here
%end // end hook
%end // end group ios9
%ctor {
if (kCFCoreFoundationVersionNumber > 1200) {
%init(iOS9);
} else {
%init(iOS8);
}
}
%hook ClassName
/* objc methods */
%end
Open a hook block for the class named ClassName .
Can be inside a %group block.
例子:
%hook SBApplicationController
- (void)uninstallApplication:(SBApplication *)application {
NSLog(@"Hey, we're hooking uninstallApplication:!");
%orig; // Call the original implementation of this method
}
%end
%new
/* objc method */
%new(signature)
/* objc method */
Add a new method to a hooked class or subclass by adding this directive above the method definition. signature is the Objective-C type encoding for the new method; if it is omitted, one will be generated.
Must be inside a %hook or %subclass block.
例子:
%new
- (void)handleTapGesture:(UITapGestureRecognizer *)gestureRecognizer {
NSLog(@"Recieved tap: %@", gestureRecognizer);
}
%subclass ClassName: Superclass <Protocol list>
/* %properties and methods */
%end
Generate a subclass at runtime. Like @property in normal Objective-C classes, you can use %property to add properties to the subclass. The %new specifier is needed for a method that doesn't exist in the superclass. To instantiate an object of the new class, you can use the %c operator.
Can be inside a %group block.
例子:
// An interface is required to be able to call methods of the runtime subclass using block syntax.
@interface MyObject : NSObject
@property (nonatomic, retain) NSString * someValue;
@end
%subclass MyObject : NSObject
%property (nonatomic, retain) NSString * someValue;
- (instancetype)init {
if ((self = %orig)) {
[self setSomeValue:@"value"];
}
return self;
}
%end
%ctor {
// The runtime subclass cannot be linked at compile time so you have to use %c().
MyObject *myObject = [[%c(MyObject) alloc] init];
NSLog(@"myObject: %@", [myObject someValue]);
}
%property (nonatomic|assign|retain|copy|weak|strong|getter=...|setter=...) Type name;
Add a property to a %subclass just like you would with @property to a normal Objective-C subclass as well as adding new properties to existing classes within %hook.
Must be inside a %hook or %subclass block.
%end
Close a %group, %hook or %subclass block.
The directives in this category should only exist within a function or method body.
%init;
%init([<ClassName>=<expr>, …]);
%init(GroupName[, [+|-]<ClassName>=<expr>, …]);
Initialize a group's method and function hooks. Passing no group name will initialize the default group. Passing ClassName=expr arguments will substitute the given expressions for those classes at initialization time. The + sigil (as in class methods in Objective-C) can be prepended to the classname to substitute an expression for the metaclass. If not specified, the sigil defaults to - , to substitute the class itself. If not specified, the metaclass is derived from the class.
The class name replacement is specially useful for classes that contain characters that can't be used as the class name token for the %hook directive, such as spaces and dots.
例子:
%hook ClassName
- (id)init {
return %orig;
}
%end
%ctor {
%init(ClassName=objc_getClass("SwiftApp.ClassName"));
}
%c([+|-]ClassName)
Evaluates to ClassName at runtime. If the + sigil is specified, it evaluates to MetaClass instead of Class. If not specified, the sigil defaults to - , evaluating to Class.
%orig
%orig(args, …)
Call the original hooked function or method. Doesn't work in a %new'd method. Works in subclasses, strangely enough, because MobileSubstrate will generate a super-call closure at hook time. (If the hooked method doesn't exist in the class we're hooking, it creates a stub that just calls the superclass implementation.) args is passed to the original function - don't include self and _cmd , Logos does this for you.
例子:
%hook ClassName
- (int)add:(int)a to:(int)b {
if (a != 0) {
// Return original result if `a` is not 0
return %orig;
}
// Otherwise, use 1 as `a`
return %orig(1, b);
}
%end
&%orig
Get a pointer to the original function or method. Return type is void (*)(id, SEL[, arg types])
例子:
// Call from outside hooked method:
void (*orig_ClassName_start)(id, SEL) = nil;
void doStuff(id self, SEL _cmd) {
if (self && orig_ClassName_start) {
orig_ClassName_start(self, _cmd);
}
}
%hook ClassName
- (void)start {
%orig;
orig_ClassName_start = &%orig;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC),
dispatch_get_main_queue(), ^{
doStuff(self, _cmd);
});
}
%end
// Call with another object:
%hook ClassName
- (int)add:(int)a to:(int)b {
int (*_orig)(id, SEL, int, int) = &%orig;
ClassName * myObject = [ClassName new];
int r = _orig(myObject, _cmd, 1, 2);
[myObject release];
return r;
}
%end
Real world example at PreferenceLoader
%log;
%log([(<type>)<expr>, …]);
Dump the method arguments to syslog. Typed arguments included in %log will be logged as well.
You can use logify.pl to create a Logos source file from a header file that will log all of the functions of that header file. Here is an example of a very simple Logos tweak generated by logify.pl.
Given a header file named SSDownloadAsset.h :
@interface SSDownloadAsset : NSObject
- (NSString *)finalizedPath;
- (NSString *)downloadPath;
- (NSString *)downloadFileName;
+ (id)assetWithURL:(id)url type:(int)type;
- (id)initWithURLRequest:(id)urlrequest type:(int)type;
- (id)initWithURLRequest:(id)urlrequest;
- (id)_initWithDownloadMetadata:(id)downloadMetadata type:(id)type;
@end
You can find logify.pl at $THEOS/bin/logify.pl and you would use it as so:
$THEOS/bin/logify.pl ./SSDownloadAsset.h
The resulting output should be:
%hook SSDownloadAsset
- (NSString *)finalizedPath { %log; NSString * r = %orig; NSLog(@" = %@", r); return r; }
- (NSString *)downloadPath { %log; NSString * r = %orig; NSLog(@" = %@", r); return r; }
- (NSString *)downloadFileName { %log; NSString * r = %orig; NSLog(@" = %@", r); return r; }
+ (id)assetWithURL:(id)url type:(int)type { %log; id r = %orig; NSLog(@" = %@", r); return r; }
- (id)initWithURLRequest:(id)urlrequest type:(int)type { %log; id r = %orig; NSLog(@" = %@", r); return r; }
- (id)initWithURLRequest:(id)urlrequest { %log; id r = %orig; NSLog(@" = %@", r); return r; }
- (id)_initWithDownloadMetadata:(id)downloadMetadata type:(id)type { %log; id r = %orig; NSLog(@" = %@", r); return r; }
%end
| 扩大 | Process order |
|---|---|
| x | will be processed by Logos, then preprocessed and compiled as Objective-C. |
| XM | will be processed by Logos, then preprocessed and compiled as Objective-C++. |
| xi | will be preprocessed first, then Logos will process the result, and then it will be compiled as Objective-C. |
| XMI | will be preprocessed first, then Logos will process the result, and then it will be compiled as Objective-C++. |
xi or xmi files enable Logos directives to be used in preprocessor macros, such as #define . You can also import other Logos source files with the #include statement. However, this is discouraged, since this leads to longer build times recompiling code that hasn't changed. Separating into x and xm files, sharing variables and functions via extern declarations, is recommended.
These file extensions control how a build system such as Theos should build a Logos file. Logos itself does not take the file extension into account and works regardless of whether a file is Objective-C or Objective-C++.
https://theos.dev/docs/
https://cydia.saurik.com/faq/developing.html
http://www.cydiasubstrate.com/id/7cee77bc-c4a5-4b8b-b6ef-36e7dd039692/
http://www.cydiasubstrate.com/inject/
https://iphonedev.wiki/index.php/Cydia_Substrate
https://cwcaude.github.io/project/tutorial/2020/07/02/iOS-tweak-dev-1.html
https://cwcaude.github.io/project/tutorial/2020/07/04/iOS-tweak-dev-2.html
https://cwcaude.github.io/project/tutorial/2020/07/12/iOS-tweak-dev-3.html
https://cwcaude.github.io/project/tutorial/2020/07/16/iOS-tweak-dev-4.html