# IOS逆向基础 **Repository Path**: Edwrard/IOS-RE ## Basic Information - **Project Name**: IOS逆向基础 - **Description**: IOS逆向基础介绍 - **Primary Language**: Objective-C - **License**: GPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 3 - **Created**: 2020-08-28 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # IOS逆向文档 ## 背景说明 ​ 爬虫项目有时候需要抓取APP调用的接口数据,如果接口入参和返回值加密了,抓包后也不清楚具体数据如何,所以,需要搞清楚接口的加密、解密方式,然后才能对应编写爬虫代码,模拟请求。因此需要查看APP调用接口的逻辑,所以需要逆向APP。 ## 前期工具和设备的准备 1. 需要MAC电脑一台,并安装xcode好开发环境。 2. iPhone手机一部(最好是越狱机,不越狱也可以,因为逆向需要砸过壳的APP,大多数的砸过壳的APP可以在MAC端的PP助手软件中下载,如果没有的话,就需要越狱手机砸壳了。) 3. xcode安装MonkeyDev工具,地址:https://github.com/AloneMonkey/MonkeyDev 4. 越狱手机可以安装Clutch工具,砸壳用。地址:https://github.com/KJCracks/Clutch 5. 电脑安装Hopper Disassembler或者IDA等反汇编工具,用于静态代码分析。 6. 安装Fiddler或者Charles等抓包工具(可选) ## 逆向基础知识准备 ​ 逆向需要基本了解oc语言(如怎样调用方法,常用的类的常用方法等等)、xcode lldb常用命令、基本的arm64汇编指令等,关于arm汇编可以参考一下简书的文章https://www.jianshu.com/p/19a276a503fb 这里不过多说明。 ​ 另外逆向APP如果是swift代码编写的项目的话(具体表现为Hopper解包后类名开头是_T,有点像乱码,如:testReturnVoidWithaId编译成 _TFC9TestSwift15TestASwiftClass21testReturnVoidWithaIdfS0_FCSo6UIViewT_),难度很大,和OC的动态性不同,swift编译后的传参返回值等都是在内存中等方式,不像oc可以直接在寄存器中看到方法名,入参,返回值等等。这里只说的是OC项目。oc的方法调用是类似于发送消息的形式,即objc_msgSend()方法,具体表现会xcode断点后汇编代码后面备注为objc_msgSend,即函数调用,所以后面逆向的时候可以重点关注这样的情况。 ## 逆向的流程 1. 如果是需要看接口参数加密的话,可以先抓包,看看目标接口,参数,了解一下特性,用于后面的静态分析。 2. 砸壳,因为在App Store上面下载的应用都被苹果加壳过了,需要砸壳才能解包。可以在PP助手下载砸壳后的应用,如果没有的话用越狱手机安装clutch砸壳。 3. 砸壳后得到ipa安装包,将其放入xcode的MonkeyDev工程的targetApp目录,接入真机,然后编译执行,第一次会提示信任,需要在手机设置-通用-描述文件与设备管理中信任APP,成功后会在targetApp目录看到多出来的程序包,然后将其用Hopper或者IDA打开。 4. 等待Hopper分析完成后,可以使用第一步的特征关键字等搜索,找到可能的逻辑类或者方法。 5. 在xcode中运行MonkeyDev工程,让APP跑起来,如果无法执行,可能是APP使用了反调试方法,参考后面说明。 6. 在xcode的输出界面点击暂停按钮或者UI按钮可以让程序中断,然后执行LLDB命令,设置断点,查看参数,函数返回值等主要操作。具体看下面说明。 ## APP调试的大致流程 ​ 首先通过静态分析,或者UI分析,找到相应的逻辑对应的类名,方法名,然后通过中断APP,执行如下LLDB命令: ```objective-c po [类名 _shortMethodDescription] ``` 查看该类的详细信息,方法,以及方法对应的内存地址。然后给目标方法打上断点,如下 ```objective-c b 方法的地址 ``` 然后结束中断(输入LLDB命令c然后回车(即continue的),或者在xcode中操作都可以),之后app界面即可正常操作,然后点击按钮触发逻辑,如点击登录按钮,如果类和方法是正确的话,此时xcode会进入刚才打上断点的方法,可以使用如下LLDB命令 ```objective-c //该命令表示读取x0寄存器的值,然后转字符串打印,po为 print object的缩写,即打印对象,一般打印oc对象会使用po命令,打印基本类型可以用p命令,打印16进制用p/x,详细介绍后面再说。由于oc语言的特性,x0用于存调用对象(即:this),或者是存函数的返回值(如果函数执行完后)。另外7个入参以内的函数,入参会分别用x0~x7寄存器存储,超过7个会用内存地址传参(这种情况后面再说) po (char *)$x0 ``` 查看方法的信息,或者读取x1(方法名),x2~x7(方法的入参,如果有的话)等等。 然后搜索该方法汇编代码内的objc_msgSend关键字,即方法调用,打上断点。然后通过LLDB命令 ```objective-c //下一步next n //继续continue c //step into si ``` 一步步debug,查看方法入参,返回值。重复上述步骤,找到APP的执行逻辑。 ## 反反调试 ​ 有些app使用了反调试技术,检测到调试状态时候回自动退出,不过大多数情况下MonkeyDev已经做了处理,如果发现APP中断退出的情况,可以做以下尝试,打开工程目录的AntiAntiDebug目录下的AntiAntiDebug.m文件,拉到最下面,找到如下方法: ```objective-c __attribute__((constructor)) static void entry(){ NSLog(@"[AntiAntiDebug Init]"); rebind_symbols((struct rebinding[1]){{"ptrace", my_ptrace, (void*)&orig_ptrace}},1); rebind_symbols((struct rebinding[1]){{"dlsym", my_dlsym, (void*)&orig_dlsym}},1); //some app will crash with _dyld_debugger_notification //rebind_symbols((struct rebinding[1]){{"sysctl", my_sysctl, (void*)&orig_sysctl}},1); rebind_symbols((struct rebinding[1]){{"syscall", my_syscall, (void*)&orig_syscall}},1); } ``` 默认情况下方法内部第三个rebind_symbols注释掉了,取消注释再重新执行 ## LLDB以及常用命令 ```objective-c //打印类的全部方法以及地址 po [类名/地址 _shortMethodDescription] // 转base64 po [$x0 base64EncodedStringWithOptions:0] // 加密key转base64 po [NSString getPublicKeyBitsFromKey:$x0] //给SomeClass类的全部方法打上断点,正则表达式 br set -r '\[.*SomeClass.*\]' //查看手机当前运行的镜像的基地址,该地址加上Hopper中的地址即表示函数的实际地址,有些情况无法使用_shortMethodDescription的时候可以使用该方法打断点 image list -f -o //以16进制,8位方式读取内存地址开始的值。其中-f为format,-s为size,内存传参可以用这种方式查看参数 memory read -f x -s 8 内存地址 ``` ## Hook方法 Hook(俗称钩子) 其实就是改变程序执行流程的一种技术的统称!iOS 开发中几种常见的Hook 方式有:Method Swizzle、fishhook、logos,这里介绍一下logos,其他自行了解。 常用到的一些语法 ```objective-c //HOOK 某个类里面的某个方法 %hook 类名 - (void)方法名:(id)arg1 .... { } %end //为某个类添加新的方法 %hook class_name // 添加一个响应事件 %new -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ [self.view endEditing:YES]; } // 添加一个类方法 %new +(void) new_func_name { NSLog(@"这是一个类方法!"); } %end //hook swift方法 %hook AnyNameYouLike (void)theFuncYouWantToHook { } %end %ctor { %init(AnyNameYouLike = objc_getClass(“SYHDAppPayProject.MainTabBarController”);); } //构造函数和析构函数 %ctor 构造函数 %dtor 析构函数 //组的概念 %group group1 %hook class_name - (void)func_name:(id)arg1 .... { //修改替换成的内容 } %end %end %group group2 %hook class_name - (void)func_name:(id)arg1 .... { //修改替换成的内容 } %end %end // 定义了组就必须对其初始化 %ctor{ NSString * version = [UIDevice currentDevice].systemVersion; if(version.doubleValue >= 10){// 当iOS版本大于10则使用组2的Logos代码,否则使用组1的 %init(group2) }else{ %init(group1) } } //常用的宏 %log //打印原始方法的所有参数信息 %orig //执行原始的方法 [%c(ViewController) click]; //%c获取类 ``` ## 技巧 - 查看block回调地址,如下: ```objective-c <__NSStackBlock__: 0x16fd26ac8> (lldb) memory read --size 8 --format x 0x16fd26ac8 0x16fd26ac8: 0x00000001a0d11558 0x00000000c2000000 0x16fd26ad8: 0x00000001022d4638 0x00000001037ab4f0 0x16fd26ae8: 0x0000000126f4a1c0 0x0000000126ac9ae0 0x16fd26af8: 0x00000001821e7f00 0x0000000124d31400 其中,第三个地址即为block回调地址:0x00000001022d4638 可以断点该地址 该地址减去基地址偏移之后可以在hopper中找到代码。 //openSSH默认密码 alpine //查看手机上安装的包,数字列表 clutch -i //砸壳 clutch -d xx(数字) ``` - SSL Pining抓不到包的原理和解决方案,查看如下文章: ``` https://www.jianshu.com/p/23545f8d36d2 https://www.jianshu.com/p/d5490c153420 ``` - 接口破解如何快速定位相关类和逻辑? ``` 抓包工具中抓包后,查看接口相关参数,如url,参数,请求头中的比较特别的字段,然后再Hopper的字符串搜索中搜索这些字段,一般可以快速定位到调用接口的相关逻辑的方法。 ```