当前位置:   article > 正文

【iOS安全】越狱iOS安装Frida | 安装指定版本Frida | Frida使用_ios frida安装

ios frida安装

越狱iPhone安装Frida

本文的方法适用于已越狱的iPhone手机

打开Cydia,软件源,编辑(右上角),添加(左上角):https://build.frida.re

图片名称
图片名称
图片名称

然后搜索Frida,点击安装

图片名称

参考:https://blog.csdn.net/boildoctor/article/details/122769942

安装指定版本Frida

iOS上的Frida版本需要和PC上的Frida版本保持一致,所以有时候需要安装指定版本Frida

下载指定版本deb包:
https://github.com/frida/frida/releases

例如:frida_15.2.2_iphoneos-arm.deb
在这里插入图片描述
通过XFTP将deb拷贝至手机/private/var/tmp目录(也就是/tmp目录)
在这里插入图片描述
ssh进入手机 /tmp目录,执行安装:

dpkg -i xx.deb
  • 1

参考:https://cloud.tencent.com/developer/article/2160543

使用Frida

在iPhone上启动Frida-server后,将iPhone通过USB连接至PC
PC上安装Frida,通过命令行输入命令 或 运行脚本

frida-ls-devices 查看电脑连接的iOS设备信息

在这里插入图片描述

frida-ps 查看正在运行的应用

USB连接

frida-ps -Ua
在这里插入图片描述

Wi-Fi连接

frida-ps -H 10.168.1.34:6666 -a
在这里插入图片描述

frida hook 类函数

  • 函数名以”+”开头的,如:“+ URLWithString:”,可以直接通过类名调用方法,相当于java中的static函数
#coding=utf-8
import frida, sys 

jscode = """
if(ObjC.available){
    console.log('\\n[*] Starting Hooking');

    var _className = "JDJR_HackersInfo"; 
    var _methodName = "+ judgementJailbreak";

    var hooking = ObjC.classes[_className][_methodName]; 
    console.log('className is: ' + _className + ' and methodName is: ' + _methodName);

    Interceptor.attach(hooking.implementation,{
        onEnter: function(args) {
            //args[0]:self
            //args[1]:The selector
            //args[2]:第一个参数
            
            console.log(' hook success ')
            this._className = ObjC.Object(args[0]).toString();
            this._methodName = ObjC.selectorAsString(args[1]);
            // console.log('Detected call to: ');
            // console.log('[-] Detected call to:  ' + this._className + ' --> ' + this._methodName);

            // console.log('\\n----------------' + this._methodName + '----------------');
            // console.log('arg2:\\n' + ObjC.Object(args[2]).toString());

            //console.log('called from:\\n' + Thread.backtrace(this.context,Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\\n') + '\\n');
            //print_arguments(args);
        },
        //onLeave.function(returnValue)被拦截函数调用之后回调 其中returnValue表示原始函数的返回值
        onLeave:function(returnValue){
            // console.log('Return value of: ');
            // console.log(' ' + this._className + ' --> ' + this._methodName);
            // var typeValue = Object.prototype.toString.call(returnValue);
            // console.log("\\t Type of return value: " + typeValue);
            // console.log("\\t Return Value: " + returnValue);

            console.log("old Return Value: " + ObjC.Object(returnValue));
            var newRet = ObjC.classes.NSString.stringWithString_("1");
            returnValue.replace(newRet);
            console.log("new Return Value: " + ObjC.Object(returnValue));
            
        }
    });


}
"""

bundle = 'xxx.xxx.xxx'

device = frida.get_usb_device() #连接usb设备 参数:超时时长
pid = device.spawn([bundle]) #启动指定bundleId的app
session = device.attach(pid) #附加到app
script = session.create_script(jscode) #创建frida javaScript脚本
script.load() #load脚本到app进程中 这样即注入成功
device.resume(pid) #恢复app运行
sys.stdin.read()#读取打印日志
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

frida hook 实例函数

  • 函数名以“-”开头的需要找到一个实例化的对象,然后再调用方法
    • 如果内存中没有这样的对象
      这种情况需要手动生成一个实例,用法为ObjC.classes.类名.alloc()
    • 如果内存中存在实例化后的对象
      这种情况需要先找出一个类的实例,使用var tmp=ObjC.chooseSync(ObjC.classes.类名),例如:
      ObjC.chooseSync(ObjC.classes.PARSHealthPedometer10thHomeViewController)[0]
      其中[0]表示取找到的实例中的第一个实例,可根据实际情况换成其他的实例。
#coding=utf-8
import frida, sys 

jscode = """
if(ObjC.available){
    console.log('\\n[*] Starting Hooking');

    // setToken: 有内容
    var _className = "WLRequestInfo"; 
    var _methodName = "- setToken:"; 
    // var hookingclass = ObjC.chooseSync(ObjC.classes[_className])[0]; //如果内存中存在实例化后的对象,需要先找出一个类的实例
    var hookingclass = ObjC.classes[_className].alloc(); //如果内存中没有实例化后的对象,手动实例化
    var hooking = hookingclass[_methodName];
    console.log('className is: ' + _className + ' and methodName is: ' + _methodName);

    Interceptor.attach(hooking.implementation,{
        onEnter: function(args) {
            //args[0]:self
            //args[1]:The selector
            //args[2]:第一个参数
            // console.log(' hook success ')
            this._className = ObjC.Object(args[0]).toString();
            this._methodName = ObjC.selectorAsString(args[1]);
            // console.log('Detected call to: ');
            // console.log('[-] Detected call to:  ' + this._className + ' --> ' + this._methodName);
            
            // console.log('\\n[-]' + this._className + ' --> ' + this._methodName + ' : ');
            console.log('\\n----------------' + this._methodName + '----------------');
            console.log('arg2:\\n' + ObjC.Object(args[2]));

            console.log('called from:\\n' + Thread.backtrace(this.context,Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\\n') + '\\n');
            //print_arguments(args);
        },
        //onLeave.function(returnValue)被拦截函数调用之后回调 其中returnValue表示原始函数的返回值
        onLeave:function(returnValue){
            // console.log('Return value of: ');
            // console.log(' ' + this._className + ' --> ' + this._methodName);
            // var typeValue = Object.prototype.toString.call(returnValue);
            // console.log("Type of return value: " + typeValue);
            // console.log("Return Value: " + returnValue);
            console.log("Return Value: \\n" + ObjC.Object(returnValue));
        }
    });

}
"""

bundle = 'cn.gov.pbc.dcep'

device = frida.get_usb_device() #连接usb设备 参数:超时时长
pid = device.spawn([bundle]) #启动指定bundleId的app
session = device.attach(pid) #附加到app
script = session.create_script(jscode) #创建frida javaScript脚本
script.load() #load脚本到app进程中 这样即注入成功
device.resume(pid) #恢复app运行
sys.stdin.read()#读取打印日志
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

参考:https://mabin004.github.io/2018/08/24/%E5%9C%A8iOS%E4%B8%8A%E4%BD%BF%E7%94%A8Frida/

frida Wi-Fi连接

先使用ssh在iPhone上执行 /usr/sbin/frida-server -l 0.0.0.0:6666,开启6666端口
iPhone和PC连接至同一局域网
假设iPhone的IP为10.168.1.34,开启端口为6666,则wifi连接代码如下:

# frida-ps -H 10.168.1.34:6666 -a
bundle = 'com.unionpay.chsp'

# wifi连接
# ssh执行 /usr/sbin/frida-server -l 0.0.0.0:6666
str_host = '10.168.1.34:6666'
manager = frida.get_device_manager()
device = manager.add_remote_device(str_host)
pid = device.spawn([bundle]) #启动指定bundleId的app
session = device.attach(pid) #附加到app
script = session.create_script(jscode) #创建frida javaScript脚本
script.load() #load脚本到app进程中 这样即注入成功
device.resume(pid) #恢复app运行
sys.stdin.read()#读取打印日志
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

frida USB连接

对比之下,USB连接部分代码如下:

# frida-ps -Ua
bundle = 'com.unionpay.chsp'

# usb连接
device = frida.get_usb_device() #连接usb设备 参数:超时时长
pid = device.spawn([bundle]) #启动指定bundleId的app
session = device.attach(pid) #附加到app
script = session.create_script(jscode) #创建frida javaScript脚本
script.load() #load脚本到app进程中 这样即注入成功
device.resume(pid) #恢复app运行
sys.stdin.read()#读取打印日志
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

完整代码如下:

#coding=utf-8
import frida, sys 

jscode = """
if(ObjC.available){
    console.log('\\n[*] Starting Hooking');

    var _className = "CDVWKWebViewEngine"; 
    var _methodName = "- userContentController:didReceiveScriptMessage:";

    var hooking = ObjC.classes[_className][_methodName]; 
    console.log('className is: ' + _className + ' and methodName is: ' + _methodName);

    Interceptor.attach(hooking.implementation,{
        onEnter: function(args) {
            //args[0]:self
            //args[1]:The selector
            //args[2]:第一个参数
            
            // console.log(' hook success ')
            // this._className = ObjC.Object(args[0]).toString();
            // this._methodName = ObjC.selectorAsString(args[1]);
            // console.log('Detected call to: ');
            // console.log('[-] Detected call to:  ' + this._className + ' --> ' + this._methodName);
            console.log('arg2: ' + ObjC.Object(args[2]).toString());
            console.log('arg3: ' + ObjC.Object(args[3]).toString());

            console.log('called from:\\n' + Thread.backtrace(this.context,Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\\n') + '\\n');

        }
    });


}
"""
# frida-ps -Ua
bundle = 'com.unionpay.chsp'

# usb连接
# device = frida.get_usb_device() #连接usb设备 参数:超时时长
# pid = device.spawn([bundle]) #启动指定bundleId的app
# session = device.attach(pid) #附加到app
# script = session.create_script(jscode) #创建frida javaScript脚本
# script.load() #load脚本到app进程中 这样即注入成功
# device.resume(pid) #恢复app运行
# sys.stdin.read()#读取打印日志

# wifi连接
# ssh执行 /usr/sbin/frida-server -l 0.0.0.0:6666
str_host = '10.168.1.34:6666'
manager = frida.get_device_manager()
device = manager.add_remote_device(str_host)
pid = device.spawn([bundle]) #启动指定bundleId的app
session = device.attach(pid) #附加到app
script = session.create_script(jscode) #创建frida javaScript脚本
script.load() #load脚本到app进程中 这样即注入成功
device.resume(pid) #恢复app运行
sys.stdin.read()#读取打印日志
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

frida-trace

模糊匹配

hook 所有类的- userContentController:didReceiveScriptMessage:函数,可以用*进行模糊匹配

// wifi连接
frida-trace -m "-[* userContentController:didReceiveScriptMessage:]" -H 10.168.1.34:6666 腾讯视频

// USB连接
frida-trace -m "-[* userContentController:didReceiveScriptMessage:]" -U  腾讯视频
  • 1
  • 2
  • 3
  • 4
  • 5

其中"腾讯视频"是进程名,可以通过frida-ps -H 10.168.1.34:6666 -a获得

参考:https://www.jianshu.com/p/7a3ba7ae3c29

自定义处理逻辑

在执行frida-trace的文件夹下,会产生__handlers__文件夹
直接修改文件夹中的JS代码,即可生效

Spawn模式和Attach 模式

Spawn模式直接启动app
关键代码:
pid = device.spawn([‘com.tencent.live4iphone’])

# frida-ps -H 10.168.1.34:6666 -a
bundle = 'com.tencent.live4iphone'

# ssh执行 /usr/sbin/frida-server -l 0.0.0.0:6666
str_host = '10.168.1.34:6666'
manager = frida.get_device_manager()
device = manager.add_remote_device(str_host)
pid = device.spawn([bundle]) 
session = device.attach(pid) #附加到app
script = session.create_script(jscode) #创建frida javaScript脚本
script.load() #load脚本到app进程中 这样即注入成功
device.resume(pid) #恢复app运行
sys.stdin.read()#读取打印日志
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Attach模式在已启动app情况下注入
关键代码:
pid = device.get_process(“腾讯视频”).pid

# frida-ps -H 10.168.1.34:6666 -a
processName = "腾讯视频"

# wifi连接
# ssh执行 /usr/sbin/frida-server -l 0.0.0.0:6666
str_host = '10.168.1.34:6666'
manager = frida.get_device_manager()
device = manager.add_remote_device(str_host)
pid = device.get_process(processName).pid
session = device.attach(pid) #附加到app
script = session.create_script(jscode) #创建frida javaScript脚本
script.load() #load脚本到app进程中 这样即注入成功
device.resume(pid) #恢复app运行
sys.stdin.read()#读取打印日志
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

具体类的处理

打印WKScriptMessage的内容
// console.log('arg3: ' + ObjC.Object(args[3]).toString());
// <WKScriptMessage: 0x28eda47e0>

var scriptMessage = ObjC.Object(args[3]);
var scriptMessageBody = scriptMessage.body();
var bodyString = scriptMessageBody.toString();

if (bodyString.includes("handlername = getTreasureBoxReportTime;")
    || bodyString.includes("handlername = sendRadarLog;")
|| bodyString.includes("handlername = setClientLog;")) {
    return;
}
console.log("scriptMessageBody: " + scriptMessageBody);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

例如:

    var _className = "KMYWKWebViewBridge";
    var _methodName = "- userContentController:didReceiveScriptMessage:";

    var hooking = ObjC.classes[_className][_methodName];
    console.log('className is: ' + _className + ' and methodName is: ' + _methodName);

    Interceptor.attach(hooking.implementation, {
        onEnter: function (args) {
            //args[0]:self
            //args[1]:The selector
            //args[2]:第一个参数

            // console.log(' hook success ')
            // this._className = ObjC.Object(args[0]).toString();
            // this._methodName = ObjC.selectorAsString(args[1]);
            // console.log('Detected call to: ');
            // console.log('[-] Detected call to:  ' + this._className + ' --> ' + this._methodName);

            // console.log('arg2: ' + ObjC.Object(args[2]).toString()); // <WKUserContentController: 0x285aa7ba0>
            // console.log('arg3: ' + ObjC.Object(args[3]).toString()); // <WKScriptMessage: 0x28eda47e0>

            var scriptMessage = ObjC.Object(args[3]);
            var scriptMessageBody = scriptMessage.body();
            var bodyString = scriptMessageBody.toString();

            if (bodyString.includes("handlername = getTreasureBoxReportTime;")
                || bodyString.includes("handlername = sendRadarLog;")
            || bodyString.includes("handlername = setClientLog;")) {
                return;
            }
            console.log("scriptMessageBody: " + scriptMessageBody);
            
            // console.log('called from:\\n' + Thread.backtrace(this.context,Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\\n') + '\\n');
        }
    });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
替换字符串参数的值
  onEnter(log, args, state) {
    // log(`-[NSMutableURLRequest setValue:${args[2]} forHTTPHeaderField:${args[3]}]`);

    // console.log('arg2: ' + ObjC.Object(args[2]).toString());
    // console.log('arg3: ' + ObjC.Object(args[3]).toString());
    // console.log("\n");

    arg3 = ObjC.Object(args[3]);

    if (arg3.isEqualToString_('Cookie')) {
      args[2] = ObjC.classes.NSString.stringWithString_("xxx");
      console.log('[*] New cookie value:'+ ObjC.Object(args[2]).toString() );
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/635007
推荐阅读
相关标签
  

闽ICP备14008679号