h4ck1nH4ck1n  2022-04-29 17:08 字节时代 隐藏边栏 |   抢沙发  2,356 
文章评分 1 次,平均分 5.0

Frida 常用姿势

Frida是一个跨平台的动态代码注入工具集,你可以使用它hook应用程序,插入自己的JavaSciprt代码,同时也能获取应用的内存和函数的完全访问权限。

推荐使用此工具进行追踪函数调用等操作,比使用Theoslogify.pl工具方便多了。

文档:https://www.frida.re/docs/examples/ios/

使用局域网连接

ssh root@192.168.50.227 -p 22 # 登录设备
/usr/sbin/frida-server -l 0.0.0.0:6666  # 手动启用服务,监听 6666 端口
frida -H 192.168.50.227:6666 -n Springboard # 电脑端连接远程设备

加载 JS 注入到指定名字的进程

frida -n Twitter -l demo1.js  # 本机
frida  -U -n Twitter -l demo1.js # USB 设备
frida  -U -f com.sougu.MyBus -l demo1.js --no-pause  # 启动应用,并且启动后不暂停

列出本机所有正在运行的进程和名称

frida-ps

列出 USB 连接的设备的所有正在运行的进程和名称

frida-ps -Uai

帮助

frida-ps -h

列出所有连接的设备

frida-ls-devices

跟踪 Native API

frida-trace -n Twitter -i "*URL*" # 本机
frida-trace -U Twitter -i "*URL*" # USB设备上的Twitter

跟踪 Objective-C API

frida-trace -U -f 'com.sougu.MyBus' -m "-[NSURL* *HTTP*]"
frida-trace -U Twitter -m "-[NSURL* *HTTP*]"
frida-trace -U -p 6604 -m "-[NSURL* *HTTP*]"
frida-trace -U QQ -m "-[CFT_PayCenterBusi *]" -m "+[CFT_PayCenterBusi init*]" -m "*[QIMService postRegisteNotification:Object:userInfo:]" # 追踪多个,实例方法,类方法,所有方法

追溯一个 Objective-C 方法调用

# Add the following code to the onEnter event-handler in the auto-generated JS of the desired API
log('\tBacktrace:\n\t' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n\t'));

将数据写入文件

agent.js:

var data = { foo: 'bar' };
send(data);

app.py:

import frida

def on_message(message, data):
    print(message['payload'])

调用 Native 函数

var address = Module.findExportByName('libsqlite3.dylib', 'sqlite3_sql');
var sql = new NativeFunction(address, 'char', ['pointer']);
sql(statement);

将 NSData 转换为字符串

var data = new ObjC.Object(args[2]);
Memory.readUtf8String(data.bytes(), data.length()); 
# Tip: 2nd argument (number of bytes) is not required if the string data is null-terminated.

将 NSData 转换为二进制数据

Memory.readByteArray(data.bytes(), data.length());

迭代一个 NSArray

var count = array.count();
for (var i = 0; i !== count; i++) {
  var element = array.objectAtIndex_(i);
}

迭代一个 NSDictionary

var enumerator = dict.keyEnumerator();
var key;
while ((key = enumerator.nextObject()) !== null) {
  var value = dict.objectForKey_(key);
}

解档

var parsedValue = ObjC.classes.NSKeyedUnarchiver.unarchiveObjectWithData_(value);

读一个结构体

# If args[0] is a pointer to a struct, and let’s say you want to read the uint32 at offset 4, you can do it as shown below: 
Memory.readU32(args[0].add(4));

显示一个弹窗

<= 7.0

var UIAlertView = ObjC.classes.UIAlertView; /* iOS 7 */
var view = UIAlertView.alloc().initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles_(
    'Frida',
    'Hello from Frida',
    NULL,
    'OK',
    NULL);
view.show();
view.release();

>= 8.0

// Defining a Block that will be passed as handler parameter to +[UIAlertAction actionWithTitle:style:handler:]
var handler = new ObjC.Block({
  retType: 'void',
  argTypes: ['object'],
  implementation: function () {
  }
});

// Import ObjC classes
var UIAlertController = ObjC.classes.UIAlertController;
var UIAlertAction = ObjC.classes.UIAlertAction;
var UIApplication = ObjC.classes.UIApplication;

// Using Grand Central Dispatch to pass messages (invoke methods) in application's main thread
ObjC.schedule(ObjC.mainQueue, function () {
  // Using integer numerals for preferredStyle which is of type enum UIAlertControllerStyle
  var alert = UIAlertController.alertControllerWithTitle_message_preferredStyle_('Frida', 'Hello from Frida', 1);
  // Again using integer numeral for style parameter that is enum
  var defaultAction = UIAlertAction.actionWithTitle_style_handler_('OK', 0, handler);
  alert.addAction_(defaultAction);
  // Instead of using `ObjC.choose()` and looking for UIViewController instances
  // on the heap, we have direct access through UIApplication:
  UIApplication.sharedApplication().keyWindow().rootViewController().presentViewController_animated_completion_(alert, true, NULL);
})

调用 [UIApplication openURL:]

// Get a reference to the openURL selector
var openURL = ObjC.classes.UIApplication["- openURL:"];

// Intercept the method
Interceptor.attach(openURL.implementation, {
  onEnter: function(args) {
    // As this is an ObjectiveC method, the arguments are as follows:
    // 0. 'self'
    // 1. The selector (openURL:)
    // 2. The first argument to the openURL selector
    var myNSURL = new ObjC.Object(args[2]);
    // Convert it to a JS string
    var myJSURL = myNSURL.absoluteString().toString();
    // Log it
    console.log("Launching URL: " + myJSURL);
  }
});

IDE 自动提示

$ git clone git://github.com/oleavr/frida-agent-example.git
$ cd frida-agent-example/
$ npm install
$ npm run watch # 监控代码修改自动编译生成js文件

使用 VSCode 等 IDE 打开此工程,在 agent 下编写 typescript,会有智能提示。

$ frida -U -f com.example.android --no-pause -l _agent.js

Python 脚本的编写

from __future__ import print_function
import frida
import sys

# 进程名
process_name = 'myprocess'
# 导入的js脚本
js_file_name = 'myhookjs.js'

# 自定义回调函数
# 数据通过send(message [,data])传递给 python 的 on_message(消息,数据)函数,其中我们前面已经介绍过了,
# 第一个参数是一个 python 字典类型,其中的 message['payload'] 存放的就是第一个参数内容

def on_message(message, data):
    if message['type'] == 'send':
        print(message['payload'])
    elif message['type'] == 'error':
        print(message['stack'])

# hook逻辑脚本
def get_js_code():
    js_file = open(js_file_name)  # type: BinaryIO
    return js_file.read()

# start here
if __name__ == '__main__':
    # 注入进程,attach传入进程名称(字符串)或者进程号(整数)
    process_id   =  0
    device = frida.get_usb_device()

    # 循环等待,根据进程名查找进程pid。找到执行hook
    while True:
        try:
            process1 = device.get_process(process_name)
            process_id = process1.pid
            # 也可用
            # pid = device.spawn([“com.android.chrome”])

            print(process_id)
            break
        except:
            pass

    session = device.attach(process_id)
    # 指定JavaScript脚本
    # script = session.create_script(get_js_code()% int(sys.argv[1], 16)))
    script = session.create_script(get_js_code())
    script.on('message', on_message)    
    script.load()
    # 读取返回输入
    sys.stdin.read()

    # int() 函数把字符串表示的16进制数转换成整数
    # 上面的jscode % int(sys.argv[1], 16)是python格式化字符串的语法

Frida hook Object-C

Attach 方法

var className = "className";
var funcName = "functionName";
var hook = eval('ObjC.classes.' + className + '["' + funcName + '"]');

# Interceptor.attach(target, callbacks)
# target是NativePointer指定要拦截调用的函数的地址
# 如果从Frida API获取地址(例如Module.getExportByName()),Frida将处理详细信息

Interceptor.attach(hook.implementation,{
        # 回调函数给出一个参数  args,可用于读取或写入参数作为NativePointer对象数组
        onEnter: function(args)
        {

        },
        # 给定一个参数的回调函数,该参数 retval是NativePointer包含原始返回值的衍生对象
        # 请注意,此对象在onLeave调用中循环使用,因此请勿在回调之外存储和使用它。如果需要存储包含的值,请进行深层复制,例如:ptr(retval.toString())
        onLeave: function(retval)
        {

        }
    }
);

查看参数类型

# objc的函数,第0个参数是id,第1个参数是SEL,真正的参数从args[2]开始
console.log("Type of arg[2] -> " + new ObjC.Object(args[2]).$className)

参数(返回值)NS 与 JS 类型转换

# log String
var myString = new ObjC.Object(args[2]);
console.log("String argument: " + myString.toString());

# NSString(NCFString) to String 
var NSString = new ObjC.Object(args[2]);
var str = NSString.UTF8String();

# replace js String
str = str.replace(/BJP/,"HZH");
# log String
console.log(str);

# NSNumber to Int
var myNumber = args[3].toInt32();
console.log(myNumber);

# Converting NSData to String
var data = new ObjC.Object(args[2]);
var myString = data.bytes().readUtf8String(data.length());
console.log(myString);

# Converting NSData to Base64String   
var myString = new ObjC.Object(args[2]); var base = myString.base64EncodedStringWithOptions_(0)
console.log("String argument: " + base);

Tip: 2nd argument (number of bytes) is not required if the string data is null-terminated.

# Converting NSData to Binary Data
var data = new ObjC.Object(args[2]);
data.bytes().readByteArray(data.length());

替换参数

var str ="hello";
var newstring = ObjC.classes.NSString.stringWithString_(str);    
args[2] =  newstring;

替换返回值

#用整数1337替换返回值
retval.replace(1337)
#用指针替换
retval.replace(ptr("0x1234"))

调用函数

var st = Memory.allocUtf8String("TESTMEPLZ!");

#In NativeFunction param 2 is the return value type,
#and param 3 is an array of input types
var f = new NativeFunction(hook.implementation, 'pointer', ['pointer','char','pointer']);
#f(st,0,NSString1);

通过一个函数获得其他函数地址进行 Hook

var className = "DTURLRequestOperation";
var funcName = "- rpcV1Sign:newSign:request: ";
var hook = eval('ObjC.classes.' + className + '["' + funcName + '"]');    
var rpcV1SignAddr = hook.implementation;
console.log('rpcV1SignAddr: ' + rpcV1SignAddr );

/*
var className2 = "DTURLRequestOperation";
var funcName2 = "- avmpSign: ";
var hook2 = eval('ObjC.classes.' + className2 + '["' + funcName2 + '"]');
var avmpSignAddr = hook2.implementation;
console.log('avmpSignAddr: ' + avmpSignAddr );
*/

#add的这个偏移是通过IDA的静态地址相减得到的
var avmpSignAddr = rpcV1SignAddr.add(0x1DCE);
console.log('avmpSignAddr: ' + avmpSignAddr);
Interceptor.attach(avmpSignAddr, {
    onEnter: function(args){
        console.log("onEnter");
        console.log(args[0]);
        console.log(args[1]);
    },
    onLeave: function(retval){
        console.log("onLeave");
        console.log(retval);

    }
});

本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

h4ck1n
H4ck1n 关注:0    粉丝:0
这个人很懒,什么都没写

发表评论

表情 格式 链接 私密 签到

扫一扫二维码分享