iOS|iOS 系统相关复习
沙盒
iOS沙盒详细介绍
iOS沙盒篇
沙盒机制介绍
iOS中的沙盒机制是一种安全体系。为了保证系统安全,iOS每个应用程序在安装时,会创建属于自己的沙盒文件(存储空间)。应用程序只能访问自身的沙盒文件,不能访问其他应用程序的沙盒文件,当应用程序需要向外部请求或接收数据时,都需要经过权限认证,否则,无法获取到数据。所有的非代码文件都要保存在此,例如属性文件plist、文本文件、图像、图标、媒体资源等,其原理是通过重定向技术,把程序生成和修改的文件定向到自身文件夹中。
注意:沙盒的目录结构 每个APP的沙盒下面都有相似目录结构,如下图
APP之间不能相互通,唯独可以通过URL Scheme可以通信。
每次编译代码会生成新的沙盒路径,注意是编译不是启动,所以模拟器或者真机运行你每次运行所得到的沙盒路径都是不一样,线上版本app真机不会生成新的沙盒路径
文章图片
image.png 沙盒中相关路径 AppName.app 应用程序的程序包目录,包含应用程序的本身。由于应用程序必须经过签名,所以不能在运行时对这个目录中的内容进行修改,否则会导致应用程序无法启动。
Documents/ 保存应用程序的重要数据文件和用户数据文件等。用户数据基本上都放在这个位置(例如从网上下载的图片或音乐文件),该文件夹在应用程序更新时会自动备份,在连接iTunes时也可以自动同步备份其中的数据。
Library:这个目录下有两个子目录,可创建子文件夹。可以用来放置您希望被备份但不希望被用户看到的数据。该路径下的文件夹,除Caches以外,都会被iTunes备份.
Library/Caches: 保存应用程序使用时产生的支持文件和缓存文件(保存应用程序再次启动过程中需要的信息),还有日志文件最好也放在这个目录。iTunes 同步时不会备份该目录并且可能被其他工具清理掉其中的数据。
Library/Preferences: 保存应用程序的偏好设置文件。NSUserDefaults类创建的数据和plist文件都放在这里。会被iTunes备份。
tmp/: 保存应用运行时所需要的临时数据。不会被iTunes备份。iPhone重启时,会被清空。
Documents/Inbox:该目录用来保存由外部应用请求当前应用程序打开的文件。比如:应用A向系统注册了几种可打开的文件格式,应用B中有一个A支持的格式的文件F,并申请调用应用A打开文件F。由于F当前是在应用B的沙盒中,我们知道,沙盒机制使不允许应用A访问应用B沙盒中的文件,因此苹果的解决方案是将文件F拷贝一份到应用A的Documents/Inbox目录下,再让应用A打开文件F。会被iTunes备份。
访问沙盒目录常用C函数介绍
//文件路径搜索
FOUNDATION_EXPORT NSArray *NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde);
该方法返回值为一个数组,在iphone中由于只有一个唯一路径,所以直接取数组第一个元素即可.
参数1:指定搜索的目录名称,比如这里用NSDocumentDirectory表明我们要搜索的是Documents目录。如果我们将其换成NSCachesDirectory就表示我们搜索的是Library/Caches目录。
参数2:搜索主目录的位置,NSUserDomainMask表示搜索的范围限制于当前应用的沙盒目录。还可以写成NSLocalDomainMask(表示/Library)、NSNetworkDomainMask(表示/Network)等
参数3:是否获取完整的路径,我们知道在iOS中的全写形式是/User/userName,该值为YES即表示写成全写形式,为NO就表示直接写成“~”。
该值为NO:Caches目录路径为~/Library/Caches
该值为YES:Caches目录路径为
/var/mobile/Containers/Data/Application/E7B438D4-0AB3-49D0-9C2C-B84AF67C752B/Library/Caches
typedef NS_OPTIONS(NSUInteger, NSSearchPathDomainMask) {
NSUserDomainMask = 1,// 用户目录 - 基本上就用这个。
NSLocalDomainMask = 2,// 本地
NSNetworkDomainMask = 4,// 网络
NSSystemDomainMask = 8,// 系统
NSAllDomainsMask = 0x0ffff// 所有
};
//常用的NSSearchPathDirectory枚举值
typedef NS_ENUM(NSUInteger, NSSearchPathDirectory) {
NSApplicationDirectory = 1,// supported applications (Applications)
NSDemoApplicationDirectory,// unsupported applications, demonstration versions (Demos)
NSAdminApplicationDirectory,// system and network administration applications (Administration)
NSLibraryDirectory,// various documentation, support, and configuration files, resources (Library)
NSUserDirectory,// user home directories (Users)
NSDocumentationDirectory,//Library 下的(Documentation)模拟器上没有创建
NSDocumentDirectory,// documents (Documents)};
获取沙盒路径
- 获取沙盒主目录路径
NSString *homePath = NSHomeDirectory();
- 获取Documents路径
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
- 获取Library路径
NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
- 获取Caches路径
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
- 获取tmp路径
NSString *tmpDir = NSTemporaryDirectory();
文件读写 向沙盒中写入文件
NSString *documentsPathW = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES)[0];
//写入文件
if (!documentsPathW) {
NSLog(@"目录未找到");
} else {
NSString *filePaht = [documentsPath stringByAppendingPathComponent:@"test.txt"];
NSArray *array = [NSArray arrayWithObjects:@"code",@"change", @"world", @"OK", @"", @"是的", nil];
[array writeToFile:filePaht atomically:YES];
}
向沙盒中读取文件
NSString *documentsPathR = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES)[0];
NSString *readPath = [documentsPathR stringByAppendingPathComponent:@"test.txt"];
NSArray *fileContent = [[NSArray alloc] initWithContentsOfFile:readPath];
NSLog(@"文件内容:%@",fileContent);
Bundle(AppName.app的内容) iOS基础之Bundle详解
iOS:NSBundle的一些理解
Bundle的使用 一般我们从bundle中获取一张图片,可以有这样的获取思路:
1)获取主bundle
2)获取自定义bundle
3)获取自定义bundle中的资源
通常可以这样写:
//主bundle,也就是可执行的工程的bundle
NSBundle *mainBundle = [NSBundle mainBundle];
//NSBundle *mainBundle = [NSBundle bundleForClass:[self class]];
//放在主工程中的自定义bundle
NSString *myBundlePath = [mainBundle pathForResource:@"MyBundle" ofType:@"bundle"];
NSBundle *myBundle = [NSBundle bundleWithPath:myBundlePath];
//放在自定义bundle中的图片
NSString *imagePath = [myBundle pathForResource:@"123" ofType:@"png"];
self.image = [UIImage imageWithContentsOfFile:imagePath];
Bundle的定义和特点 Bundle是一个含有可执行的代码及代码所需资源,以特定标准的层次结构组合起来的文件夹。这里的可执行是指编译过后可直接运行的代码程序,一个典型的例子就是iOS程序打包后的ipa,我们解压ipa后会得到一个payload文件夹,进入文件夹之后会发现一个和ipa名称相同的.app文件夹。这个.app文件夹就是一个Bundle。
系统如何识别Bundle呢?一般而言一个文件夹如果带着.app,.bundle,.framework,.plugin,.kext等等特定后缀,那么系统就认为是Bundle。如果使用Xcode创建项目的话,Xcode会提供相应的模板来生成正确的Bundle类型。
从上面的后缀我们也可以看出Bundle主要分为:
- Application。应用程序,包含代码和资源。iOS和macOS的app就是这种。
- Frameworks。框架,包含动态共享库和相应资源。我们常用的系统库和第三方库都属于这种。
- Plug-Ins。插件,macOS很多的系统功能支持插件,一种动态加载代码模块的方式。
Bundle的内部结构 Application Bundle的结构
Application类型的bundle是很常见的,一般里面会包含一下几中类型的文件:
- Info.plist文件。每个程序中必须有这个文件,因为它包含了程序运行的配置信息,是系统运行程序的依据。
- 可执行代码文件。这是程序的主体,包含了程序的进入点和链接的静态代码。
- 资源文件。程序运行过程中需要的资源,比如图片,音频,视频,多语言的字符串文件,nib文件,数据文件,配置文件等等。这里面的大部分文件都可以根据语言、地区、设备通过特定的结构或命名方式加以区分,程序会自动识别加载。
- 其他支持文件。Mac app可以嵌入高层资源,比如私有库,插件,文件末班,自定义数据资源等等。iOS app可以包含自定义数据资源,但是不能包含私有库和插件。
MyApp
MyApp//代码编译链接后的可执行文件
Info.plist //程序信息
Assets.car//Assets中的资源对应的文件。
MyAppIcon.png//所有的icon图片
LaunchImage.png//所有启动图片
nibs//工程中的xib,storyboard生成的nib文件
customResource//工程目录下或者更里层文件夹下的图片,音、视频,数据文件等自定义资源
customBundle.bundle//工程中使用的其他第三方或资源的bundle
en.lproj//语言区分的文件夹,可以使字符串、图片,nib文件等
MyString
MyImage
zh-Hans.lproj
MyString
MyImage
PkgInfo//系统识别信息
_CodeSignature//签名信息文件夹,里面的CodeResources包含对bundle中的所有资源文件的签名信息
embedded.mobileprovision //打包的配置文件信息
Assets.car无法直接打开,可以使用工具 cartool 列出包含的所有文件名称。PkgInfo、签名信息和嵌入配置文件都是打包后生成的,是系统验证app的依据。
Framework Bundle结构
Framework Bundle和app bundle的最大不同在于框架库有版本控制,因此必须包含版本列表和当前版本信息。
Framework的bundle结构如下:
MyFramework.framework/
MyFramework -> Versions/Current/MyFramework
Resources-> Versions/Current/Resources
Versions/
A/
MyFramework
Headers/
MyHeader.h
Resources/
Eglish.lproj/
InfoPlist.strings
Info.plist
Current -> A
上面的根目录下的MyFramework和Resources,以及Versions下面的current 都是文件引用,表明引用文件的位置。真正的代码,资源,header等内容都放在Versions文件夹下面的具体版本文件夹中,Current就指向当前版本的引用。
Bundle的基本使用 使用bundle主要都是围绕着定位资源路径而来的。包括:获取bundle及其信息(不管是main bundle还是其他bundle),获取资源路径(根据资源名称和类型定位),使用系统API直接获取资源(比如图片、音频、本地化字符串、context help,nibs,Info.plist内容等等)。
在磁盘上查找资源时,Bundle对象遵循特定的搜索模式。首先返回全局资源(即不在特定于语言的.lproj目录中的资源),然后返回特定于区域和语言的资源。此搜索模式表示捆绑包按以下顺序查找资源:
- 全局(非本地化)资源
- 特定于区域的本地化资源(基于用户的区域首选项)
- 特定于语言的本地化资源(基于用户的语言首选项)
- 开发语言资源(由bundle的Info.plist文件中的CFBundleDevelopmentRegion键指定)
在查找资源文件时,bundle对象在确定要返回的文件时会自动考虑许多标准文件名修饰符。可以为特定设备(iphone,ipad)或特定屏幕分辨率(@2x,@ 3x)标记资源。指定所需资源的名称时,请不要包含这些修饰符。 bundle对象选择最适合底层设备的文件。
NSBundle的基本使用 NSBundle这个类其实就是用来定位可执行资源的。获取到具体的可执行文件的位置,然后再加载。
mainBundle和bundleForClass都是返回一个NSBundle对象。mainBundle
对于所有Mach-O Type类型,mainBundle返回的都是可执行工程的bundle。
例如:有一个Executable工程Demo,使用到了动态库工程DynamicFramework和静态库的工程StaticFramework,那么无论是在Demo中,还是DynamicFramework和StaticFramework中,最终mainBundle返回的都是Demo的bundle!
bundleForClass
如果是对于Executable类型的工程,或者是静态的工程,无论class是属于可执行Executable类型的工程,还是属于其他的静态库,最终返回的是main bundle,相当于我们上面的[NSBundle mainBundle]的返回结果。相反的,对于动态的工程,可以获取到该工程的bundle。
imageNamed与imageWithContentsOfFile imageNamed与imageWithContentsOfFile小结
[UIImage imageNamed:]方法导致的卡顿优化
关于 iOS Asset 探讨
结论
图像的重复利用使用imageNamed的。例如:需要在一个TableView里重复加载同样一个图标,那么用imageNamed加载图像,系统会把那个图标Cache到内存,在Table里每次利用那个图像的时候,只会把图片指针指向同一块内存。这种情况使用imageNamed加载图像就会变得非常有效。
不需要重用该图像,或者要通过网络下载或加载大的图像时,使用imageWithContentsOfFile的方式加载图像从磁盘加载图片,UIImage主要提供了两种方式:
+(UIImage *)imageNamed:(NSString *)name;
+(UIImage *)imageWithContentsOfFile:(NSString *)path;
imageNamed:第一次加载图片时会缓存图片到内存,下次直接从缓存中读取,适合使用频繁的图片。 //这种初始化方法可以直接不给出图片的具体名字,它会自动匹配
优点:
方便快捷,只有第一次使用的时候稍慢,接下来在使用就会快些.
缺点:
如果在当前工程中大量会大量的浪费内存空间
imageNamed:方法还有个限制,它是在main bundle里找图片,如果图片放在Images.xcassets或者直接把图片方在工程里,参数直接传图片名可以找到。像我司的图片是放在单独建立的bundle里,如果要用imageNamed:加载的话文件名前面就要加上bundle名,像这样a.bundle/b.png。
imageWithContentsOfFile:根据路径去读取,不会把图片缓存到内存,每次调用都要重新从磁盘加载一次。如果图片在当前工程中只使用一次,应该选择这个方法。屏幕适配问题
iOS的图片文件需要提供3种尺寸的1x、2x、3x,根据不同的屏幕尺寸我们需要加载不同的图片,关于不同屏幕的图片加载,苹果已经帮我们封装好了,我们只需要将3中尺寸的图片放到工程中,然后调用imageNamed:或者imageWithContentsOfFile:,它会自动根据屏幕尺寸来加载不同的图片。
关于imageNamed:,官方文档中有这么一段讨论:
This method looks in the system caches for an image object with the specified name and returns the variant of that image that is best suited for the main screen.imageWithContentsOfFile:还没找到官方文档的说明(如果各位知道,欢迎各位大牛在评论中提出),不过我测试过是可以的。
#pragma mark --- 将NSData类型的数据存储到本地(以图片为例)
//这种必须拼接图片的全名称,否则image路径为空
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"v_red_heart_selected@3x.png" ofType:nil];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];
//将image类型的对象转换成为NSData类型数据进行存储
//使用UIImageJPEGRepresentation:将图片转换成为NSData类型
//第一个参数:要转换的image对象
//第二个参数:表示图片压缩的值
//IPhone中将大于2M的图片,使用该方法,最终会将图片保存成jpeg格式
NSData *imageData = https://www.it610.com/article/UIImageJPEGRepresentation(image, 1);
//找到路径存储
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
//最终路径
NSString *imagePath = [documentPath stringByAppendingString:@"/1234.jpeg"];
[imageData writeToFile:imagePath atomically:YES];
NSLog(@"imagePath = %@",imagePath);
//读取NSData类型的数据
//需求:将NSData类型的数据读取出来,转换成为UIImage类型并且显示在imageView上
NSData *newData = [NSData dataWithContentsOfFile:imagePath];
UIImage *showImage = [[UIImage alloc] initWithData:newData];
UIImageView *showImageView = [[UIImageView alloc] initWithImage:showImage];
[self.view addSubview:showImageView];
应用间的跳转 iOS - URL Schema
Universal Link以及支持微信和QQSDK
iOS Universal Links(通用链接)
Schema schema是iOS9之前比较主流的一种跳转方案了, 更多的是用在了两个APP相互跳转中。也可以在Safari中输入schema://跳转到App内部。在每次跳转的时候都会弹框询问。如果iPhone中如果没有安装则会直接弹出错误提示
注册Schema 在Info.plist中进行配置
URL Identifier,一个字符串对象。该字符串是你自定义的 URL scheme 的名字。建议采用反转域名的方法保证该名字的唯一性,比如 com.yourCompany.yourApp。
文章图片
image 通过自定义 URL Scheme 向应用传递参数
文章图片
image 第一步:在模拟器
safari
中输入:LionsomApp://?token=123abc®istered=1
【iOS|iOS 系统相关复习】第二步:跳转到应用中获取参数:
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {
NSLog(@"从哪个app跳转而来 Bundle ID: %@", options[UIApplicationOpenURLOptionsSourceApplicationKey]);
NSLog(@"URL scheme:%@", [url scheme]);
NSLog(@"URL query:%@", [url query]);
// 允许打开
return YES;
}
文章图片
image Universal Link Univeresal Link(通用链接) 是 iOS9推出的特性,当用户点击通用链接时,iOS设备可以不通过Safari或网页,直接打开App,比如在备忘录中直接打开App;
同时由于通用链接是标准的HTTPS链接,既可以打开App,也可以打开网页(在未安装App的时候)。
同时可以使用通用链接,在不同App页面跳转,以及传递参数。
Universal Links(通用链接)的优点 唯一性:不像自定义的schema链接,通用链接不会被其它的APP所使用。因为它使用标准的https链接到你自己的域名。【因为你的域名不会被其他人所使用】
安全性:当用户下载APP的时候,iOS会检查你上传到web服务器的文件以确保您的网站允许您的应用程序以其名义打开网址。因为只有本人有权利创建且上传该文件到服务器,所以网站和APP的关联是安全的。
灵活性:甚至在iOS设备没有安装你项目的时候通用链接也会正常工作。当设备没有安装APP的时候,点击通用链接会在Safari展示你网站的内容。
灵活性:一个通用链接可以同时作用于项目和网站中,其它APP可以在不知道你的APP是否安装的情况下与你的APP通信
如何支持Universal Link 1 . 设置配置文件
appapple-app-site-association
, 注意:该文件没有后缀, 格式为
{
"applinks": {
"apps": [],
"details": [
{
"appID": "D3KQX62K1A.com.example.appstore",
"paths": [
"/test/*","/qq_conn/10000000/*"
]
},
{
"appID": "X62K1AD3KQ.com.example.enterprise",
"paths": [
"/test/*","/qq_conn/10000000/*"
]
}
]
}
}复制代码
其中,
AppID
的格式为 .
Paths
中第一个为你自己的路径, 可以只填通配符 *
, 第二个为QQ的通用链接配置,10000000
是你的QQ AppId
然后将该文件上传到你的网站的
.well-known
文件夹或者根目录下。例如:
https://yourdomain/.well-known/apple-app-site-association
或者
https://yourdomain/apple-app-site-association
具体可参考官网 Settiing up an App's Assocaiated Domains
配置 Assocaiated Domains 2.1 在developer.apple.com中打开Assocaiated Domains
文章图片
image.png 2.2 在Xcode -> TARGETS -> Signing & Capabilities中添加Associated Domains功能,添加配置如下
文章图片
image.png 2.3. 上述两项配置完成后,重装真机后,在Safari中输入 app.yourDoamin.com/test/, 打开后下拉可以看到如下页面,即表示通用链接接通
image.png 进入app后的处理 现在用户点击某个链接,直接可以进我们的app了,但是我们的目的是要能够获取到用户进来的链接,根据链接来展示给用户相应的内容。
我们需要在工程里的 AppDelegate 里实现方法
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
{
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb])
{
NSURL *url = userActivity.webpageURL;
if (url是我们希望处理的)
{
//进行我们的处理
}
else
{
[[UIApplication sharedApplication] openURL:url];
}
}return YES;
}
ASLR (地址空间布局随机化) iOS逆向工程(九):ASLR
Mach-O文件介绍之ASLR(进程地址空间布局随机化)
概述
ASLR(Address space layout randomization)是一种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,通过增加攻击者预测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目的。
ASLR前言
以前我们用Xcode的LLDB指令打断点时,可以用方法名打断点,例如
breakpoint set -n "[UIViewController touchBegin:]"
,但是我们想动态调试别人的App,就不能用方法名称了,需要用到方法的内存地址才能打,例如 breakpoint set --address 0x123123123
而想知道方法的内存地址就需要学习ASLR
一、什么是ASLR?
- ASLR,全称是Address Spce Layout Randomization,翻译过来就是地址空间布局随机化,是一种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,增加了攻击者预测目的地址的难度,防止攻击者直接定位代码位置,阻止溢出攻击。
- 苹果从iOS 4.3之后引入了ASLR技术,所以想要逆向别人的App,学会ASLR是必不可少的。
- ASLR的概念看着复杂,实际上非常简单,其实就是当Mach-O文件载入虚拟内存的时候,起始地址不从0x00000000开始了,而是随机增加一段,例如从0x50000开始,后面的函数地址都会增加0x50000
-
- Mach-O文件的内部分为三个区域:
Header
、Load Commands
、Data
,如下图所示:
文章图片
image
- Mach-O文件的内部分为三个区域:
-
- Header区,存放了CPU类型、Mach-O文件的具体类型、LoadCommands的数量、LoadCommands的大小等数据,如下所示:
文章图片
image
- Header区,存放了CPU类型、Mach-O文件的具体类型、LoadCommands的数量、LoadCommands的大小等数据,如下所示:
-
- Load Commands区,存放了各段的名称、各段的虚拟内存地址、各段的虚拟内存大小、各段的在Mac-O中的偏移量、各段的在Mac-O中的偏移大小等数据 ,如下图所示,重点注意看
VM Address、VM Size
、File Offset、File Size
这两组数据 。
VM Address、VM Size
记录这此段在虚拟内存的位置和大小
File Offset、File Size
记录着此段在Mach-O中的位置和大小。
- Load Commands区,存放了各段的名称、各段的虚拟内存地址、各段的虚拟内存大小、各段的在Mac-O中的偏移量、各段的在Mac-O中的偏移大小等数据 ,如下图所示,重点注意看
文章图片
image
-
- Data区,存放着各段数据
-
- 我们观察Mach-O文件的
Load Commands
各段的描述信息,重点观察VM Address、VM Size
、File Offset、File Size
,就可以知道Mach-O在虚拟内存中是如何存放的了
- 我们观察Mach-O文件的
-
- 例如下图中的
LC_SEGMENT_64(__PAGEZERO)
段,我们可以看出来,PAGEZERO
段在虚拟内存中是从0x0
开始,大小是0x100000000
;PAGEZERO
段在Mach-O中是从0x0
开始,大小是0x0
,也就是说PAGEZERO
在Mach-O中实际不存在数据 ,只有加载到内存后才会展开。
文章图片
image
- 例如下图中的
-
- 例如下图中的
__TEXT
段,我们可以看出来,TEXT
段在虚拟内存中从0x10000000
开始,大小是0x6B8C000
;TEXT
段在Mach-O中从0x0
开始,大小是0x6B8C000
文章图片
image
- 例如下图中的
-
- 例如下图中的
__DATA
段 ,我们可以看出来,DATA
段在虚拟内存中从0x106B8C000
开始,大小是0x1820000
;TEXT
段在Mach-O中从0x6B8C000
开始,大小是0x1350000
文章图片
image
- 例如下图中的
-
- 综上所述 ,我们就可以绘制出比较直观的图来表示,如下图所示:
文章图片
image 四、使用ASLR时,Mach-O文件是如何载入内存的?
-
- ASLR会在虚拟内存中的最开始的位置产生了随机偏移量,每次载入内存时,偏移量都会不同,导致后续各段的地址都会增加此偏移量,以增加攻击者预测地址的难度
-
- 例如:ASLR产生的随机偏移量是
0x5000
,那么Mach-O载入虚拟内存中后,就如下图所示:
文章图片
image
- 例如:ASLR产生的随机偏移量是
-
- 苹果从
iOS 4.3
之后引入了ASLR技术,也就说把Mach-O载入内存后,所有的地址都会经过ASLR偏移
- 苹果从
-
- 我们使用MachOView工具静态分析看到的
VM Address
,是未经过ASLR偏移的虚拟内存地址,也就是说MachOView中的VM Address
加上ASLR偏移量
才是虚拟内存中的真正地址
- 我们使用MachOView工具静态分析看到的
-
- 总结一下:
- Hopper、IDA、MachOView等工具中的看到的地址都是未使用ASLR的VM Address,所以要想知道虚拟内存中真正地址,就需要再加上ASLR偏移量
- 虚拟内存中的真正地址 = ASLR偏移量 + PAGEZERO的大小 + Mach-O中的偏移量 (其中PAGEZERO的大小就是VM Size,Mach-O中的偏移量就是Offset,因为PAGEZERO到内存中才会展开,所以要加上PAGEZERO的大小)
- 虚拟内存中的真正地址 = ASLR偏移量 + 用工具看到的VM Address
- 虚拟内存中的真正地址 = ASLR偏移量 + Mach-O文件中的VM Address
- Mach-O文件中的File Offset = 虚拟内存中的真正地址 - ASLR偏移量 - PAGEZERO的大小
-
- 想要得到ASLR偏移量,可以在使用LLDB命令
image list -o -f | grep Mach-O文件名称
,来查看加载到内存中的ASLR偏移量。(注意:ASLR是随机的,每次加载到内存都不一样)
- 想要得到ASLR偏移量,可以在使用LLDB命令
-
- 按照上一篇的方法,进入到WeChat的
lldb环境
- 按照上一篇的方法,进入到WeChat的
-
- 使用LLDB指令
image list -o -f | grep Mach-O文件名称
,获取ASLR偏移量,如下图所示,本次载入内存的偏移量是0x27c0000
文章图片
image
- 使用LLDB指令
-
- 用Hopper等工具静态分析出某个函数在虚拟内存中的函数地址,这里的函数地址是静待分析出来的,也就是未经过ASLR偏移的地址,并不能直接用于打断点,如下图所示,
-[BaseMsgContentViewController SendTextMessageToolView:]
函数的未ASLR偏移的函数地址是0x1002aec90
image
- 用Hopper等工具静态分析出某个函数在虚拟内存中的函数地址,这里的函数地址是静待分析出来的,也就是未经过ASLR偏移的地址,并不能直接用于打断点,如下图所示,
-
- 使用LLDB指令
breakpoint set -a 函数地址
,给某个函数打断点,这里的函数地址指的是虚拟内存中的真实函数地址,也就是说这里的函数地址,是ASLR偏移量+静待分析的函数地址,也就是0x1002aec90+0x27c0000
,完整的LLDB指令就是breakpoint set -a 0x1002aec90+0x27c0000
,如下图所示:
文章图片
image
- 使用LLDB指令
-
- 打完断点之后,当App触发此断点时,就会卡住,以便我们输入LLDB调试,例如,我这里对微信的发送消息的方法
-[BaseMsgContentViewController SendTextMessageToolView:]
打了断点,之后每次发消息时都会卡主,以便我们继续输入LLDB指令调试
- 打完断点之后,当App触发此断点时,就会卡住,以便我们输入LLDB调试,例如,我这里对微信的发送消息的方法
-
- 如果不想要断点了,可以用
breakpoint delete 断点编号
删除断点;可以通过breakpoint list
命令,列出所有的断点编号
- 如果不想要断点了,可以用
extern intptr_t_dyld_get_image_vmaddr_slide(uint32_t image_index);
一般使用方法如下:
uint32_t c = _dyld_image_count();
for (uint32_t i = 0;
i < c;
i++) {
intptr_t index= _dyld_get_image_vmaddr_slide(i);
}
哪里使用? ALSR决定了虚拟地址在内存中会发生的偏移量。例如一个 segment_command_64 的vmaddr 字段决定了其在虚拟内存中的地址。但是其真正的内存地址则是vmaddr + ALSR。
在fishhook中计算程序基址,使用动态符号表的虚拟地址和偏移来计算基址,具体算法如下:
基址 = __LINKEDIT.VM_Address - __LINK.File_Offset + silde的改变值
其中的silde就是由于ALSR所产生的随机偏移量。
签名机制 Bang's Blog:iOS App 签名的原理
iOS 签名机制
目的 先来看看苹果的签名机制是为了做什么。在 iOS 出来之前,在主流操作系统(Mac/Windows/Linux)上开发和运行软件是不需要签名的,软件随便从哪里下载都能运行,导致平台对第三方软件难以控制,盗版流行。苹果希望解决这样的问题,在 iOS 平台对第三方 APP 有绝对的控制权,一定要保证每一个安装到 iOS 上的 APP 都是经过苹果官方允许的,怎样保证呢?就是通过签名机制。
一、 通过App Store安装
文章图片
image.png
- 由苹果生成一对公私钥,公钥内置与iOS设备中,私钥由苹果保管。
- 开发者上传App给苹果审核后,苹果用私钥对App数据进行签名,发布至App Store。
- iOS设备下载App后,用公钥进行验证,若正确,则证明App是由苹果认证过的。
文章图片
image.png
- 开发时需要真机测试时,需要从钥匙串中的证书中心创建证书请求文件(CSR),并传至苹果服务器。
- Apple使用私钥对 CSR 签名,生成一份包含Mac公钥信息及Apple对它的签名,被称为证书(CER:即开发证书,发布证书)。
- 编译完一个App后,Mac电脑使用私钥对App进行签名。
- 在安装App时,根据当前配置把CER证书一起打包进App。
- iOS设备通过内置的Apple的公钥验证CER是否正确,证书验证确保Mac公钥时经过苹果认证的。
- 再使用CER文件中Mac的公钥去验证App的签名是否正确,确保安装行为是经过苹果允许的。
注: 证书请求文件(CertificateSigningRequest.certSigningRequest),用于绑定电脑,文件中应该有Mac电脑的公钥。三、通过Ad-Hoc正式打包安装 Xcode打包App生成ipa文件,通过iTunes或者蒲公英等第三方发布平台,安装到手机上。流程步骤基本和真机调试相同,差别在于第4步:
- 开发时需要打包测试或发布时,需要从钥匙串中的证书中心创建证书请求文件(CSR),并传至苹果服务器。
- Apple使用私钥对 CSR 签名,生成一份包含Mac公钥信息及Apple对它的签名,被称为证书(CER:即开发证书,发布证书)。
- 编译完一个App后,Mac电脑使用私钥对App进行签名。
- 编译签名完之后,要导出ipa文件,导出时,需要选择一个保存的方法(App Store/Ad Hoc/Enterprise/Development),就是选择将上一步生成的CER一起打包进App。
- iOS设备通过内置的Apple的公钥验证CER是否正确,证书验证确保Mac公钥是经过苹果认证的。
- 再使用CER文件中Mac的公钥去验证App的签名是否正确,确保安装行为是经过苹果允许的。
附加一些东西 通过真机调试安装和证书打包安装,不加限制,可能会导致被滥用(不通过App Store,只通过第三方发布平台就能安装),因此苹果加了两个限制:在苹果注册过的设备才可以安装;签名只针对某一个App。
在上述第4步,打包证书进App中时,还需要加上允许安装的设备ID和App对应的APPID等数据(Profile文件)。
根据数字签名的原理,只要数字签名通过验证,第 5 步这里的设备 IDs / AppID / Mac公钥 就都是经过苹果认证的,无法被修改,苹果就可以限制可安装的设备和App,避免滥用。
苹果还要控制iCloud/push/后台运行等,这些都需要苹果授权签名,苹果把这些权限开关统称为:Entitlements,去让苹果授权。
因此证书中可能包含很多东西,不符合规定的格式规范,所以有了Provisioning Profile(描述文件),描述中包含了证书以及其他所有的信息及信息的签名。
四种签名方式的区别
文章图片
image.png 总结一下最终流程
文章图片
image.png 1、在你的 Mac 开发机器生成一对公私钥,这里称为公钥L,私钥L。L:Local
2、苹果自己有固定的一对公私钥,私钥在苹果后台,公钥在每个 iOS 设备上。这里称为公钥A,私钥A。A:Apple
3、把公钥 L 传到苹果后台,用苹果后台里的私钥 A 去签名公钥 L。得到一份数据包含了公钥 L 以及其签名,把这份数据称为证书(cer)。
4、在苹果后台申请 AppID,配置好设备 ID 列表和 APP 可使用的权限,再加上第③步的证书,组成的数据用私钥 A 签名,把数据和签名一起组成一个 Provisioning Profile 文件,下载到本地 Mac 开发机。
5、在开发时,编译完一个 APP 后,用本地的私钥 L 对这个 APP 进行签名,同时把第④步得到的 Provisioning Profile 文件打包进 APP 里,文件名为 embedded.mobileprovision,把 APP 安装到手机上。
6、在安装时,iOS 系统取得证书,通过系统内置的公钥 A,去验证 embedded.mobileprovision 的数字签名是否正确,里面的证书签名也会再验一遍。
确保了 embedded.mobileprovision 里的数据都是苹果授权以后,就可以取出里面的数据,做各种验证,包括用公钥 L 验证APP签名,验证设备 ID 是否在 ID 列表上,AppID 是否对应得上,权限开关是否跟 APP 里的 Entitlements 对应等。
其他人想要编译签名App时应怎么做?
简单就是把私钥给他。私钥也是从 钥匙串 中导出,就是.p12文件,其他Mac导入私钥后就可以正常使用了。
查看ipa包中注册的设备ID
解压.ipa文件,得到App数据包,显示包内容,找到embedded.mobileprovision文件所在目录,运行命令security cms -D -i embedded.mobileprovision
文章图片
image.png
推荐阅读
- 2020-04-07vue中Axios的封装和API接口的管理
- 如何在Mac中的文件选择框中打开系统隐藏文件夹
- iOS中的Block
- 单点登陆
- 操作系统|[译]从内部了解现代浏览器(1)
- 游乐园系统,助力游乐园管理
- 中国MES系统软件随工业化成长
- 推荐系统论文进阶|CTR预估 论文精读(十一)--Deep Interest Evolution Network(DIEN)
- 记录iOS生成分享图片的一些问题,根据UIView生成固定尺寸的分享图片
- Cesium|Cesium 粒子系统学习