Android部分

基础知识

1)安卓分层

简单提一下安卓分层,这个点知道了更好,不知道也无所谓。毕竟我们不是开发,只是为了避免下述情况:
我要学习so文件HOOK。一波百度 “HOOK so层”之后,出现了一个“native”。点进去一看,其内容是对so文件hook。

那native是啥?这就要提一下安卓的分层。
Android采用分层的架构,分为四层,从高层到底层分为应用程序层(app+System apps),应用程序框架层(Java API Framework),系统运行库和运行环境层(Libraries + android Runtime)和Linux核心层(HAL+ Linux Kernel),如下图所示:

细分之后如下图:

其中,Native层(本地服务)这部分为常见一些本地服务和一些链接库等。这一层的一个特点就是通过C和C++语言实现。比如我们现在要执行一个复杂运算,如果通过java代码去实现,那么效率会非常低,此时可以选择通过C或C++代码去实现,然后和我们上层的Java代码通信(这部分在android中称为jni机制,下面会提)。又比如我们的设备需要运行,那么必然要和底层的硬件驱动交互,也要通过Native层。
安卓native层,通俗来说就是对lib目录下的so文件,so文件是Android NDK动态链接库,是二进制文件,作用相当于windows下的.dll文件。

2)so 文件

so 是 shared object 的缩写,见名思义就是共享的对象,机器可以直接运行的二进制代码。so 主要存在于 Unix 和 Linux 系统中。它是 c/c++ 实现的功能函数集合,并对外提供标准的接口,外层可以通过这个接口调用c/c++的代码。

Android 系统为什么要使用.so文件呢?
Android 系统应用基本都是基于 Java 语言开发,而Java 语言是不能直接访问Android系统底层的硬件接口。而Android系统中可以通过 JNI 和 硬件访问服务去访问系统底层的硬件接口。比如:开启蓝牙、关闭蓝牙等。

安卓so文件存在与app的lib目录下,利用解压软件查看目录结构如下:

该目录下存在各个CPU构架的分类。不同CPU架构的android手机加载时会在libs下找自己对应的目录,从对应的目录下寻找需要的so文件。如果没有对应的目录,就会去armeabi下去寻找,如果已经有对应的目录,却没有找到对应的so文件,也不会去armeabi下去寻找了。
目前主流的Android设备主要是 armeabi-v7a 架构的,然后是 x86 和 armeabi 了。如果同时包含了 armeabi, armeabi-v7a和x86,所有设备都可以运行。这个点是为了寻找将要HOOK的so文件,armeabi-v7a构架的手机去HOOK其他构架的so文件,一句话就是“可以,但是没必要。非要改也不拦着。”这边贴一个兼容内容:

以armeabi-v7a设备为例,该Android设备会优先寻找libs目录下的armeabi-v7a文件夹。如果只有armeabi-v7a文件夹而没有 so文件会报错;如果找不到armeabi-v7a文件夹,则寻找armeabi文件夹,兼容运行该文件夹下的so,但是不能兼容运行x86的so。所以项目中如果只含有x86的so,在armeabi和armeabi-v7a也是无法运行的。

3)JNI

定义:Java Native Interface,即 Java本地接口
作用:使得Java 与 本地其他类型语言(如C、C++)交互,即在 Java代码里调用 C、C++等语言的代码或 C、C++代码调用 Java 代码

特别注意:
JNI是 Java 调用 Native 语言的一种特性
JNI 是属于 Java 的,与 Android 无直接关系

JNI 代码经过编译之后在Unix/Linux系统上就会生成 .so文件,通过调用Java代码调用.so中的接口方法即可实现硬件的访问。
在 Android 系统下 JNI 可以通过NDK快速实现。

我们首要目的就是要了解.so文件的作用是用来访问系统底层的接口,而Android应用基本都是Java开发,而Java不支持直接访问硬件,但是Android提供了两种方式去访问接口:JNI和硬件访问服务。JNI的方式编译后会产生.so文件。同时Android还给开发者提供了NDK这个开发工具包,开发者可以使用NDK快速实现 JNI的功能。

4)加载过程

关注上图中存在的几个关键字。

IDA基础使用

IDA这个工具闻名遐迩,不过现在只需要其一小部分。吾爱上有其7.0版本的破解版,注意的是在安装的时候中文目录的问题,在包含中文目录时会出现40343这个错误。

本项目阶段使用Ghidra,分析相对简单,无复杂的操作细节,相较IDA会有一些小细节,故尔此处以IDA分析为例。

1.运行ida.exe(不是ida64.exe),打开so文件。

2.常用区域为红线选择区域,网路上也存在很多ida插件来辅助进行操作,这个东西在需要的时候,度娘基本可以解决。

Function window:so文件中包含函数的显示区域;
IDA View-A:so文件汇编语言显示区域;
Hex View-1:so文件16进制显示区域;
Imports/Exports:so文件应用/输出函数显示区域。

3、F5快捷操作

选择查看的函数,因为是原始显示为汇编语言。汇编不咋会的可使用F5快捷操作将其转化为伪C代码。
转化前:

转化后:

测试环境

推荐只安装Frida的真机,与XP框架同时使用会存在一定蜜汁问题。

so层导出函数的Hook

先贴上基本的Hook代码:

# -*- coding: UTF-8 -*-
import frida, sys
jsCode = """
Java.perform(function(){var nativePointer = Module.findExportByName("libhello.so", "Java_com_xiaojianbang_app_NativeHelper_add");          send("native: " + nativePointer);        Interceptor.attach(nativePointer, {onEnter: function(args){send(args[0]);send(args[1]);send(args[2].toInt32());send(args[3].toInt32());send(args[4].toInt32());     },onLeave: function(retval){send(retval.toInt32()); }   });
});
"""
def message(message, data):         if message["type"] == 'send':     print(u"[*] {0}".format(message['payload']))    else:       print(message)
process = frida.get_remote_device().attach("com.xiaojianbang.app")
script= process.create_script(jsCode)
script.on("message", message)
script.load()
sys.stdin.read()

对比上述代码与前次JAVA层代码,发现两者的变化不大,主要的改变都在jscode中。故本此只对js代码内容进行

描述。其余代码除变更外不做过多赘述。

jsCode = """
Java.perform(function(){            var nativePointer = Module.findExportByName("libhello.so", "Java_com_xiaojianbang_app_NativeHelper_add");          //代码中Module.findExportByName为Frida可使用JavaScript Api中Module中的findExportByName方法Frida可以使用的API及API中方法,在官方文档中有相应的列举但不是很全面。故而需要在以后的使用
学习过程中进行爬贴积累。        //findExportByName方法有两个参数,第一个为hook的so文件名,第二个为hook的函数名。在获取到两个值之后返回hook函数名的地址           send("native: " + nativePointer);        //打印获取到的地址值         Interceptor.attach(nativePointer, {         //对获取到地址函数进行hook,Interceptor.attach函数需要两个参数。第一个为获取到的地址值,第二个为{}中内容。        onEnter: function(args){            //onEnter,函数调用前进入,用于hook函数的函数参数的修改。args可不用更改。    send(args[0]);send(args[1]);send(args[2].toInt32());send(args[3].toInt32());    send(args[4].toInt32());    //上节对so文件进行伪C代码转换后发现Hook的函数有5个参数值,故在此处输出5个。      },      onLeave: function(retval){      //onLeave,函数调用后进入,用于hook函数的返回值修改。retval可不更改   send(retval.toInt32());     }   });
});
"""

运行hook脚本其结果如下:

函数参数返回值修改

本节开始利用hook对函数相关值进行修改,过程中开始涉及一些特定的函数使用。需要对Frida官方的JavaScript API内容进行熟悉,本文所使用基本可在官方文档中找解释。今后在对其他so文件等内容进行Hook时,官方文档是个比较重要的内容,获取方式有官方英文版S和看雪的中文翻译版。

核心代码如下;

jsCode = """
Java.perform(function(){            var nativePointer = Module.findExportByName("libhello.so", "Java_com_xiaojianbang_app_NativeHelper_add");          send("native: " + nativePointer);            Interceptor.attach(nativePointer, {     onEnter: function(args){send(args[0]);  send(args[1]);send(args[2].toInt32());send(args[3].toInt32());send(args[4].toInt32());args[4] = ptr(1000); //修改参数内容,ptr为new NativePointer。new NativePointer(s):通过 s 创建一个新的 NativePointer 对象, 可以是一个数字也可以 是一个包含以 "0x" 开头的十六进制的数字字符串.可以使用 ptr(s) 作为这个方法的简写。      send(args[4].toInt32());            },      onLeave: function(retval){  send(retval.toInt32()); retval.replace(10000);  //修改返回值内容。onLeave: function(retval): 被拦截函数调用之后回调,其中retval表示原始函数的返回值,retval是从NativePointer继承来的,是对原始返回值的一个封装,可以使用retval.replace(1337)调用来修改返回值的内容。需要注意的一点是,retval对象只在 onLeave函数作用域范围内有效,因此如果你要保存这个对象以备后续使用的话,一定要使用深拷贝来保存对象,比如:ptr(retval.toString())。       send(retval.toInt32());                 }       });
});
"""

运行结果如下:

枚举导入导出函数表

本处需要官方文档Module相关内容如下:

通过查询官方文档获取到遍历所使用的相关函数,只需添加循环即可输出结果。代码里加相关筛选代码,代码如下:

jsCode = """
Java.perform(function(){var imports = Module.enumerateImportsSync("libhello.so");for(i = 0; i < imports.length; i++) {if(imports[i].name == 'strncat'){//寻找名字为strncat的函数send(imports[i].name + ": " + imports[i].address);break;           }               }               var exports = Module.enumerateExportsSync("libhello.so");for(i = 0; i < exports.length; i++) {if(exports[i].name.indexOf('add') != -1){//寻找名字里包含add的函数send(exports[i].name + ": " + exports[i].address);        break;      }               }
});
"""

未加判断运行结果如下:

添加判断运行结果如下:

未导出函数HOOK

在so文件中存在部分函数不在导出列表中,偶尔也会需要HOOK该函数。而未导出函数是包含导出函数,及可用未导出函数方式去获取导出函数的地址。

代码内容如下:

jsCode = """
Java.perform(function(){                    var soAddr = Module.findBaseAddress("libhello.so");                  send('soAddr: ' + soAddr);       var MD5FinalAddr = soAddr.add(0x1768 + 1);        //1768为函数偏移量,表现形式见下图: send('MD5FinalAddr: ' + MD5FinalAddr);                       onEnter: function(args){        send(args[0]);  send(args[1]);                      },              onLeave: function(retval){  send(retval);               }           });
});
"""

偏移量IDA表现图:

执行结果如下:

Android脱壳

本节算是Frida安卓部分的结尾,前两节介绍了Frida如何HOOK安卓Java层、native层。也相信大家通过各自的渠道也学到了不少内容。本文旨在分享Frida脱壳的常规思路,大家可依据实际情况结合IDA编写代码。

1、 加壳

加壳可理解为通过Android动态加载机制,在运行时加载dex、jar、apk文件并执行。其过程大致如下:
A、壳代码先运行,进行初始化。
B、壳代码开始解密被保护的代码。
C、壳代码加载解密后的代码。
D、壳代码将程序控制权转交保护代码。
壳的强度指的是对dex、jar等文件的加密强度,以及部分反调试、反注入的强度。市面上不同的厂商有着自己的加密方式。

2、 dex文件

脱壳最终目的是为了还原动态加载的dex文件。所以,应对该dex的结构有一定的了解。首先放上google官方文档对dex解释的链接,里面相当详细的介绍了dex的格式的组成。(https://source.android.google.cn/devices/tech/dalvik/dex-format#header-item)。
了解了dex文件结构,可帮助我们分析出壳主要对dex那些内容做了修改,然后调试在dex文件加载过程中涉及这段数据的代码,为之后的修复做准备。

3、 dex文件加载

Android平台是基于Linux系统开发的,在应用层加载的是Dalvik专用的dex文件。在native加载的是elf格式。Dalvik虚拟机运行时不是直接加载dex文件,而是执行程序字节码。其步骤大致如下:
A、将dex文件结构优化成odex文件结构。
B、将odex文件结构解析成dexfile结构。
C、 将dexfile文件结构转化成Dalvik虚拟机需要的运行时的数据结构ClassObject*字节码,后解释器执行。

4、 so文件加载过程

在Android程序安装完成后,根据Android程序的结构,我们知道最主要的是classes.dex文件和程序自身加载的链接库so文件。Java代码通过system.loadlibrary(so文件名)语句加载动态链接库。在native层的加载过程是从nativeload()函数开始。

5、 脱壳思路

Dex和jar在加载时是解密状态,可定位到App在动态加载一个文件时的入口和出口,然后通过内存转储的方式获得被加载的dex文件。
在Android应用层、动态加载dex等文件是通过Android提供的dexclassloader的类构造方法完成。dexclassloader通过调用父类的构造方法,对输入的dex文件进行校验,最终会走的dvmdexfileopenfromfd()或dvmdexfileopenpartial()这两个native()函数中的一个,后获取一个dexfile结构体。
内存转储的方法可部分脱壳,对于专业加固的壳,dex被转储后不能被正常解析,这是需要分析以获取的dex,分析其异常点。根据dex加载等过程,判断壳在哪里做了桩点,需修复dex文件。

6、 简单脱壳相关代码

代码编写的思路为通过Android加载dex时的系统函数,分析dex文件加载在内存里的初始位置和总长度,并将内存里的内容保存在本地。
Android系统版本不同,打开dex文件的函数不同:
Android 4.0:libdvm.so::dexfileparse
Android 5.0及以上:libart.so::openmemory、libart.so::opencommon

依据各位对Frida的学习,我们在使用Frida是可直接编写js脚本,后利用:
frida -U -f appname -l XXXX.js --no-pause
可直接运行js脚本,至于Frida可以使用js的那些内容,可详见Frida官方的js api接口文档。在该文档中对相关接口作用做了详尽说明,脚本写法可参考上一文内容。不做过多的赘述。
接下来,我们按照上文思路开始(在已知安卓版本的前提下):

1)、 获取当前App所调用的so文件:

代码如下:

运行结果如下:

通过上述脚本可获取到app所使用的so文件、地址、大小、路径。方便后期复杂加壳相关so拉取。现在继续按照我们的思路继续向下走。

2)、 获取记载dex文件函数

代码如下:

运行结果如下:

在执行后可能会发现,多个函数名。但都不是我们提到的几个关键函数,那是因为这个函数被混淆了,在一般加壳时,app运行过程中只会调用提到的关键函数,一般情况下hook第一个函数即可。

3)、内存转储

代码如下:

运行结果如下:

通过以上结果,我们可以发现。该app转储下来多个dex文件,学名叫做multidex。目前个人对该技术也不是很了解,采用的方式还是一个个打开查看。

通过打开上述转储dex文件,发现还是存在非正常函数名。这说明通过内存转储的方式去获取的方式不是很精准,需要结合ida方式查看。推荐一篇大佬的文章https://mp.weixin.qq.com/s/Nb1BjJ-i7v7hBWHwEQ9Log

IOS部分

通过对Android Native层的相关内容的学习,大家应该对Frida的相关使用有了一定的经验,那恭喜你IOS部分,Frida的分析过程是很相似的:通过使用hopper、Ghidra等对IPA文件的头文件进行分析。 同时,也可使用class-dump,将相关类库文件拖出来,

但是以上文件内没有详细的代码内容,这个时候就需要我们使用hopper等工具去加载IPA文件里的二进制文件,然后查看伪C代码。

一般过程为:
1、打开Hopper工具
2、找到xx.app文件
3、右击xx.app文件,选择"显示包内容"
4、找到二进制文件
5、选中二进制文件,并选中拖放到Hopper工具里

以上,获取到相关代码后。就可以开始进行分析了。本文会在MonkeyDev使用部分进行描述,在Frida部分依靠Android的经验,将简要描述。

基础知识部分

通过工作学习,大家都晓得。 IOS开发有两种语言,objecive-c和Swift。本项目阶段要求能看懂下面的代码示例,了解一定的类知识。

objective-c

//导入头文件
#import <Foundation/Foundation.h> //入口函数
int main(int argc, const char * argv[]){@autoreleasepool//打印语句NSLog(@"HELLO");//调用方法//格式化字符串NSString * str = [NSString stringWithFormat;@"字符串"];NSLog(str);} return 0;
}

字符串前面要加@
方法调用使用中括号,一般情况下使用.

swift

//导入模块
import Foundation//打印语句
print("HELLO");//调用方法
//格式化字符串
let str = String(format:"字符串")
print(str)

swift语法结构类时 Java,后面无需跟分号,也不需要指定类型,和kotlin差不多

小总结

上面给大家简要写了一个语法示例,对两种语言的样子应该有了一个较为清晰的认知,这里说一下当前阶段我们为什么以Objc为主,而不是swift。原因很简单:
1、Objc发展已经相对完善,新版本迭代很少,版本兼容性好。适用于版本更新较多、追求稳定的项目。
2、Swift版本兼容较差,Swift4写的代码不一定可以在Swift5中编译通过。

熟练使用苹果官网给文档,本人在刚开始学习时比较依赖百度,后面发现阅读相关官类的文档,解决问题比百度要节省很多时间:https://developer.apple.com/search/?q=UITextView

MonkeyDev部分

简述

MonkeyDev是一个极为方便的逆向调试平台,集众家所长
主要包含四个模块
Logos Tweak – 使用theos提供的logify.pl工具将.xm文件转成.mm文件进行编译,集成了Cydia Substrate,它的主要作用是针对OC方法、C函数以及函数地址进行HOOK操作。
CaptainHook Tweak – 使用CaptainHook提供的头文件进行OC函数的Hook以及属性的获取。
Command-line Tool – 可以直接创建运行于越狱设备的命令行工具。
MonkeyApp – 自动给第三方应用集成Reveal、Cycript和注入dylib的模块,支持调试dylib和第三方应用,只需要准备一个砸壳后的ipa或者app文件即可。

安装与踩坑

官方安装地址:https://github.com/AloneMonkey/MonkeyDev/wiki

踩过的坑:

1、Types.xcspec not found

错位内容为:

Creating symlink to Xcode templates...
Modifying Bash personal initialization file...
File /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications/MacOSX Package Types.xcspec not found

解决方式

sudo ln -s /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/PrivatePlugIns/IDEOSXSupportCore.ideplugin/Contents/Resources /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications

参考地址:https://github.com/AloneMonkey/MonkeyDev/issues/266

2、编译报错’Cycript/Cycript.h’ file not found

参考地址:https://github.com/AloneMonkey/MonkeyDev/issues/309

3、编译报错file not found: /usr/lib/libstdc++.dylib

参考地址:https://github.com/AloneMonkey/MonkeyDev/issues/279

4、编译报错’Executable Not Found’

执行command + shift + k,清理下build data 就可以正常运行。但是此方法不解决根本问题,每次遇到该问题需重复执行。

环境准备

1、手机连接

通过SSH连接iPhone时,有两种方式:SSH+IP地址、USB连接

日常IOS HOOK测试建议采用USB直接链接。相较于IP地址连接,减少了iPhone与mac的网络环境要求,不需要在每次调试时去查看iPhone的IP地址。

USB连接过程:

使用usbmuxd工具,将本地的2222端口转发到iOS的22端口

brew install usbmuxd
iproxy 2222 22

为啥是2222端口?是因为在MonkeyDev的相关工具套件中包含有frida-ios-dump项目,该项目可在GitHub进行单独下载。项目中dump.py文件中有如下内容:

User = 'root'
Password = 'alpine'
Host = 'localhost'
Port = 2222

2、Reveal安装与使用

3、Hopper安装(反编译,查看各个类的方法)

4、Logos语法介绍(Hook原代码)

https://iphonedev.wiki/index.php/Logos

实例

本节以斗鱼APP为例,简单串一下使用流程。因为也是自己摸索这学习,耽误了很多时间。首先,先看一下最终的效果:

本例目的是HOOK斗鱼用户快速登录时的手机号。

前提准备

1、已经完成砸壳子的目标APP。工具:frida-ios-dump

2、目标APP相关代码》工具:class-dump

这部分没啥好讲的,百度有一堆使用教程

HOOK过程

创建项目

启动Xcode新建一个项目。

之前,如果你已经完成了MonkeyDev的安装步骤,在你的新建项目下面会出现相关项目模版。我们这里新建一个MonkeyAPP。

填写相关信息,Team信息需要自己准备。其他字段按字面意思填写,点击下一步将项目放在一个自己能找到的地方。

进入项目内容,进入部分设置和文件导入。

点击运行,一般情况下,你的项目会报错。我遇到的报错内容,已在前面提到了,按步骤操作就好。导入相关框架文件,可试着重新打开项目。

UI分析,定位函数

解决完所有报错后,运行你的项目。等待一段时间,完成文件安装后,完成如下操作。

同时,将APP打开将要分析函数相关界面,本例即快速登录界面。

完成上述步骤后,经过一段时间,你的Xcode界面会出现如下界面:

圈红位置,可进行相关视图的改变。经过相关文字提示,可快速定位到下相关功能类:

这里可能会问为什么是DYQuickMobileTextField而不是UITextFieldLabel,简单解释一下就是UITextFieldLabel算是一个公共调用,在其他类下也可使用,现在我们去分析一下DYQuickMobileTextField这个类文件。

通过类文件可以清晰看到DYQuickMobileTextField继承的父类信息、目标函数reformatAsPhoneNumber。斗鱼开发大大目的性比较直接,也方便快速定位到目标函数。

接下来可以准备写HOOK代码来验证一下,当前函数是否是我们需要的。首先,返回项目目录,修改目标文件的类型。该xm文件即是我们要编写HOOK代码的地方。

重新选择该文件使修改生效,删除部分内容,留下如图内容:

编写HOOK代码

通过前面步骤,我们可以开始,相关函数HOOK代码的编写。选择类文件中函数行至DouyuDomeDylib.xm文件中,进行相关内容的变形

接下来,就可以根据我们的需求,在函数体内写代码。这里写了一个简单的输出。

// See http://iphonedevwiki.net/index.php/Logos

#import <UIKit/UIKit.h>

#import <Foundation/NSObjCRuntime.h>

%hook DYQuickMobileTextField

- (void)reformatAsPhoneNumber:(id)arg1{

NSLog(@“手机号:%@”,arg1);

}

%end

执行结果

HOOK到的结果不是自己想要的结果,我们进一步进行输出优化,这一步自己在搞的时候,花了整一下午的时间。有个小建议就是,多看objc代码中相关类的代码,很多输入、输出等要求很清晰。

这个分析过程,这里大致分析过程如下:

对DYLoginPhoneInputView为相关输入的展示类,发现其没有定义自己的文件展示,选择使用父类继续。该父类是apple官类。那我们可以猜想,斗鱼对于text相关细节的展示也是使用的官类。

APPLE相关官类为:UITextView。地址为:https://developer.apple.com/documentation/uikit/uitextview?language=objc

故尔,我们可以采用正向开发思路,将目标函数内容arg1赋给UITextView类型的 *pwd。最终优化代码如下:

// See http://iphonedevwiki.net/index.php/Logos

#import <UIKit/UIKit.h>

#import <Foundation/NSObjCRuntime.h>

%hook DYQuickMobileTextField

- (void)reformatAsPhoneNumber:(id)arg1{

UITextView * pwd = arg1;

NSLog(@“手机号:%@”,pwd.text);

}

%end

执行结果;

Frida部分

Frida基础HOOK

Python基础脚本

Hook.py

# _*_ coding: utf-8 _*_import logging
import frida
import syslogging.basicConfig(level=logging.DEBUG)def on_message(message, data):if message['type'] == 'send':print("[*] {0}".format(message['payload']))else:print(message)with open("ios_hook.js", 'r', encoding='utf-8') as f:sta = ''.join(f.readlines())
rdev = frida.get_device(id ="设备id")
print("设备连接成功")
session = rdev.attach('app包名')  #app包名
print("连接成功")
print(session)
script = session.create_script(sta)
print(script)
def show(message,data):print(message)
script.on("message",show)# 加载脚本
script.load()
sys.stdin.read()

HOOK方法入参

Hook.js

function hook_function_input(){var PDDURLRequestSetHeadersHook = eval('ObjC.classes.类名["- 方法名"]')  #xxx["- xxx:xx:xxx:xx:xxxx:"]'Interceptor.attach(PDDURLRequestSetHeadersHook.implementation, {onEnter: function(args) {// args[0] is self// args[1] is selector (SEL "sendMessageWithText:")// args[2] holds the first function argument, an NSStringconsole.log(`2----${ObjC.Object(args[2])}`)console.log(`3----${ObjC.Object(args[3])}`)console.log(`4----${ObjC.Object(args[4])}`)console.log(`5----${ObjC.Object(args[5])}`)console.log(`6----${ObjC.Object(args[6])}`)//logBacktrace(this.context, "operationFromDict:")}, onLeave: function(retval) {console.log(`factorSign-result:${ObjC.Object(retval)}\n`);}});
}

说明:

在进行特定方法相关hook时,编写脚本时要在其名称前加"- "。

越狱检测绕过

Hook.js

function bypassJailbreakDetection() {try {var className = "JailbreakDetection";var funcName = "+ isJail";var hook = eval('ObjC.classes.' + className + '["' + funcName + '"]');Interceptor.attach(hook.implementation, {onLeave: function(retval) {console.log("[*] Class Name: " + className);console.log("[*] Method Name: " + funcName);console.log("\t[-] Type of return value: " + typeof retval);console.log("\t[-] Original Return Value: " + retval);retval.replace(0x0);console.log("\t[-] Type of return value: " + typeof retval);console.log("\t[-] Return Value: " + retval);}});} catch(err) {console.log("[-] Error: " + err.message);}
}

寻找所有类

Hook.js

function show_classes_of_app()
{console.log("[*] Started: Find Classes")var count = 0for (var className in ObjC.classes){if (ObjC.classes.hasOwnProperty(className)){console.log(className);count = count + 1}}console.log("\n[*] Classes found: " + count);console.log("[*] Completed: Find Classes")
}

寻找所有类所有方法

Hook.js

find-all-methods-all-classes()
{console.log("[*] Started: Find Methods")if (ObjC.available){for (var className in ObjC.classes){if (ObjC.classes.hasOwnProperty(className)){console.log("[+] Class: " + className);var methods = eval('ObjC.classes.' + className + '.$methods');for (var i = 0; i < methods.length; i++){console.log("\t[-] Method: "+methods[i]);}}}}else{console.log("Objective-C Runtime is not available!");}console.log("[*] Completed: Find Methods")}

寻找某一特定方法

Hook.js

find-specific-method()
{console.log("[*] Started: Find Specific Method");if (ObjC.available){for (var className in ObjC.classes){try{if (ObjC.classes.hasOwnProperty(className)){try{var methods = eval('ObjC.classes.' + className + '.$methods');for (var i = 0; i < methods.length; i++){try{//填写方法名if(methods[i].includes("方法名")){console.log("[+] Class: " + className);console.log("\t[-] Method: "+methods[i]);}}catch(err){console.log("[!] Exception3: " + err.message);}}}catch(err){console.log("[!] Exception2: " + err.message);}}}catch(err){console.log("[!] Exception1: " + err.message);}}}else{console.log("Objective-C Runtime is not available!");}console.log("[*] Completed: Find Specific Method");
}

寻找某一特定类下的全部方法

Hook.js

hook-all-methods-of-specific-class()
{console.log("[*] Started: Hook all methods of a specific class");if (ObjC.available){try{//Your class name herevar className = "YOUR_CLASS_NAME_HERE";var methods = eval('ObjC.classes.' + className + '.$methods');for (var i = 0; i < methods.length; i++){try{console.log("[-] "+methods[i]);try{console.log("\t[*] Hooking into implementation");//eval('var className2 = "'+className+'"; var funcName2 = "'+methods[i]+'"; var hook = eval(\'ObjC.classes.\'+className2+\'["\'+funcName2+\'"]\'); Interceptor.attach(hook.implementation, {   onEnter: function(args) {    console.log("[*] Detected call to: " + className2 + " -> " + funcName2);  } });');var className2 = className;var funcName2 = methods[i];var hook = eval('ObjC.classes.'+className2+'["'+funcName2+'"]');Interceptor.attach(hook.implementation, {onEnter: function(args) {console.log("[*] Detected call to: " + className2 + " -> " + funcName2);}});console.log("\t[*] Hooking successful");}catch(err){console.log("\t[!] Hooking failed: " + err.message);}}catch(err){console.log("[!] Exception1: " + err.message);}}}catch(err){console.log("[!] Exception2: " + err.message);}}else{console.log("Objective-C Runtime is not available!");}console.log("[*] Completed: Hook all methods of a specific class");}

改变方法入参

hook.js

function show_modify_function_args()
{  var hook = ObjC.classes["类名"]["方法名"];Interceptor.attach(hook.implementation, {onEnter: function(args) {// args[0] is self// args[1] is selector (SEL "sendMessageWithText:")// args[2] holds the first function argument, an NSStringconsole.log("\n[*] Detected call to: " + className + " -> " + funcName);console.log("\t[-] Argument Value: "+args[2]);//替换原有参数值var newargval = ptr("0x0")args[2] = newargvalconsole.log("\t[-] New Argument Value: " + args[2])}});
}

改变方法输出

hook.js

function show_modify_function_return_value()
{var hook = ObjC.classes["类名"]["方法名"];Interceptor.attach(hook.implementation, {onLeave: function(retval) {console.log("\n[*] Class Name: " + className);console.log("[*] Method Name: " + funcName);console.log("\t[-] Type of return value: " + typeof retval);//console.log(retval.toString());console.log("\t[-] Return Value: " + retval);//替换原有输出结果var newretval = ptr("0x0") //新的结果赋值retval.replace(newretval)console.log("\t[-] New Return Value: " + newretval)}});
}

脱壳

GitHub项目:frida-iOS-dump

这里提一下,苹果对上架的应用会在审核后加一个官方的壳子。

踩坑

1、脱壳卡住不动

当你遇到上述情况时,不要怀疑。原因是权限不够,命令变更为:

sudo python3 dump.py 斗鱼

结果如下:

Frida—HOOK 学习笔记2相关推荐

  1. Frida—HOOK 学习笔记1

    JAVA层HOOK 在开始之前,依旧是熟悉的工具官方文档.因本人英文水平不高,大佬们可通过官网(https://frida.re/docs/home/)或看雪(https://bbs.pediy.co ...

  2. PC微信hook学习笔记(一)—— 获取个人信息

    PC微信hook学习笔记(一)-- 获取微信个人信息 1 起步 2. 获取基址 2.1 用CE查看个人信息 2.1.1 获取昵称基址 2.1.2 dll模块基址 2.2 用OD查看个人信息 2.3 内 ...

  3. Angular ngOnChanges hook学习笔记

    只有这三种事件才会导致Angular视图的更新,都是异步事件. Events:如 click, change, input, submit 等用户事件 XMLHttpRequests:比如从远端服务获 ...

  4. HOOK学习笔记与心得

    一.  Hook介绍 钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的.当消息到达后,在目标窗口处理 ...

  5. css view a if属性,uni-app学习笔记(2)view属性控制css样式

    uni-app学习笔记(2)view属性控制css样式 uniapp通过标签属性来改变样式 当鼠标按下去的时候,他会变成这个样式 hover-class="box-active" ...

  6. PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 call

    您的位置 首页 PyTorch 学习笔记系列 PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 发布: 2017年8月4日 7,195阅读 ...

  7. java获取内存基址_安卓逆向|菜鸟的FRIDA学习笔记:内存读写

    假设你的手机已经root,并已开启frida服务,电脑端已安装好Python,frida,IDA,GDA. 样本地址: 链接: https://pan.baidu.com/s/1y3kIXcBv25Q ...

  8. Windows驱动开发学习笔记(六)—— Inline HOOK

    Windows驱动开发学习笔记(六)-- Inline HOOK SSDT HOOK Inline Hook 挂钩 执行流程 脱钩 实验一:3环 Inline Hook 实验二:0环 Inline H ...

  9. Windows驱动开发学习笔记(五)—— SSDT HOOK

    Windows驱动开发学习笔记(五)-- SSDT HOOK 系统服务表 系统服务描述符表 实验一:通过代码获取SSDT表地址 通过页表基址修改页属性 方法1:修改页属性 方法2:修改CR0寄存器 实 ...

最新文章

  1. mysql中imagin的类型_Image转换成Mysql的blob类型 | 学步园
  2. 法国科学家发布AI模型,阐释蛋白结构和功能及进化关系
  3. 转贴:BMP格式详解 二 (转载)
  4. Window核心编程
  5. Python Django 模板继承(header,body,footer抽取)
  6. 经典C语言程序100例之十四
  7. 这个神奇的库,可以将数据平滑化并找到异常点
  8. Stackoverflow的见解:投票最多的是Spring 4问题
  9. java 音频对比_java – 比较两个不同的音频文件不起作用
  10. centos 宝塔面板 mongodb 设置用户账号密码登录
  11. TTYL的完整形式是什么?
  12. Veeam FAQ系列转载(一):备份
  13. Romoting 通信DEMO(整理)
  14. anaconda navigator更新_Python 数据分析答疑 1:安装 Anaconda
  15. 客户端连接idea_IDEA新特性:提前知道代码怎么走!
  16. 实现前后端分离的心得
  17. Bailian2996 选课【置换】
  18. 超20GB显存怪兽!RTX 30或于9月9日发布,AMD蓄力卡皇之争
  19. 基于B站服务器宕机事故,小鸟云聊聊高防云服务器选择技巧
  20. 4万字【Python高级编程】保姆式教学,330页PDF10万字的知识点总结

热门文章

  1. Windows8/Silverlight/WPF/WP7周学习导读(11月12日-11月18日)
  2. 德州学院大学计算机,德州学院 计算机系 李天志老师简介 联系方式 手机电话 邮箱...
  3. error: exportArchive: No signing certificate “iOS Distribution“ found
  4. linux 开源视频剪辑,Linux 上的开源视频剪辑软件Olive
  5. Vue项目和Tauri接入Google Analytics谷歌统计流程
  6. 环洋市场调研-2021年全球肽化妆品行业调研及趋势分析报告
  7. 想要更高效地使用云计算,推荐学习云计算部署的五大策略
  8. 计算机音乐谱毛不易,中国内地流行男歌手毛不易歌曲简谱精选
  9. yocs_velocity_smoother速度平滑配置与使用
  10. 大数据项目实战——电信业务大数据分析系统