文章目录

0x01 工具安装

0x02 设备环境配置(Android)

0x03 命令行运行

0x04 Python脚本运行

0x05 基本使用

0x06 常用API整理

0x07 开发用例

Frida,是一个类似 Xposed/Substrate 的跨平台、动态、插装工具,借助 python 和 javascript,可以非常快速便捷地操作目标进程、修改函数参数返回值等,支持 Android/iOS/Macos/Linux/Windows,参考官网

0x01 工具安装

有两种安装方式,命令行直接安装

# python 支持

$ pip3 install frida

# CLI命令行界面工具,支持 frida、frida-ls-devices、frida-ps、frida-kill、frida-trace、frida-discover

$ pip3 install frida-tools

或者直接从源码编译

$ git clone git://github.com/frida/frida.git

$ cd frida

# Linux 下直接 make 编译

# Macos 或 iOS 下需要安装证书,然后导出证书名指定为环境变量,再编译

$ export MAC_CERTID=frida-cert/export IOS_CERTID=frida-cert

$ make

# 确保系统信任新安装的证书,需要重启一下 taskgated 守护进程

$ sudo killall taskgated

0x02 设备环境配置(Android)

原理:手机端安装一个server程序,将手机端的端口转发到PC端,PC端通过python脚本进行交互,脚本内部又是通过javascript语言来写代码执行操作

frida-server下载地址,注意对应版本和支持平台,可以使用下面的命令查看设备abi信息

$ adb shell getprop | grep abi

[ro.product.cpu.abi]: [arm64-v8a]

[ro.product.cpu.abilist]: [arm64-v8a,armeabi-v7a,armeabi]

[ro.product.cpu.abilist32]: [armeabi-v7a,armeabi]

[ro.product.cpu.abilist64]: [arm64-v8a]

下载server程序并解压缩,然后推送到手机内部存储路径

$ curl -O https://github.com/frida/frida/releases/download/12.8.1/frida-server-12.8.1-android-arm64.xz

$ xz -d frida-server-12.8.1-android-arm64.xz

$ adb push frida-server-12.8.1-android-arm64 /data/local/tmp/frida-server

修改权限运行

blueline:/ # chmod 777 frida-server

blueline:/ # ./frida-server

跟IDA一下,需要对端口进行转发,默认使用27042端口与frida-server通信

$ adb forward tcp:27042 tcp:27042

然后可以运行ps查看手机端进程列表

$ frida-ps -R

PID Name

----- ---------------------------------------------------

581 ATFWD-daemon

29920 adbd

586 android.hardware.biometrics.fingerprint@2.1-service

398 android.hardware.cas@1.0-service

399 android.hardware.configstore@1.0-service

400 android.hardware.dumpstate@1.0-service.bullhead

401 android.hardware.graphics.allocator@2.0-service

556 audioserver

557 cameraserver

......

附加某个进程

$ frida -R com.demo.fridahook

____

/ _ | Frida 12.8.1 - A world-class dynamic instrumentation toolkit

| (_| |

> _ | Commands:

/_/ |_| help -> Displays the help system

. . . . object? -> Display information about 'object'

. . . . exit/quit -> Exit

. . . .

. . . . More info at https://www.frida.re/docs/home/

[Remote::com.demo.fridahook]->

对于非root设备,网上也给出了方法,需要反编译目标应用包,然后注入frida-gadget,参考这里【待验证】

下载对应版本和平台的工具,frida-gadget下载地址,修改命名为libfrida-gadget.so,放到反编译包/lib/目录下

从AndroidManifest.xml文件中找到应用入口文件,在较为靠前的函数中加载动态库

const-string v0, "frida-gadget"

invoke-static {v0}, Ljava/lang/System;>loadLibrary(Ljava/lang/String;)V

另外还需要检查下网络权限,如没有需要添加

回编译应用包,并签名安装,查看会显示Gadget进程

$> frida-ps -U

PID Name

----- ------

16251 Gadget

执行命令进行连接

frida -U gadget

安装objection,自动完成frida gadget注入到apk中

$ pip3 install -U objection

$ objection patchapk -s target_app.apk

0x03 命令行运行

命令行附加应用进程然后敲代码进程注入

$ frida -U -f com.demo.fridahook

____

/ _ | Frida 12.8.1 - A world-class dynamic instrumentation toolkit

| (_| |

> _ | Commands:

/_/ |_| help -> Displays the help system

. . . . object? -> Display information about 'object'

. . . . exit/quit -> Exit

. . . .

. . . . More info at https://www.frida.re/docs/home/

Spawned `com.demo.fridahook`. Use %resume to let the main thread start executing!

[LGE Nexus 5X::com.demo.fridahook]-> Java

{

"androidVersion": "8.1.0",

"available": true

}

[LGE Nexus 5X::com.demo.fridahook]->

插装指定函数,会在当前目录__handlers__文件夹下生成open.js脚本

$ frida-trace -i open -U com.demo.fridahook

或者直接加载js脚本

$ frida -U/-R -l --no-pause -f

js脚本用法示例

// javascript

// 将线程附加到Java虚拟机,成功会回调function()

Java.perform(function() {});

// 由于js代码注入存在超时,通常在外面再包装一层

setImmediate(function() {

...

});

// 动态获取一个js包装的Java类

var clazz = Java.use(className);

// $new()调用类构造方法,$dispose()调用析构清空js对象

// 获取到Java类之后,直接通过.获取方法

// 有些重载的方法需要通过.overload()传入参数类型指定方法

// 通过方法的.call(args)来调用函数,通过方法的.implementations = function(){}来实现hook

clazz.method.implementation = function(args) {

......

}

clazz.method.overload(argumentsType[]).call(args);

// 将js包装的类实例转换成另一种js包装类

Java.cast(clazz1, Clazz2)

0x04 Python脚本运行

除了在命令行直接将脚本注入进程,也可以通过python来加载脚本到指定进程

这里给出一个通用模板

# python

# -*- coding: utf-8 -*-

import frida

import sys

def on_message(message, data):

if message['type'] == 'send':

print("*****[frida hook]***** : {0}".format(message['payload']))

else:

print("*****[frida hook]***** : " + str(message))

def get_javascript(filepath):

code = ''

with open(filepath, 'r') as file:

code = code + file.read()

return code

# 连接远端设备

device = frida.get_remote_device()

# 附加到进程

session = device.attach(package_name)

# 1、直接写入 javascript 代码

javascript = """

"""

# 2、从文件中加载 javascript 脚本代码

javascript = get_javascript(javascript_file)

# 基于脚本内容创建运行脚本对象

script = session.create_script(javascript)

script.on('message', on_message)

# 加载脚本并执行

script.load()

sys.stdin.read()

0x05 基本使用

Frida提供了一些工具直接操作

比如查看连接的设备

# bash

$ frida-ls-devices

Id Type Name

---------------- ------ ------------

local local Local System

0105a575959b9fa9 usb LGE Nexus 5X

tcp remote Local TCP

使用python脚本获取的也是这样

# python

import frida

print(frida.get_local_device())

print(frida.get_usb_device())

print(frida.get_remote_device())

-------------------------------------------------------------------------

Device(id="local", name="Local System", type='local')

Device(id="0105a575959b9fa9", name="LGE Nexus 5X", type='usb')

Device(id="tcp", name="Local TCP", type='remote')

其中,local device对应的就是PC本机,usb device对应连接的Android设备,remote device也是经过端口转发的Android设备

附加指定进程

$ frida-trace -i open -U

也封装了许多常用功能和函数,可以直接调用接口

查看所有进程

# python

processes = device.enumerate_processes()

for process in processes:

print(process)

查看所有安装的应用

# python

applications = device.enumerate_applications()

for application in applications:

print(application)

获取顶层应用进程

# python

front_app = remote_device.get_frontmost_application()

print(front_app)

-------------------------------------------------------------------------

Application(identifier="com.android.settings", name="设置", pid=6683)

获取加载的类

// javascript

Java.enumerateLoadedClasses({

onMatch:function(_className){

log("found instance of '" + _className + "'");

},

onComplete: function(){

log("enumerating completed !!!");

}

});

获取类声明的函数

// javascript

var clazz = Java.use(className);

var methods = clazz.class.getDeclaredMethods();

if(methods.length > 0){

log("getDeclaredMethods of class '" + className + "':");

methods.forEach(function(method){

log(method);

});

}

获取调用堆栈

// javascript

var stack = Java.use("java.lang.Thread").$new().currentThread().getStackTrace();

for(var i = 2; i < stack.length; i++){

log("getStackTrace[" + (i-2) + "] : " + stack[i].toString());

}

获取加载的所有模块

// javascript

Process.enumerateModules({

onMatch: function(module) {

log(module.name + " : " + module.base + "\t" + module.size + "\t" + module.path);

},

onComplete: function() {

log("enumerating completed !!!");

}

});

获取所有的导出符号

// javascript

var symbols = Module.enumerateExportsSync(moduleName);

symbols.forEach(function(symbol){

log(symbol.name + " address = " + symbol.address);

});

return symbols;

获取JNI注册的函数信息

// javascript

var RegisterNativesAddr = getSymbolAddr("libart.so", "_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi");

if(RegisterNativesAddr != null){

log("find symbol 'RegisterNatives' in libart.so, address = " + RegisterNativesAddr);

Interceptor.attach(RegisterNativesAddr, {

onEnter: function(args) {

var class_name = Java.vm.getEnv().getClassName(args[1]);

var methods_ptr = ptr(args[2]);

var module = Process.findModuleByAddress(Memory.readPointer(methods_ptr));

log("RegisterNativeMethod class = " + class_name + ", module = " + module.name + ", base = " + module.base);

var method_count = parseInt(args[3]);

log("registered methods count = " + method_count);

// get registered native method info

var offset = Process.pointerSize;

for (var i = 0; i < method_count; i++) {

var name = Memory.readCString(Memory.readPointer(methods_ptr.add(offset*3*i)));

var sig = Memory.readCString(Memory.readPointer(methods_ptr.add(offset*3*i+offset)));

var address = Memory.readPointer(methods_ptr.add(offset*(3*i+2)));

log("methods name = " + name + ", sig = " + sig + ", address = " + ptr(address) + ", offset = " + ptr(address).sub(module.base));

}

},

onLeave: function() {}

});

}

导出远程方法,注意不支持大写字母和下划线

// javascript

rpc.exports = {

: function(args) {

......

},

: function(args) {

......

}

};

远程调用

# python

script.exports.exportfuncname1(args)

0x06 常用API整理

console

log/warn/error(message) : 打印日志

hexdump(address, options) : 打印address内存,输出格式由options定义,一般为offset、length、header、ansi

Process

id : 返回目标进程id

isDebuggerAttached() : 检查目标进程是否附加成功

enumerateModules() : 枚举当前进程已加载的所有模块,并返回数组

enumerateThreads() : 枚举当前进程的所有线程,并返回数组

getCurrentThreadId() : 返回当前线程id

Module

name : 返回模块名称

base : 返回模块加载到内存中的地址,类型为 NativePointer

size : 模块大小

path : 模块的完整文件路径

load() : 加载so文件,返回 Module 对象

enumerateImports() : 枚举所有导入函数,返回数组(type/name/module/address)

enumerateExports() : 枚举所有导出函数,返回数组(type/name/module/address)

enumerateSymbols() : 枚举所有符号,返回数组(isGlobal/type/section/name/address)

findExportByName()/getExportByName() : 返回指定so文件中导出函数绝对地址

findBaseAddress()/getBaseAddress() : 返回指定so文件内存基址

Memory

scan(address, size, pattern, callback) : 从address开始搜索size大小空间中满足pattern条件的数据回调callback,可以返回“stop”提前取消内存扫描

scan(address, size, pattern) : 返回符合条件的数据数组

alloc(size) : 在目标进程堆中申请size大小的内存,并按照Process.pageSize对齐,返回NativePointer的地址指针,且自动释放

allocUtf8String(string)/allocUtf16String(string)/allocAnsiString(string) : 分配UTF-8/UTF-16/ANSI字符串

copy(obj_addr, tar_addr, size) : 将目标地址开始size大小的数据拷贝到目的地址

writeByteArray(addr, array) : 将数组写入目的地址

readByteArray(addr, size) : 从目标地址读取size大小的数组

Java

available : 判断当前进程是否加载JavaVM,Dalvik/ART虚拟机

androidVersion : 当前设备系统版本

enumerateLoadedClasses() : 枚举当前加载的所有类,回调onMatch(name)/onComplete()

enumerateLoadedClassesSync() : 枚举当前加载的所有类,返回数组

enumerateClassLoaders() : 枚举当前所有的类加载器,回调onMatch(loader)/onComplete()

enumerateClassLoadersSync() : 枚举当前所有的类加载器,返回数组

perform(callback) : 用于当前线程附加到Java VM,回调callback

use(classname) : 动态获取classname对应的类定义,$new()调用构造函数,$dispose()显式释放

choose(classname) : 在堆上查找实例化对象,回调onMatch(instance)/onComplete()

cast(instance, clazz) : 将指定变量或数据强制转化为给定类定义

array(type, [value1, value2, …]) : 定义type类型的Java的数组

registerClass(args) : 注册一个新的Java类并返回对象,类定义由args给出,name指定类名[superClass指定父类,implements指定实现接口数组,fields指定域字段,methods指定方法]

vm : 虚拟机,一般用于调用getEnv()来获取JNIEnv对象

Interceptor

attach(address, callback) : 拦截NativPointer类型的函数地址,回调onEnter/onLeave

detach() : 取消对地址的拦截

replace(function, callback) : 替换NativeFunction类型的原函数为回调函数

NativePointer : javascript中的指针

new NativePointer(var) : 构造指向var的指针

add/sub/and/or/xor(var) : 四则和逻辑运算

shl/shr(n) : 左/右移动n位

isNull() : 检查指针是否为空

toString/toInt32() : 转为字符串/32位整数

readXXX()/writeXXX(value) : 从内存读取内容/向内存写入内容

readByteArray(length) : 从内存读取length长度字节以ArrayBuffer返回

writeByteArray(bytes) : 将Arraybuffer类型或整数数组写入内存位置

readCSString/readUtf8String/readUtf16String/readAnsiString() : 将内存位置的字节作为ASCII、UTF-8、UTF-16、ANSI字符串读取

writeUtf8String/writeUtf16String/writeAnsiString(value) : 对JavaScript字符串进行编码并写入内存位置

NativeFunction : 本地函数

new NativeFunction(address, retType, argTypes) : 构造方法,address为NativePointer类型函数地址,retType指定返回类型,argTypes数组指定参数类型,返回函数对象,可以直接调用

NativeCallback : 本地函数回调

new NativeCallback(callback, retType, argTypes) : 构造方法,callback为回调函数,retType指定返回类型,argTypes数组指定参数类型,返回NativePointer对象

0x07 开发用例

上面所有的代码都可以在这里找到,这是我从学习frida开始写过的一些用例,包含测试应用程序、hook测试用例,还封装了一些常用功能

另外总结了一些优秀的应用案例:

appmon

objection

ssl_logger

passionfruit

r2frida = Frida + Radare2

Brida = Frida + Burp

python的cmd5x.call用法_Frida用法详解【附用例】相关推荐

  1. Python os.path() 模块 详解 附算例

    Python os.path() 模块 函数 说明 os.path.abspath(path) 返回绝对路径 os.path.basename(path) 返回文件名 os.path.commonpr ...

  2. Python中Print()函数的用法___实例详解(二)(全,例多)

    Python中Print()函数的用法___实例详解(二)(全,例多) 目录 十一.Print()小例子 十二.Print()中文输入显示乱码问题 十三.Print()写入文件 十四.print()在 ...

  3. python cut函数_基于python cut和qcut的用法及区别详解

    我就废话不多说了,直接上代码吧: from pandas import Series,DataFrame import pandas as pd import numpy as np from num ...

  4. python中的super用法详解_python super用法及原理详解

    这篇文章主要介绍了python super用法及原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 概念 super作为python的内建函数. ...

  5. python Format()函数的用法___实例详解(一)(全,例多)___各种格式化替换,format对齐打印

    python Format()函数的用法___实例详解(一)(全,例多) (格式化替换,关键字替换,列表字典替换,类格式化, 魔法函数格式化,对齐及填充格式化,format对齐打印) 本篇目录内容:

  6. python bisect_Python中bisect的用法及示例详解

    bisect是python内置模块,用于有序序列的插入和查找. 查找: bisect(array, item) 插入: insort(array,item) 查找 import bisect a = ...

  7. Sklearn中predict_proba函数用法及原理详解

    Sklearn中predict_proba函数用法及原理详解(以logistic回归为例) 网上对predict_proba的数学原理解释的太少了,也不明确,特意总结一下,并给出有些不能用该方法的原因 ...

  8. C# Timer用法及实例详解

    1.C# Timer用法及实例详解 http://developer.51cto.com/art/200909/149829.htm http://www.cnblogs.com/OpenCoder/ ...

  9. html渐变线条代码,CSS3实现线性渐变用法示例代码详解

    前言 演示下太老版本浏览器的渐变实现了[IE9-]; IE9以前,渐变都是通过滤镜实现的,大体的写法就是这样; .testDiv { width:400px; height:400px; border ...

  10. groupby的用法及原理详解

    groupby的用法及原理详解_回家养老-CSDN博客_groupby 写在前面的话:用了好久group by,今天早上一觉醒来,突然感觉group by好陌生,总有个筋别不过来,为什么不能够sele ...

最新文章

  1. nginx+uwsgi+django1.6 配置过程
  2. 右脑编程法--左脑是基础(4)之语言篇
  3. SpringMVC-文件上传
  4. MySQL的insert ignore与replace into不同
  5. JavaScript 项目构建工具 Grunt 实践:安装和创建项目框架
  6. LDAP----manage-account
  7. 深入理解Netscaler INat
  8. 内存颗粒和闪存颗粒的区别_国产闪存颗粒终于熬出头 紫光存储S100固态硬盘评测...
  9. Java基础知识汇总(持续更新)
  10. 自带公网IP上阿里云 | 凌云时刻
  11. 将string转换为char*
  12. Android集成三方腾讯浏览器X5内核
  13. 复盘:pearson皮尔森相关系数和spearman斯皮尔曼相关系数的区别
  14. 电力巡检系统登录页面
  15. vyos使用flask
  16. 计算机后台打印机程序,打印机不能正常工作,提示'操作无法完成,后台打印程序服务没有运行'的解决方法...
  17. 度数换算_视力表、近视度数换算方法(实用珍藏版)
  18. 人工智能证书有什么作用?
  19. 用于定义图像热区的html标记是,html 图像热区链接
  20. 华为创始人任正非简介

热门文章

  1. 03 python爬虫 (数据存储)
  2. 聊聊我的成长--数据库初学过程--MySQL的安装与配置
  3. Redis:Redis配置文件相关配置、Redis的持久化
  4. Centos 6.x Linux硬盘分区、删除分区、格式化分区
  5. DataReader关闭的问题
  6. 雨课堂 《知识产权法》笔记
  7. DATAWINDOW技巧2
  8. VS小番茄安装教程(保姆级别)
  9. 谷歌浏览器扩展程序报错 The message port closed before a response was received.
  10. 不会PS怎么抠图换背景?赶紧收藏这3个好用的一键抠图神器