From:Frida hook 常用函数分享:https://www.52pojie.cn/thread-1196917-1-1.html
From:Frida Hook Android 常用方法:https://blog.csdn.net/zhy025907/article/details/89512096
Frida官方手册 - 函数Hook:https://blog.csdn.net/freakishfox/article/details/78289293
Frida Java Hook 详解(安卓9):代码及示例(上):https://www.secpulse.com/archives/132082.html
Android 常用 Hook 方法汇总
在逆向过程中,Frida 是非常常用的 Hook 工具,这个工具在日常使用的过程中,有很多通用方法,这里记录一下,方便查阅,部分函数使用的时候,可能需要稍微修改一下。
主要是搬迁的一下参考文章的片段
Android逆向之旅—Hook神器家族的Frida工具使用详解
Frida官方文档
看雪 Frida官方手册 - JavaScript API(篇一)
看雪 Frida官方手册 - JavaScript API(篇二)
Js中几种String Byte转换方法
Frida learn by example
补充一个参考文章–Hook 属性
From:https://www.52pojie.cn/forum.php?mod=viewthread&tid=931872
Frida 文档:https://frida.re/docs/home/
Frida 从入门到入门 --- 安卓逆向菜鸟的 frida 使用说明:https://bbs.pediy.com/thread-226846.htm
frida入门总结:https://www.52pojie.cn/thread-1128884-1-1.html
*****************************************************************************************
https://bbs.pediy.com/user-811277.htm
初识Frida--Android逆向之Java层hook (一)
初识Frida--Android逆向之Java层hook (二)
进阶Frida--Android逆向之动态加载dex Hook(三)(上篇)
进阶Frida--Android逆向之动态加载dex Hook(三)(下篇)
*****************************************************************************************
frida分 客户端环境 和 服务端环境。
注:Windows 系统执行命令可以在 CMD 中进行;Linux 和 MacOS 执行命令可以在终端中进行。adb 是 Android一个调试工具,具体安装方法不是本文的重点。
Frida 客户端命令行的参数解释:
frida -U -l exploit.js -f com.package.name
其中 js 脚本的写作方式如下
setImmediate(function() { //prevent timeout
console.log("[*] Starting script");
Java.perform(function() {
myClass = Java.use("com.package.name.xxActivity");
myClass.implementation = function(v) {
// do sth.
}
})
})
import frida, sys
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = """
Java.perform(function () {
// Function to hook is defined here
var MainActivity = Java.use('com.example.hook.MainActivity');
// hook method is setString
MainActivity.setString.implementation = function (str) {
// Show a message to know that the function got called
send('hook success');
console.log('string is: ' + str));
};
});
"""
process = frida.get_usb_device().attach('com.example.hook')
#pid = device.spawn(["com.android.chrome"])
#session = device.attach(pid)
#device.resume(pid)
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Hook Start Running')
script.load()
sys.stdin.read()
示例 2:
# -*- coding: utf-8 -*-
# @Author : 佛祖保佑, 永无 bug
# @Date : 2021-03-24, 0024
# @File : frida_test.py
# @Software: PyCharm
# @description : XXX
import sys
import frida
'''
/* frida hook 格式示例 */
console.log("Script loaded successfully ");
Java.perform(function x() {
console.log("Inside java perform function");
//定位类
var my_class = Java.use("com.example.demo2.MainActivity");
console.log("Java.Use.Successfully!");//定位类成功!
//在这里更改类的方法的实现(implementation)
my_class.fun.implementation = function(x,y){
//打印替换前的参数
console.log( "original call: fun("+ x + ", " + y + ")");
//把参数替换成2和5,依旧调用原函数
var ret_value = this.fun(2, 5);
return ret_value;
}
});
'''
# 从此处开始定义用来 Hook 的 javascript 代码
js_code_1 = """
console.log("Script loaded successfully ");
function get_android_version() {
// 获取当前安卓设备的安卓版本
var android_version = Java.androidVersion;
// send('android version:' + android_version);
console.log(android_version);
}
function get_load_classes() {
//获取该应用加载的类
var class_names = Java.enumerateLoadedClassesSync();
for (var i = 0; i < class_names.length; i++){
// send('[class_name]:' + class_names[i]);
console.log('[class name]:' + class_names[i]);
}
}
function change_return_value() {
//获得 MainActivity 类
var MainActivity = Java.use('com.yaotong.crackme.MainActivity');
var java_string = Java.use('java.lang.String');
MainActivity.securityCheck.implementation = function(java_string){
send('I am here'); // 发送信息,用于回调python中的函数
return true; //劫持返回值,修改为我们想要返回的字符串
}
}
Java.perform(function() {
console.log("Inside java perform function");
get_android_version();
get_load_classes();
change_return_value();
});
"""
js_code_2 = '''
function hook_main_activity_onCreate() {
var main_activity = Java.use('com.yaotong.crackme.MainActivity');
main_activity.onCreate.overload('') = function () {
send('I am here');
this.onCreate()
}
}
Java.perform(function(){
hook_main_activity_onCreate()
})
'''
js_code_3 = '''
//var walk = Process.enumerateModules();
//for(var i=0; i
不同的参数类型都有自己的表示方法
对于基本类型,直接用它在Java中的表示方法就可以了,不用改变,例如:
基本类型 的 数组:用 左中括号 接上 基本类型 的缩写
基本类型缩写表示表:
基本类型 | 缩写 |
---|---|
boolean | Z |
byte | B |
char | C |
double | D |
float | F |
int | I |
long | J |
short | S |
修改参数为 byte[] 类型的构造函数的实现
ClassName.$init.overload('[B').implementation=function(param){
//do something
}
注:ClassName 是使用 Java.use定义的类,param 是可以在函数体中访问的参数
改多参数的构造函数的实现
ClassName.$init.overload('[B','int','int').implementation=function(param1,param2,param3){
//do something
}
ClassName.$init.overload().implementation=function(){
//do something
}
调用原构造函数
ClassName.$init.overload().implementation=function(){
//do something
this.$init();
//do something
}
注意:当 构造函数 或者 函数 有多种重载形式,比如一个类中有两个形式的 func:void func() 和 void func(int),要加上 overload 来对函数进行重载,否则可以省略overload
Hook 构造方法时,与普通方法类似,只不过将方法名改成了 $init
// Hook构造方法时与普通方法类似,只不过将方法名改成了 $init
Java.perform(function () {
var hook_cls = Java.use('com.threetails.demo.Utils');
hook_cls.$init.implementation = function (a, b) {
console.log("Hook Start...");
send('arg0: '+ a);
send('arg1: '+ b);
return this.$init(a, b);
}
});
Java.perform(function () {
var hook_cls = Java.use('com.threetails.demo.Utils');
/* ovldMethod是一个重载方法,必须加上overload(参数类型)才能Hook。
若参数类型为字符串,应该传java.lang.String,因为字符串在java中与int不同,它不是基本类型,而是类。
如果不知道参数类型,可以去掉overload,执行一下程序会抛出错误,在错误提示中可以看到对应的参数类型。*/
hook_cls.ovldMethod.overload("int").implementation = function (a) {
console.log("Hook Start...");
send('arg0: '+ a);
send('result: '+ this.ovldMethod.overload("int")(a));
return this.ovldMethod.overload("int")(a);
}
});
和 Java 一样,创建 类实例 就是调用 构造函数,用 $new 表示一个构造函数。
var ClassName=Java.use("com.luoye.test.ClassName");
var instance = ClassName.$new();
类实例化以后,就可以调用其他函数
var ClassName=Java.use("com.luoye.test.ClassName");
var instance = ClassName.$new();
instance.func();
如果方法参数的类型并非 java 的内置类型,而是一个自定义类,你又想在Hook时传入自定义对象,怎么办?假设现在有个自定义类叫 Dog,它有两个属性:name 和 age。看看下面这个示例:
Java.perform(function () {
var hook_cls = Java.use('com.threetails.demo.Utils');
// 首先声明一个dog类
var dog_cls = Java.use('com.threetails.demo.Dog');
hook_cls.feed.implementation = function (dog_obj) {
console.log("Hook Start...");
send('old dog:'+dog_obj);
// 调用dog_cls.$new来实例化一个自定义的dog对象
var new_dog = dog_cls.$new("大黄", 5);
send('new dog:'+new_dog);
// 调用原方法并将对象参数替换为自定义的dog
return this.feed(new_dog);
}
});
还是以 Dog 类为例, 现在我们需要将传入的 Dog 对象的 age 修改为 1。
正常修改
Java.perform(function () {
var hook_cls = Java.use('com.threetails.demo.Utils');
hook_cls.feed.implementation = function (dog_obj) {
console.log("Hook Start...");
// 需要使用.value来获取属性值
send(dog_obj.age.value);
// 需要使用.value来修改属性值
dog_obj.age.value = 1;
send(dog_obj.name.value);
return this.feed(dog_obj);
}
});
使用反射
修改对象属性还有另外一种方式,也就是使用 java 的反射, 这种方式相对复杂一些。
Java.perform(function () {
var hook_cls = Java.use('com.threetails.demo.Utils');
// 首先需要声明一个类构造器
var clazz = Java.use('java.lang.Class');
hook_cls.feed.implementation = function (dog_obj) {
console.log("Hook Start...");
// 调用Java.cast方法,传入类和类构造器得到类的id,再调用getDeclaredField得到属性id
var ageid = Java.cast(dog_obj.getClass(),clazz).getDeclaredField('age');
// 设置该属性为可访问
ageid.setAccessible(true);
// 调用get方法获取属性值
var old_value = ageid.get(dog_obj);
// 此处必须使用console.log才能打印我们需要的值
console.log(old_value);
// 调用setInt修改属性值
ageid.setInt(dog_obj, 1);
return this.feed(dog_obj);
}
});
Java.perform(function () {
// Java.use(类的路径),声明一个要hook的类
var hook_cls = Java.use('com.threetails.demo.Utils');
// 修改该类下normalMethod的实现,参数个数需要与原方法保持一致
hook_cls.normalMethod.implementation = function (a, b) {
console.log("Hook Start...");
send('arg0: '+ a);
send('arg1: '+ b);
send('result: '+ this.normalMethond(a,b));
/* 为了让程序正常运行,需要调用原方法将结果返回。若想修改参数值,可以在调用时直接将参数替换,
如:return this.normalMethond(1,2),但需注意参数类型的一致性。*/
return this.normalMethond(a,b);
}
});
注: 在修改函数实现时,如果原函数有返回值,那么我们在实现时,也要返回合适的值。( 构造函数 没有返回值 )
修改函数名为 func,参数为 byte[] 类型的函数的实现。如果原函数有返回值,则修改后函数也需要有返回值。
ClassName.func.overload('[B').implementation=function(param){
// do something
// return ...
}
如果原函数有返回值,则修改后函数也需要有返回值。
ClassName.func.overload().implementation=function(){
// do something
// return
}
带返回值的函数修改
ClassName.func.overload().implementation=function(){
// do something
return this.func();
}
用 Java.cast 方法来对一个对象进行类型转换,
示例:将 variable 转换成 java.lang.String
var StringClass = Java.use("java.lang.String");
var NewTypeClass = Java.cast(variable, StringClass);
这个字段标记 Java虚拟机(例如: Dalvik 或者 ART)是否已加载, 操作Java任何东西的之前,要确认这个值是否为 true
当 Javascript 代码被成功附加到目标进程时,则调用 Java.perform(fn) 方法,我们核心的代码要在里面写。格式:
Java.perform(function(){
//do something...
});
var MainActivity = Java.use('ese.xposedtest.MainActivity');
//外部类 修改返回值
MainActivity.OutClass.implementation = function (arg) {
var ret = this.OutClass(arg);
console.log('Done:' + arg);
return ret;
}
// If two methods of a class have the same name
// you need to use 'overload'
// hook method 1
myClass.myMethod.overload().implementation = function(){
// do sth
}
myClass.myMethod.overload("[B", "[B").implementation = function(param1, param2) {
// do sth
}
myClass.myMethod.overload("android.context.Context", "boolean").implementation = function(param1, param2){
// do sth
}
// hook method 2
//待补充
// Intercept the initialization of java.lang.Stringbuilder's overloaded constructor.
// Write the partial argument to the console.
const StringBuilder = Java.use('java.lang.StringBuilder');
//We need to overwrite .$init() instead of .$new(), since .$new() = .alloc() + .init()
StringBuilder.$init.overload('java.lang.String').implementation = function (arg) {
var partial = "";
var result = this.$init(arg);
if (arg !== null) {
partial = arg.toString().replace('\n', '').slice(0,10);
}
// console.log('new StringBuilder(java.lang.String); => ' + result)
console.log('new StringBuilder("' + partial + '");')
return result;
}
console.log('[+] new StringBuilder(java.lang.String) hooked');
const JavaString = Java.use('java.lang.String');
var exampleString1 = JavaString.$new('Hello World, this is an example string in Java.');
console.log('[+] exampleString1: ' + exampleString1);
var inInnerClass = Java.use('ese.xposedtest.MainActivity$inInnerClass');
inInnerClass.methodInclass.implementation = function()
{
var arg0 = arguments[0];
var arg1 = arguments[1];
send("params1: "+ arg0 +" params2: " + arg1);
return this.formInclass(1,"Frida");
}
Interceptor.attach(Module.findExportByName("xxx.so" , "xxxx"), {
onEnter: function(args) {
send("open(" + Memory.readCString(args[0])+","+args[1]+")");
},
onLeave:function(retval){
}
});
var ah = Java.use("com.ah");
console.log("To Log: " + ah.a.value);
ah.a.value = true;
AndroidLog = Java.use("android.util.Log")
AndroidException = Java.use("java.lang.Exception")
function printStackTrace(){
console.log(AndroidLog .getStackTraceString(AndroidException .$new()));
}
function byte2string(array){
var result = "";
for(var i = 0; i < array.length; ++i){
result+= (String.fromCharCode(array[i]));
}
return result;
}
ArrayBuffer转String: 解决中文乱码(模板)
function ab2str(buf) {
return new Uint16Array(buf)
// encodedString = String.fromCodePoint.apply(null, new Uint16Array(buf));
// // decodedString = encodeURI(encodedString);//没有这一步中文会乱码
// // console.log(decodedString);
// return encodedString
}
function string2byte(str){
for (var i = 0,arr=[]; i < str.length;i++){
arr.push(str.charCodeAt(i));
}
return new Uint8Array(arr);
}
字符串 转 Uint8Array (模板)
function stringToUint8Array(str){
var arr = [];
for (var i = 0, j = str.length; i < j; ++i) {
arr.push(str.charCodeAt(i));
}
var tmpUint8Array = new Uint8Array(arr);
return tmpUint8Array
}
string 转 ArrayBuffer (模板)
function str2ab(str) {
var buf = new ArrayBuffer(str.length * 2); // 每个字符占用2个字节
var bufView = new Uint16Array(buf);
for (var i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
Uint8Array 转 字符串 (模板)
function Uint8ArrayToString(fileData){
console.log(fileData)
var dataString = "";
for (var i = 0; i < fileData.length; i++) {
dataString += String.fromCharCode(fileData[i]);
}
return dataString
}
function intTobytes(n) {
var bytes = [];
for (var i = 0; i < 2; i++) {
bytes[i] = n >> (8 - i * 8);
}
return bytes;
}
function str2arraybuffer(str) {
var buf = new ArrayBuffer(str.length * 2); // 每个字符占用2个字节
var bufView = new Uint16Array(buf);
for (var i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
console.log(new Date(new Date().getTime())
这个非常常用,因为有些byte是没有办法转成string的,只能转成hex,用来查看
function byteToHexString(uint8arr) {
if (!uint8arr) {
return '';
}
var hexStr = '';
for (var i = 0; i < uint8arr.length; i++) {
var hex = (uint8arr[i] & 0xff).toString(16);
hex = (hex.length === 1) ? '0' + hex : hex;
hexStr += hex;
}
return hexStr.toUpperCase();
}
function hexStringToByte(str) {
if (!str) {
return new Uint8Array();
}
var a = [];
for (var i = 0, len = str.length; i < len; i+=2) {
a.push(parseInt(str.substr(i,2),16));
}
return new Uint8Array(a);
}
# -*- 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()
基于frida框架Hook native中的函数(1):https://blog.csdn.net/qq1084283172/article/details/80788959
安卓逆向入门之使用 frida 框架简单 Hook native 层的函数:https://blog.csdn.net/cpongo1/article/details/102580584
抖音数据采集Frida教程,Frida Java Hook 详解:代码及示例(下):https://segmentfault.com/a/1190000039041770
hook工具 frida 原理及使用:https://www.jianshu.com/p/51e6aef175a2
哔哩哔哩:https://www.bilibili.com/video/av927982888/
frida hook AES DES RSA 自吐算法:https://www.cnblogs.com/shlyd/p/14057423.html
frida hook java原生算法同时打印调用堆栈:https://blog.csdn.net/weixin_34365417/article/details/93088342
FridaHookSysAPI:https://github.com/zhudongjie/FridaHookSysAPI
Frida 各种 Hook 示例 js :https://www.jianshu.com/p/4291ee42c412
百度关键字:frida hook 安卓 抓包 或者 frida hook okhttp 抓包
精品连载丨安卓 App 逆向课程之四 frida 注入 Okhttp 抓包中篇:https://blog.csdn.net/cqcre/article/details/107293553
Android的网络请求库选择:https://www.jianshu.com/p/e97530e9e0b6
android 网络请求库的比较:https://www.cnblogs.com/waterhorse/p/5153624.html
示例代码:
# -*- coding: UTF-8 -*-
import sys
import frida
'''
哔哩哔哩:https://www.bilibili.com/video/av927982888/
frida hook AES DES RSA 自吐算法:
https://www.cnblogs.com/shlyd/p/14057423.html
frida hook java原生算法同时打印调用堆栈:
https://blog.csdn.net/weixin_34365417/article/details/93088342
FridaHookSysAPI
https://github.com/zhudongjie/FridaHookSysAPI
Frida 各种 Hook 示例 js
https://www.jianshu.com/p/4291ee42c412
'''
js_code = """
function showStacks() {
send(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
}
// 这两种打印 堆栈信息的方法都可以
function showStacks_2() {
Java.perform(function () {
send(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
});
}
function bytesToHex(arr)
{
var str = "";
for(var i=0; i
有了以上的基础知识,我们就可以进行编写代码了
场景:假设有以下的程序,给isExcellent方法传入两个值,通过计算,返回一个布尔值,表示是否优秀。默认情况下,它是只会显示是否优秀:false
的,因为我们默认传入的数很小:
Java 代码:
public class MainActivity extends AppCompatActivity {
private String TAG="Crackme";
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView =findViewById(R.id.tv);
textView.setText("是否优秀:"+isExcellent(46,54));
}
private boolean isExcellent(int chinese, int math){
if( chinese + math >=180){
return true;
}
else{
return false;
}
}
}
我们编写一个脚本来 Hook isExcellent函数,使它返回true,显示为是否优秀:true。对于这种简单的场景,直接修改返回值就可以了,因为只有结果是重要的。
JavaScript 代码
想直接返回结果很简单,直接在匿名方法里return即可。
if(Java.available){
Java.perform(function(){
var MainActivity = Java.use("com.luoyesiqiu.crackme.MainActivity");
MainActivity.isExcellent.implementation=function(){
return true;
}
});
}
注:这里为什么要打开两次App?第一打开是为了让 frida 能够找到进程,第二次打开是为了验证结果,即使 Hook 成功了,界面是有缓存的,并不能实时显示 Hook 结果,所以需要重新打开 App
场景:假设有以下场景,isExcellent 除了返回是否优秀以外,方法的内部还把分数打印出来。
Java 代码:
public class MainActivity extends AppCompatActivity {
private String TAG="Crackme";
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView =findViewById(R.id.tv);
textView.append("是否优秀:"+isExcellent(46,54)+"\n");
}
private boolean isExcellent(int chinese, int math){
textView.append("语文+数学总分:"+(chinese+math)+"\n");
if( chinese + math >=180){
return true;
}
else{
return false;
}
}
}
这种情况下我们不可能只返回是否优秀吧,显示的总分很低,但是却返回优秀,是很尴尬的...所以我们要修改 isExcellent 方法的参数,使其通过计算打印和返回合理的值。
JavaScript 代码
if(Java.available){
Java.perform(function(){
var MainActivity = Java.use("com.luoyesiqiu.crackme.MainActivity");
MainActivity.isExcellent.overload("int","int").implementation=function(chinese,math){
return this.isExcellent(95,96);
}
});
}
上面的代码,通过 overload 方法重载参数,修改 isExcellent 方法实现,并在实现函数里调用原来的方法,得到新的返回值
在本文刚开始的时候说到,我们可以编写 Python 代码来配合 Javascript 代码注入。
下面我们来看看,怎么使用,先看一段代码:
# -*- coding: UTF-8 -*-
import frida, sys
jscode = """
if(Java.available){
Java.perform(function(){
var MainActivity = Java.use("com.luoyesiqiu.crackme.MainActivity");
MainActivity.isExcellent.overload("int","int").implementation=function(chinese,math){
console.log("[javascript] isExcellent be called.");
send("isExcellent be called.");
return this.isExcellent(95,96);
}
});
}
"""
def on_message(message, data):
if message['type'] == 'send':
print(" {0}".format(message['payload']))
else:
print(message)
pass
# 查找USB设备并附加到目标进程
session = frida.get_usb_device().attach('com.luoyesiqiu.crackme')
# 在目标进程里创建脚本
script = session.create_script(jscode)
# 注册消息回调
script.on('message', on_message)
print(' Start attach')
# 加载创建好的javascript脚本
script.load()
# 读取系统输入
sys.stdin.read()
上面是一段 Python 代码,我们来分析它的步骤:
注: 1. 如果想在 javascript 输出日志,可以调用 console.log()方法。
2. 如果想给客户端发送消息,可以在 javascript 代码里调用 send()方法,
并在客户端 Python 代码里注册一个消息回调来接收服务端发来的消息。
可以看到,结合python代码,使注入更加的灵活了。
如果想看 Python端 frida 模块的代码,可以访问:https://github.com/frida/frida-python/blob/master/frida/core.py
参考:https://www.frida.re/docs/home/
# -*- coding: UTF-8 -*-
import frida, sys
jscode = """
if(Java.available){
Java.perform(function(){
var MainActivity = Java.use("com.lanshifu.demo_module.ui.activity.DemoMainActivity");
MainActivity.testCrash.overload("int").implementation=function(chinese,math){
console.log("[javascript] testCrash method be called.");
send("hook testCrash method success>>>");
return this.testCrash(12345678);
}
//声明一个Java类
var MyClass = Java.use("com.lanshifu.demo_module.ui.activity.DemoHookTestActivity$MyClass");
//hook 无参构造
MyClass.$init.overload().implementation=function(){
//调用原来构造
this.$init();
send("hook 无参构造 ");
}
//hook 有参构造,入参是 int[]
MyClass.$init.overload('[I').implementation=function(param){
send("hook 有参构造 init(int[] i) method ");
}
//hook 多个参数构造,入参是 int,int
MyClass.$init.overload("int","int").implementation=function(param1,param2){
send("hook success ");
send(param1);
send(param2);
//修改返回值
return 100;
}
//void函数
MyClass.method1.overload().implementation=function(){
send("hook method1 ");
this.method1();
}
//有入参的函数
MyClass.method2.overload("int","int").implementation=function(param1,param2){
send("hook method2 ");
//实例化一个类并调用它的method3方法
var ClassName=Java.use("com.lanshifu.demo_module.ui.activity.DemoHookTestActivity$MyClass");
var instance = ClassName.$new();
instance.method3(1,2,3);
this.method2(100,100)
}
//
MyClass.method3.overload("int","int","int").implementation=function(param1,param2,param3){
send("hook method3 ");
this.method3(100,100,100)
}
});
}
"""
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
pass
# 查找USB设备并附加到目标进程
session = frida.get_usb_device().attach('com.lanshifu.demo_module')
# 在目标进程里创建脚本
script = session.create_script(jscode)
# 注册消息回调
script.on('message', on_message)
print('[*] Start attach')
# 加载创建好的javascript脚本
script.load()
# 读取系统输入
sys.stdin.read()
定位关键函数
取消勾选反混淆,否则影响关键函数定位
尝试搜索 AES、DES、RSA、加密、encode、decode 等关键字,也可以仔细跟进 http 等请求发起过程定位加解密函数
分析实现加密与解密的函数。加密函数:传入了公钥以及需要加密的字节数组
编写 hook.js
function main() {
if (Java.available) {
console.log("*********** hook start ************");
Java.perform(function() {
var JniUtils = Java.use("com.gdtel.eshore.mss.lib.b.b");
JniUtils.a.overload("java.security.interfaces.RSAPrivateKey","[B").implementation=function(arg1,arg2){
console.log("*********** decodeByAES start ************");
var a =this.a(arg1,arg2);
var String = Java.use("java.lang.String")
var data = String.$new(a)
console.log("from data: " + data)
return a;
}
JniUtils.a.overload("java.security.interfaces.RSAPublicKey","[B").implementation=function(arg3,arg4){
console.log("*********** encodeByRSA start ************");
var b =this.a(arg3,arg4);
var String1 = Java.use("java.lang.String")
var data1 = String1.$new(arg4)
console.log("from data: " + data1)
return b;
});
}
function printTrace(){
console.log("****************** printTrace start ***********************");
var jAndroidLog = Java.use("android.util.Log");
var jException = Java.use("java.lang.Exception");
var threadef = Java.use('java.lang.Thread');
var threadinstance = threadef.$new();
var stack = threadinstance.currentThread().getStackTrace();
console.log("Full call stack:");
for(var i = 0; i < stack.length; ++i){
console.log(stack[i].toString());
}
console.log("****************** printTrace finish ***********************");
}
}
setImmediate(main)
运行:frida -U -l demo.js xxx.xxx.xxx.xxx
关键字:frida hook 混淆后的方法
求助] Frida Hook混淆后的类、方法、变量异常
https://github.com/xiaokanghub/Android
https://bbs.pediy.com/thread-258200.htm
:https://developer.aliyun.com/article/690210
使用 codecs.open(scriptpath, "r", "utf-8")
打开文件读取 js 即可。
device = frida.get_device_manager().get_device("094fdb0a0b0df7f8")
mgr = frida.get_device_manager()
device = mgr.add_remote_device("30.137.25.128:13355")
pid = device.spawn([packename])
process = device.attach(pid)
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
device.resume(pid)
import sys
import frida
session = frida.attach("hello")
script = session.create_script("""
Interceptor.attach(ptr("%s"), {
onEnter: function(args) {
send(args[0].toString());
var op = recv('input', function(value) {
args[0] = ptr(value.payload);
});
op.wait();
}
});
""" % int(sys.argv[1], 16))
def on_message(message, data):
print(message)
val = int(message['payload'], 16)
script.post({'type': 'input', 'payload': str(val * 2)})
script.on('message', on_message)
script.load()
sys.stdin.read()
# -*- coding: utf-8 -*-
import frida
system_session = frida.attach(0)
bytecode = system_session.compile_script(name="bytecode-example", source="""\
'use strict';
rpc.exports = {
listThreads: function () {
return Process.enumerateThreadsSync();
}
};
""")
session = frida.attach("Twitter")
script = session.create_script_from_bytes(bytecode)
script.load()
api = script.exports
# 这里的 list_threads 是 listThreads 驼峰命名法自动转换后的结果,
# 由 rpc exports 功能导出给 python 调用
print("api.list_threads() =>", api.list_threads())
function hook_sms() {
var SmsManager = Java.use('android.telephony.SmsManager');
SmsManager.sendDataMessage.implementation = function (
destinationAddress, scAddress, destinationPort, data, sentIntent, deliveryIntent) {
console.log("sendDataMessage destinationAddress: " + destinationAddress + " port: " + destinationPort);
showStacks();
this.sendDataMessage(destinationAddress, scAddress, destinationPort, data, sentIntent, deliveryIntent);
}
}
setTimeout
延迟执行一次setTimeout(funcA, 15000);
setInterval
间隔循环执行var id_ = setInterval(funcB, 15000);
clearInterval(id_); // 终止
function bin2String(array) {
if (null == array) {
return "null";
}
var result = "";
try {
var String_java = Java.use('java.lang.String');
result = String_java.$new(array);
} catch (e) {
dmLogout("== use bin2String_2 ==");
result = bin2String_2(array);
}
return result;
}
function bin2String_2(array) {
var result = "";
try {
var tmp = 0;
for (var i = 0; i < array.length; i++) {
tmp = parseInt(array[i]);
if (tmp == 0xc0
|| (tmp < 32 && tmp != 10)
|| tmp > 126) {
return result;
} // 不是可见字符就返回了, 换行符除外
result += String.fromCharCode(parseInt(array[i].toString(2), 2));
}
} catch (e) {
console.log(e);
}
return result;
}
function getFormatDate() {
var date = new Date();
var month = date.getMonth() + 1;
var strDate = date.getDate();
if (month >= 1 && month <= 9) {
month = "0" + month;
}
if (strDate >= 0 && strDate <= 9) {
strDate = "0" + strDate;
}
var currentDate = date.getFullYear() + "-" + month + "-" + strDate
+ " " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
return currentDate;
}
function dmLogout(str) {
var threadid = Process.getCurrentThreadId();
console.log("[" + threadid + "][" + getFormatDate() + "]" + str);
}
var showStacks = function () {
Java.perform(function () {
dmLogout(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); // 打印堆栈
});
}
var anti_fgets = function () {
dmLogout("anti_fgets");
var fgetsPtr = Module.findExportByName("libc.so", "fgets");
var fgets = new NativeFunction(fgetsPtr, 'pointer', ['pointer', 'int', 'pointer']);
Interceptor.replace(fgetsPtr, new NativeCallback(function (buffer, size, fp) {
var retval = fgets(buffer, size, fp);
var bufstr = Memory.readUtf8String(buffer);
if (bufstr.indexOf("TracerPid:") > -1) {
Memory.writeUtf8String(buffer, "TracerPid:\t0");
// dmLogout("tracerpid replaced: " + Memory.readUtf8String(buffer));
}
return retval;
}, 'pointer', ['pointer', 'int', 'pointer']));
};
var anti_antiDebug = function () {
var funcPtr = null;
funcPtr = Module.findExportByName("xxxx.so", "p57F7418DCD0C22CD8909F9B22F0991D3");
dmLogout("anti_antiDebug " + funcPtr);
Interceptor.replace(funcPtr, new NativeCallback(function (pathPtr, flags) {
dmLogout("anti ddddddddddddddebug LR: " + this.context.lr);
return 0;
}, 'int', ['int', 'int']));
};
function hook_native_newString() {
var env = Java.vm.getEnv();
var handlePointer = Memory.readPointer(env.handle);
dmLogout("env handle: " + handlePointer);
var NewStringUTFPtr = Memory.readPointer(handlePointer.add(0x29C));
dmLogout("NewStringUTFPtr addr: " + NewStringUTFPtr);
Interceptor.attach(NewStringUTFPtr, {
onEnter: function (args) {
...
}
});
}
function hook_native_GetStringUTFChars() {
var env = Java.vm.getEnv();
var handlePointer = Memory.readPointer(env.handle);
dmLogout("env handle: " + handlePointer);
var GetStringUTFCharsPtr = Memory.readPointer(handlePointer.add(0x2A4));
dmLogout("GetStringUTFCharsPtr addr: " + GetStringUTFCharsPtr);
Interceptor.attach(GetStringUTFCharsPtr, {
onEnter: function (args) {
var str = "";
Java.perform(function () {
str = Java.cast(args[1], Java.use('java.lang.String'));
});
dmLogout("GetStringUTFChars: " + str);
if (str.indexOf("linkData:") > -1) { // 设置过滤条件
dmLogout("========== found linkData LR: " + this.context.lr + " ==========");
}
}
});
};
var hook_uri = function () {
// coord: (7520,0,19) | addr: Ljava/net/URI;->parseURI(Ljava/lang/String;Z)V | loc: ?
var uri = Java.use('java.net.URI');
uri.parseURI.implementation = function (a1, a2) {
a1 = a1.replace("xxxx.com", "yyyy.com");
dmLogout("uri: " + a1);
showStacks();
return this.parseURI(a1, a2);
}
}
function hook_xml() {
var xmlSerializer = Java.use('org.kxml2.io.KXmlSerializer'); // org.xmlpull.v1.XmlSerializer
xmlSerializer.text.overload('java.lang.String').implementation = function (text) {
dmLogout("xtext: " + text);
if ("GPRS" == text) {
dmLogout("======>>> found GPRS");
showStacks();
}
return this.text(text);
}
}
function hook_log() {
dmLogout(TAG, "do hook log");
var Log = Java.use('android.util.Log');
Log.v.overload('java.lang.String', 'java.lang.String').implementation = function (tag, content) {
dmLogout(tag + " v", content);
};
Log.d.overload('java.lang.String', 'java.lang.String').implementation = function (tag, content) {
dmLogout(tag + " d", content);
};
Log.w.overload('java.lang.String', 'java.lang.String').implementation = function (tag, content) {
dmLogout(tag + " w", content);
};
Log.i.overload('java.lang.String', 'java.lang.String').implementation = function (tag, content) {
dmLogout(tag + " i", content);
};
Log.e.overload('java.lang.String', 'java.lang.String').implementation = function (tag, content) {
dmLogout(tag + " e", content);
};
}
var friendlyFunctionName = new NativeFunction(friendlyFunctionPtr, 'void', ['pointer', 'pointer']);
var returnValue = Memory.alloc(sizeOfLargeObject);
friendlyFunctionName(returnValue, param1);