Frida ALL IN ONE

文章目录

  • Frida all in one
    • Frida环境与基础使用
      • 多python frida版本切换
        • pyenv安装
        • pyenv使用
      • Frida环境安装
        • 手机环境配置
          • 监听
      • Objection环境安装
      • Frida开发环境搭建
      • Objection使用
        • Objection基本使用
        • objection插件
        • Wallbreaker
        • FRIDA-DEXDump
        • objection高级使用
    • Frida Java层应用
      • hook方法
        • hook静态方法与实例方法
        • hook构造方法
        • 对方法重载的hook
        • hook指定方法的所有重载
        • hook内部类与匿名类中的方法
      • 内存搜索
        • 查找实例对象
        • 主动调用方法、获取及修改变量
        • 枚举所有类并hook所有方法
        • hook动态加载dex
        • 内存搜索接口
          • 枚举类的所有接口
          • 寻找接口的实现类
        • 定位抽象类的实现类
      • 打印与参数构造
        • 加载dex使用gson打印
        • 查看类与类型强转
        • Array数组
          • hook array数组
          • 构造 array数组
          • 打印 array数组
          • byte[]打印
        • 枚举
        • HashMap类型
        • 不可见函数名hook
      • Others
        • Frida构造多线程(新建类)
        • 获取context
        • 强制主线程运行
    • java层代码模板复用
      • 打印调用栈
      • 格式化打印十六进制数据
      • 事件
        • hook Toast提示 定位
        • hook findViewById 定位组件
        • hook StartActivity
        • hook onClick事件 定位
      • hook Keystore:客户端证书校验
    • Frida RPC(Remote procedure call)
      • RPC主动调用
      • rpc动态修改
      • More
    • Frida native层应用
      • 判断Thumb OR arm
      • hook用户函数
        • attach静态注册(或导出)函数
        • 主动调用静态注册(或导出)函数
        • replace静态注册(或导出)函数
        • 通过地址偏移操作未导出函数
      • 模块相关操作
        • 枚举出所有模块的所有导出符号
        • 遍历某模块符号/导出/导入
      • JNI框架层的利用
        • JNI框架hook:
        • JNI框架replace:
    • native层代码模板复用
      • Frida写文件
      • 遍历module的导出表与符号表
      • 读写 std::string
    • 相关脚本使用
      • Unpackers
        • FRIDA-DEXDump
        • frida_fart
        • frida_dump
      • NetWork
        • r0capture
        • OkHttpLogger-Frida
        • frida_ssl_logger
        • okhttp-sslunpinning
      • Trace
        • jnitrace
        • frida_hook_libart
        • frida-trace
      • Memory
        • Wallbreaker
    • Or

Frida all in one

努力ALL IN ONE,部分代码可配合AndroidReversePractice/FridaTestApp at main · Forgo7ten/AndroidReversePractice (github.com)试验。

JavaScript API | Frida • A world-class dynamic instrumentation framework

Frida环境与基础使用

多python frida版本切换

pyenv安装

curl -L https://gitee.com/ibopo//pyenv-installer/raw/master/bin/pyenv-installer | bash
cd ~/.pyenv && src/configure && make -C src

.bashrc添加

# pyenv start
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
export PATH="$PYENV_ROOT/shims:$PATH"
if command -v pyenv 1>/dev/null 2>&1; then
 eval "$(pyenv init -)"
fi
eval "$(pyenv virtualenv-init -)"
# pyenv end

pyenv使用

# 下载python版本3.8.2
pyenv install 3.8.2

# 查看pyenv版本
pyenv versions

# 切换某个python版本
pyenv local 3.8.2

# 切换到系统python版本
python local system

Frida环境安装

需前往frida · PyPI查看支持python版本(如frida 15.0.13不支持python3.7)

pip install frida-tools

安装特定版本

### Python3.8.2 ###
pyenv local 
pip install frida==12.8.0
pip install frida-tools==5.3.0
pip install objection==1.8.4

手机环境配置

前往Releases · frida/frida (github.com)下载对应的frida-server文件,frida-server版本要和frida版本一致

push到设备目录,赋予执行权限

监听

设置端口号为8888

./friandx86 -l 0.0.0.0:8888

Objection环境安装

用原版源

pip install objection

Frida开发环境搭建

全局获得代码提示

npm install --save @types/frida-gum
# -g到全局, --save到当前目录
git clone https://gitee.com/Forgo7ten/frida-agent-example.git
cd frida-agent-example/
npm install

agent目录下编写

/** test.js **/
function main() {
    Java.perform(function x() {
        console.log("Hello Frida")
    })
}
setImmediate(main)

控制台执行

frida -H 192.168.0.106:8888 -f com.android.settings -l test.js --no-pause

-H:设置主机和端口号
-f:启动某个应用(packagename)
-l:执行的js文件
  • npm run watch会监控代码修改自动编译生成js文件

或者python脚本执行

# loader.py
import time
import frida

# 获取指定 UID/远程 设备
device = frida.get_device_manager().add_remote_device("192.168.0.103:8888")
# 寻找某apk包名
pid = device.spawn("com.android.settings")
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
with open("test.js") as f:
    # 创建js脚本
    script = session.create_script(f.read())
# 加载js脚本
script.load()

Objection使用

Objection基本使用

objection启动并注入内存

objection -N -h [hostname] -p [port] -d -g [packagename] explore
# -N 指定网络连接
# -h 指定主机名
# -p 指定端口
# -d 启用debug模式
# -g 打开的app包名

memory

# 查看内存中加载的库
memory list modules

# 查看库的导出函数
memory list exports [modulename]

# 结果保存到文件(以json形式)
memory list exports [modulename] --json [filename]
# 提取整个内存
memory dump all [filename]

# 提取部分内存
memory dump from_base [start_addr] [length] [file_name]

android

# 搜索类的实例
android heap search instances [class_name]
# 主动调用实例方法
android heap execute [instance_handle] [method_name]

# 在实例上执行js代码
android heap evaluate [instance_handle] {Enter}
## 输入js代码 ## (clazz代表当前类 )

启动activity或service

# 启动activity
android intent launch_activity [activity]
# 启动service
android intent launch_service [service]

hooking

# 查看内存中的activity
android hooking list activities

# 查看内存中的service
android hooking list services

# 查看内存中所有的类
android hooking list classes

# 列出某个类的所有方法
android hooking list class_methods [class]
# 搜索包含特定关键词的类
android hooking search classes [string]

# 内存中搜索包含特定关键词的方法
android hooking search methods [string]
# 生成该类所有方法的hook,每次调用都会被log
android hooking generate simple [class]

# 附带参数:额外打印参数,调用栈,返回值
android hooking watch class [class_method] --dump-args --dump-backtrace --dump-return

# hook某方法(同时会hook所有重载)
android hooking watch [class_method] [method_name]

objection jobs

# 查看jobs列表(当有hook任务时)
jobs list

# 删除任务 JobID通过list得到
jobs kill [JobID]

objection插件

插件使用

plugin load [FileUrl]

Wallbreaker

FRIDA-DEXDump

objection高级使用

objection spawn进程(hook某些一启动就执行的app)

objection -d -g [packageName] explore --startup-command 'android hooking watch class xxx'
objection -d -g [packageName] explore -c commands.txt

Frida Java层应用

hook方法

hook静态方法与实例方法

Frida对静态方法与实例方法没有区分,直接hook即可。

function main() {
    Java.perform(function () {
            // 先查找HookedObject类,然后hook其的stringTwo方法
            Java.use("com.forgotten.fridatestapp.HookedObject").stringTwo.implementation = function (arg) {
              // this为当前实例,获得原方法执行的结果
              var result = this.stringTwo(arg);
              // 打印参数和原方法结果
              console.log("stringTwo arg,result: ", arg, result);
              // 对方法的结果进行修改(相当于重写了该方法)
              return Java.use("java.lang.String").$new("hhello");
            };
    });
}
// Frida一附加上,就执行函数
setImmediate(main);

hook构造方法

构造方法为$init,例如

function main2() {
    Java.perform(function(){
        // hook有重载的无参方法时,overload()填空
        Java.use("com.forgotten.fridatestapp.HookedObject").$init.overload().implementation = function () {
            // this为当前实例,获得原方法执行的结果
            var result = this.$init();
            // 可以打印参数和原方法结果
            console.log("call $init");
            // 返回原方法执行的结果
            return result;
        };
    })
}

对方法重载的hook

使用.overload()来确定hook哪个重载方法

function main() {
    Java.perform(function () {
        // hook addNumber方法 function参数列表可以什么都不填
        Java.use("com.forgotten.fridatestapp.HookedObject").addNumber.overload("int", "int").implementation = function () {
            // 内置有变量[argument],为方法的参数列表
            for (var i = 0; i < arguments.length; i++) {
                console.log("addNumber arguments[" + i + "]=" + arguments[i]);
            }
            var result = this.addNumber(arguments[0], arguments[1]);
            console.log("addNumber arg,result: ", arguments, result);
            return 99;
        };
    });
}
// Frida一附加上,就执行函数
setImmediate(main);

hook指定方法的所有重载

function hookMethodAllOverloads(className, methodName) {
    Java.perform(function () {
        // hook 指定方法的所有重载
        var clazz = Java.use(className);
        // Object.toString同Object["toString"]
        var overloadsLength = clazz[methodName].overloads.length;
        for (var i = 0; i < overloadsLength; i++) {
            clazz[methodName].overloads[i].implementation = function () {
                // 主动调用原方法获得结果
                var result = this[methodName].apply(this, arguments);
                var paramStr = "";
                // 遍历arguments
                for (var j = 0; j < arguments.length; j++) {
                    if (j == arguments.length - 1) {
                        paramStr += arguments[j];
                    } else {
                        paramStr += arguments[j] + ",";
                    }
                }
                // 打印参数以及结果
                console.log("Called", className + "." + methodName + "(" + paramStr + ") :", result);
                // 调用原方法
                return result;
            };
        }
        console.log("[" + overloadsLength + "]", className + "." + methodName, "Hooked!");
    });
}

function main() {
    var className = "com.forgotten.fridatestapp.HookedObject";
    var methodName = "addNumber";
    hookAllOverloads(className, methodName);
}

hook内部类与匿名类中的方法

内部类获取时className使用$拼接,如:

var InnerClazz = Java.use("com.forgotten.fridatestapp.HookedObject$innerClass");

匿名类也同样使用$拼接,但其后的要使用smali或者objection内存搜索来查看

var clazz = Java.use("com.forgotten.fridatestapp.MainActivity$1");

内存搜索

查找实例对象

function main() {
    Java.perform(function () {
        // 要查找的对象类名
        var className = "com.forgotten.fridatestapp.MainActivity";
        Java.choose(className, {
            onMatch: function (instance) {
                console.log("Found it!!", instance);
                // something to do...
            },
            onComplete: function () {
                console.log("Search complete!");
            },
        });
    });
}

保存对象在外部方法使用

Java.retain(obj):复制一份obj的java包装器以便于以后使用。

Java.perform(() => {
    const Activity = Java.use("android.app.Activity");
    let lastActivity = null;
    Activity.onResume.implementation = function () {
        lastActivity = Java.retain(this);
        this.onResume();
    };
});

主动调用方法、获取及修改变量

  • 主动调用方法

    • 静态方法使用Java.use获得后直接调用
    • 非静态方法需要使用Java.choose查找到类实例后进行调用
    • 构造方法为$init
    • 对于方法重载
      • 可根据传入的参数自动选择重载
      • 或通过.overload().apply()手动选择选择要调用的重载方法
  • 静态/非静态变量

    • 获取成员变量:[field].value
      • 获取静态成员的值:使用或者类实例
      • 获取非静态成员的值:内存搜索到类实例后获取。
    • 设置成员变量的值,写法是[field_name].value = [value],其他方面和函数一样。
    • 如果有一个成员变量和成员函数的名字相同,则在其前面加一个_,如_[field_name].value = [value]
function invoke() {
    Java.perform(function () {
        // 在内存中搜索类的实例
        Java.choose("com.forgotten.fridatestapp.HookedObject", {
            // 如果匹配上执行回调,参数为类实例
            onMatch: function (instance) {
                console.log("Found `HookedObject` instance:", instance);
                // 打印私有成员变量的值,需要[field].value
                console.log("instance.score =", instance.score.value);
                // 打印静态成员变量的值
                console.log("HookedObject.msg =", Java.use("com.forgotten.fridatestapp.HookedObject").msg.value);
                console.log("instance.msg =", instance.msg.value);
                // 修改成员变量的值
                instance.score.value = Java.use("java.lang.Integer").parseInt("-900");
                // 打印修改之后的值
                console.log("instance.score =", instance.score.value);
                // 与方法同名的成员变量,需前面加_
                console.log("instance.stringTwo =", instance._stringTwo.value);
                
                // 主动调用方法,根据传入的参数自动选择重载
                console.log(instance.addNumber(1, 2));
                console.log(instance.addNumber(4, 5, 6));
                // 调用指定方法重载:apply参数一为调用的对象,参数二为参数列表
                console.log(instance.addNumber.overload("int", "int").apply(instance,[8,9]));
            },
            // 搜索完成执行回调
            onComplete: function () {
                console.log("Found Completed");
            },
        });
    });
}

枚举所有类并hook所有方法

配合hook指定方法的所有重载来hook所有的方法

使用Java.enumerateLoadedClasses()API

function main(){
    Java.perform(function(){
        Java.enumerateLoadedClasses({
            // name为加载的类名字符串
            onMatch: function(name,handle){
                // 可以通过包名限定需要处理的类名
                if (name.indexOf("com.forgotten.fridatestapp") != -1){
                    console.log(name,handle);
                    // 利用反射 获取类中的所有方法
                    var TargetClass = Java.use(name);
                    // return Method Object List
                    var methodsList = TargetClass.class.getDeclaredMethods(); 
                    for (var i = 0; i < methodsList.length; i++){
                        // 打印其中方法的名字
                        console.log(methodsList[i].getName());
                        // 可以hook该类中的所有方法
                        hookMethodAllOverloads(name,methodsList[i].getName());
                    }
                }
            },
            
            onComplete: function(){
                console.log("enumerateLoadedClasses complete!!!")
            }
        })
    })
}

使用Java.enumerateLoadedClassesSync()API

function main() {
    Java.perform(function () {
        // return String[] class name
        var classList = Java.enumerateLoadedClassesSync();
        for (var i = 0; i < classList.length; i++) {
            var targetClass = classList[i];
            if (targetClass.indexOf("com.forgotten.fridatestapp") != -1) {
                console.log("hook the class: ", targetClass);
                var TargetClass = Java.use(targetClass);
                // 利用反射获取类中的所有方法
                var methodsList = TargetClass.class.getDeclaredMethods();
                for (var j = 0; j < methodsList.length; j++) {
                    console.log(methodsList[j].getName());
                    hookMethodAllOverloads(targetClass,methodsList[j].getName());
                }
            }
        }
    });
}

hook动态加载dex

function findClassLoader(className) {
    Java.perform(function () {
        // 枚举内存中的 类加载器
        Java.enumerateClassLoaders({
            onMatch: function (loader) {
                try {
                    // 如果找到的类加载器 能加载的类有[class_name]
                    if (loader.findClass(className)) {
                        console.log("Successfully found loader");
                        console.log(loader);
                        // 设置 java默认的classloader
                        Java.classFactory.loader = loader;
                    }
                } catch (error) {
                    console.log("find error:" + error);
                }
            },
            onComplete: function () {
                console.log("End");
            },
        });
        // 再 使用该类
        Java.use("[class_name]");
    });
}

或使用Java.enumerateClassLoadersSync()API

内存搜索接口

枚举类的所有接口
function findAllInterfaces(packageName) {
    Java.perform(function () {
        Java.enumerateLoadedClasses({
            onMatch: function (class_name) {
                // 对搜索范围进行限定
                if (class_name.indexOf(packageName) < 0) {
                    return;
                } else {
                    var hook_cls = Java.use(class_name);
                    var interfaces = hook_cls.class.getInterfaces();
                    if (interfaces.length > 0) {
                        console.log(class_name + ": ");
                        for (var i in interfaces) {
                            console.log("\t", interfaces[i].toString());
                        }
                    }
                }
            },
            onComplete: function () {
                console.log("end");
            },
        });
    });
}

function main() {
    var packageName = "com.forgotten.fridatestapp";
    findAllInterfaces(packageName);
}

寻找接口的实现类
function findInterface(packageName, interfaceName) {
    Java.perform(function () {
        Java.enumerateLoadedClasses({
            onMatch: function (class_name) {
                // 对搜索范围进行限定
                if (class_name.indexOf(packageName) < 0) {
                    return;
                } else {
                    var hook_cls = Java.use(class_name);
                    var interfaces = hook_cls.class.getInterfaces();
                    if (interfaces.length > 0) {
                        for (var i in interfaces) {
                            if (interfaces[i].toString().indexOf(interfaceName) >= 0) {
                                console.log("Found:", class_name);
                                break;
                            }
                        }
                    }
                }
            },
            onComplete: function () {
                console.log("end");
            },
        });
    });
}

function main() {
    var packageName = "com.forgotten.fridatestapp";
    var interfaceName = "android.view.View$OnClickListener";
    findInterface(packageName, interfaceName);
}

定位抽象类的实现类


function findAllSuperclasses(packageName) {
    Java.perform(function () {
        Java.enumerateLoadedClasses({
            onMatch: function (class_name) {
                // 对搜索范围进行限定
                if (class_name.indexOf(packageName) < 0) {
                    return;
                } else {
                    var hook_cls = Java.use(class_name);
                    var superClass = hook_cls.class.getSuperclass();
                    console.log(class_name,":")
                    while(superClass!=null){
                        console.log("\t",superClass.toString());
                        superClass = superClass.getSuperclass();
                    }
                }
            },
            onComplete: function () {
                console.log("end");
            },
        });
    });
}

function main() {
    var packageName = "com.forgotten.fridatestapp";
    findAllSuperclasses(packageName);
}

打印与参数构造

加载dex使用gson打印

Java.openClassFile("xxx.dex").load();

// 示例
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
const gson = Java.use('com.r0ysue.gson.Gson');
gson.$new().toJson( object );

查看类与类型强转

对于hook时,找不到相关属性(Frida不清楚类型的),可以使用Cast类型强转来指定类型。

// 通过反射查看当前实例类型(所属类)
.getClass().getName().toString()

// 子类强制转换父类
Java.cast()
var WaterHandle = Java.cast(JuiceHandle,Java.use("com.r0ysue.a0526printout.Water"))

/** 父类不能强制转换子类 **/
/**
 * 根据实例对象找到其类名
 */
function getObjClassName(obj) {
    if (!jclazz) {
        var jclazz = Java.use("java.lang.Class");
    }
    if (!jobj) {
        var jobj = Java.use("java.lang.Object");
    }
    return jclazz.getName.call(jobj.getClass.call(obj));
}

Array数组

hook array数组
// char[] [C
Java.use("java.util.Arrays").toString.overload('[C').implementation = function(charArray){}

// 同理byte[] 为[B
构造 array数组
// 构造char[]数组
Java.array('char', [ '烟','村','四','五','家']);

// 构造String[]数组
Java.array('java.lang.String', [ '烟','村','四','五','家']);
function main() {
    Java.perform(function () {
        var string1 = Java.use("java.lang.String").$new("123");
        var string2 = Java.use("java.lang.String").$new("");
        // var strarr = Java.array("java.lang.String", [string1, string2]);
        
        // 创建String数组,并添加值
        var Ref_arr = Java.use("java.lang.reflect.Array");
        var stringClass = Java.use("java.lang.String").class;
        var arr = Ref_arr.newInstance(stringClass, 2);
        Ref_arr.set(arr, 0, string1);
        Ref_arr.set(arr, 1, string2);

        var obj1 = Java.use("java.lang.String").$new("24717361");
        var obj2 = Java.use("java.lang.Integer").$new(19);
        var obj3 = Java.use("java.lang.String").$new("");
        // 创建Object数组
        var objarr = Java.array("java.lang.Object", [arr, obj1, obj2, obj3]);
        console.log(JSON.stringify(objarr))
        console.log(Java.use("java.util.Arrays").toString(objarr));
    });
}
打印 array数组
  1. 使用gson打印
  2. 使用java.util.Arrays.toString()打印
  3. 使用JSON.stringify()打印
function main(){
    Java.perform(function () {
        // Hook Arrays.toString方法 重载char[]
        Java.use("java.util.Arrays").toString.overload("[C").implementation = function () {
            // 打印参数
            console.log("arg = ", arguments[0]);
            // 可正确打印方法1
            // console.log("arg = ", this.toString(arguments[0]));
            console.log("arg = ", Java.use("java.util.Arrays").toString(arguments[0]));
            // 可正确打印方法2
            console.log("arg = ", JSON.stringify(arguments[0]));
            /* 手动构造一个Java array:参数一为类型,参数二为数组 */
            var arg = Java.array("char", ["上", "山", "打", "老", "虎"]);
            var result = this.toString(arg);
            console.log("[NEW]arg,result = ", arg, result);
            return result;
        };
    });
}
byte[]打印
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
console.log(ByteString.of(result).hex());

或者使用格式化打印十六进制数据

枚举

Java.perform(function () {
    Java.choose("com.forgotten.fridatestapp.construct.ConstructoredObject", {
        onMatch: function (instance) {
            // 找到后获取实例field map的值,尝试转为自定义Enum Signal类型
            var venum = Java.cast(instance.color.value, Java.use("com.forgotten.fridatestapp.construct.ConstructoredObject$Signal"));
            console.log("venum:", venum);
            // 调用Enum的方法
            console.log("venum.name():", venum.name());
        },
        onComplete: function () {
            console.log("venum: search completed");
        },
    });
});

HashMap类型

function main() {
    Java.perform(function () {
        // 在内存中查找ConstructoredObject类的实例
        Java.choose("com.forgotten.fridatestapp.construct.ConstructoredObject", {
            onMatch: function (instance) {
                // 找到后获取实例field map的值,尝试转为HashMap类型
                var vmap = Java.cast(instance.map.value, Java.use("java.util.HashMap"));
                console.log("vmap:", vmap);
                // 1.
                var key_iterator = vmap.keySet().iterator();
                while (key_iterator.hasNext()) {
                    var key = key_iterator.next().toString();
                    var value = vmap.get(key).toString();
                    console.log(key + ": " + value);
                }
                // 2.
                var entry_iterator = vmap.entrySet().iterator();
                while (entry_iterator.hasNext()) {
                    var entry = Java.cast(entry_iterator.next(),Java.use("java.util.HashMap$Node"));
                    console.log("entry", entry);
                    console.log(entry.getKey(), entry.getValue());
                }
                // 3.
                console.log("vmap.toString():", vmap.toString());
            },
            onComplete: function () {
                console.log("vmap: search completed");
            },
        });
    });
}

不可见函数名hook

Java.perform(
    function x() {
        // 定义目标类
        var targetClass = "com.example.hooktest.MainActivity";
        var hookCls = Java.use(targetClass);
        // 获得目标类的所有方法
        var methods = hookCls.class.getDeclaredMethods();
        // 遍历所有方法名
        for (var i in methods) {
            console.log(methods[i].toString());
            console.log(encodeURIComponent(methods[i].toString().replace(/^.*?\.([^\s\.\(\)]+)\(.*?$/, "$1")));
        }
        // 如果有等于不可见字符类的
        hookCls[decodeURIComponent("%D6%8F")]
            .implementation = function (x) {
                console.log("original call: fun(" + x + ")");
                var result = this[decodeURIComponent("%D6%8F")](900);
                return result;
            }
    }
)

Others

Frida构造多线程(新建类)

新建线程(即实现类时实现Runnable接口)

function newThread() {
    Java.perform(function () {
        var Runnable = Java.use("java.lang.Runnable");
        var Thread = Java.use("java.lang.Thread");
        var MyRunnable = Java.registerClass({
            name: "com.forgotten.thread",
            implements: [Runnable],
            fields: {
                // 成员变量名,与类型
                num: "int",
                str: "java.lang.String"
            },
            methods: {
                $init: [
                    {
                        returnType: "void",
                        argumentTypes: ["int","java.lang.String"],
                        implementation: function (num,str) {
                            this.num.value = num;
                            this.str.value = str;
                        },
                    },
                ],
                run: function () {
                    var i = 0;
                    for (i = 0; i < 10; i++) {
                        console.log(this.num.value,this.str.value);
                        this.num.value = this.num.value + 1;
                        Thread.sleep(5);
                    }
                },
            },
        });

        var myRunnable = MyRunnable.$new(10,"hello");
        var myThread = Thread.$new(myRunnable);
        myThread.start();
    });
}

获取context

function getContext(){
    Java.perform(function(){
        var currentApplication = Java.use("android.app.ActivityThread").currentApplication();
        console.log(currentApplication);
        var context = currentApplication.getApplicationContext();
        console.log(context);
        var packageName = context.getPackageName();
        console.log(packageName);
        console.log(currentApplication.getPackageName());
    })
}

强制主线程运行

Java.perform(function() {
  var Toast = Java.use('android.widget.Toast');
  var currentApplication = Java.use('android.app.ActivityThread').currentApplication(); 
  var context = currentApplication.getApplicationContext();

  Java.scheduleOnMainThread(function() {
    Toast.makeText(context, "Hello World", Toast.LENGTH_LONG.value).show();
  })
})

java层代码模板复用

打印调用栈

function printStack(name) {
    Java.perform(function () {
        var Exception = Java.use("java.lang.Exception");
        var ins = Exception.$new("Exception");
        var straces = ins.getStackTrace();
        if (straces != undefined && straces != null) {
            var strace = straces.toString();
            var replaceStr = strace.replace(/,/g, "\\n");
            console.log("=============================" + name + " Stack strat=======================");
            console.log(replaceStr);
            console.log("=============================" + name + " Stack end=======================\r\n");
            Exception.$dispose();
        }
    });
}
// sample 1
var throwable = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new());
console.log(throwable);
// sample 2
var exception = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
console.log(exception);

格式化打印十六进制数据

/**
 * 十六进制打印数组
 * @param {*} array 数组
 * @param {*} off 偏移
 * @param {*} len 长度
 */
function jhexdump(array, off, len) {
    off = off || 0;
    len = len || 0;
    var llen = len == 0 ? array.length : len;
    var ptr = Memory.alloc(llen);
    for (var i = 0; i < llen; ++i) Memory.writeS8(ptr.add(i), array[i]);
    //console.log(hexdump(ptr, { offset: off, length: len, header: false, ansi: false }));
    console.log(hexdump(ptr, { offset: off == 0 ? 0 : off, length: llen, header: false, ansi: false }));
}

事件

hook Toast提示 定位

hook findViewById 定位组件

hook StartActivity

Java.perform(function () {
    var Activity = Java.use("android.app.Activity");
    //console.log(Object.getOwnPropertyNames(Activity));
    Activity.startActivity.overload('android.content.Intent').implementation=function(p1){
        console.log("Hooking android.app.Activity.startActivity(p1) successfully,p1="+p1);
        //console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
        console.log(decodeURIComponent(p1.toUri(256)));
        this.startActivity(p1);
    }
    Activity.startActivity.overload('android.content.Intent', 'android.os.Bundle').implementation=function(p1,p2){
        console.log("Hooking android.app.Activity.startActivity(p1,p2) successfully,p1="+p1+",p2="+p2);
        //console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
        console.log(decodeURIComponent(p1.toUri(256)));
        this.startActivity(p1,p2);
    }
    Activity.startService.overload('android.content.Intent').implementation=function(p1){
        console.log("Hooking android.app.Activity.startService(p1) successfully,p1="+p1);
        //console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
        console.log(decodeURIComponent(p1.toUri(256)));
        this.startService(p1);
    }
})

hook onClick事件 定位

hook Keystore:客户端证书校验

Frida RPC(Remote procedure call)

  • Frida(rpc)开到公网:

    通过vps安装NPS来将公网的流量转发到手机相应端口

  • 利用flask开启网址服务

    利用python flask库,将主动调用函数映射为网址服务,只要访问即可调用app中方法

RPC主动调用

rpc.exports导出名不可以有大写字母或者下划线

function invoke() {
    Java.perform(function () {
        // 搜索HookedObject类实例
        Java.choose("com.forgotten.fridatestapp.HookedObject", {
            onMatch: function (instance) {
                console.log("found HookedObject:", instance);
                // 查找到实例后主动调用方法
                console.log("ho.getPasswd():", instance.getPasswd("123 ABC"));
            },
            onComplete: function () {
                console.log("HookedObject: search complete.");
            },
        });
    });
}
/* 测试函数 */
function test() {
    console.log("I'm Frida_rpc.js!");
}
/* 导出函数列表(可供py调用的) py函数映射和实际函数名 */
rpc.exports = {
    invokefunc: invoke,
    testfunc: test,
};

import time
import frida

## handler | script脚本信息交互函数
def my_message_handler(message,payload):
    print(message)
    print(payload)

# 通过Usb连接设备
# device = frida.get_usb_device()

# 通过ip:port 连接设备
device = frida.get_device_manager().add_remote_device("192.168.0.104:8888")
################ 通过spawn方式启动 ###########################
pid = device.spawn(["com.forgotten.fridatestapp"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
##### <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
# session = device.attach("com.forgotten.fridatestapp")
################ 通过attach现有进程方式启动 ###################
with open("./frida_rpc.js") as f:
    # 创建一个新脚本
    script = session.create_script(f.read())
# 加载信息交互handler函数
script.on("message",my_message_handler)
# 加载脚本
script.load()

command = ""
while True:
    command = input("Enter Command(y/t/n): ")
    if command=="y":
        script.exports.invokefunc()
    elif command=="t":
        script.exports.testfunc()
    elif command=="n":
        break

rpc动态修改

Java.perform(function () {
    Java.use("com.forgotten.fridatestapp.HookedObject").getPasswd.implementation = function () {
        // 需要发送给python的字符串:由函数的参数和结果拼接而成
        var string_to_send = arguments[0] + ":" + this.getPasswd(arguments[0]);
        var string_to_recv;
        // 发送到python程序
        send(string_to_send);
        // 同时调用.wait()来 阻塞运行,等待接收消息
        recv(function (received_json_objection) {
            // 接收来的json字符串
            console.log("recv in js:",JSON.stringify(received_json_objection))
            // 打印json的`my_data`,json串来自python
            string_to_recv = received_json_objection.my_data;
            console.log("string_to_recv:", string_to_recv);
        }).wait();
        // 将接收到的字符串当作被hook函数的结果返回回去
        var result = Java.use("java.lang.String").$new(string_to_recv);
        return result;
    };
});

import time
import frida


## handler | script脚本信息交互函数
def my_message_handler(message, payload):
    print(message)  # 打印得到的信息
    print(payload)  # 输出的为`none`?
    # 如果`type`字段为"send" 则是js发来的消息
    if message["type"] == "send":
        # 打印json的`payload`内容(js发送过来的内容)
        print(message["payload"])
        # 向script发送消息,格式为字典
        script.post({"my_data": "Hello"})


# 通过Usb连接设备
# device = frida.get_usb_device()

# 通过ip:port 连接设备
device = frida.get_device_manager().add_remote_device("192.168.0.104:8888")
################ 通过spawn方式启动 ###########################
pid = device.spawn(["com.forgotten.fridatestapp"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
##### <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
# session = device.attach("com.forgotten.fridatestapp")
################ 通过attach现有进程方式启动 ###################
with open("./frida_rpc.js") as f:
    # 创建一个新脚本
    script = session.create_script(f.read())
# 加载信息交互handler函数
script.on("message", my_message_handler)
# 加载脚本
script.load()

command = ""
while True:
    command = input("Enter `n` for leave: ")
    if command == "n":
        break

More

frida-python/examples at main · frida/frida-python (github.com)

Frida native层应用

Frida实现的Env:frida-java-bridge/env.js at main · frida/frida-java-bridge (github.com)

判断Thumb OR arm

判断Thumb OR arm模式:

  • IDA查看指令机器码长度,都为4字节为arm指令
  • push指令为thumb指令特有

hook用户函数

attach静态注册(或导出)函数

function main0() {
    /* hook 可导出的native函数 */
    Java.perform(function () {
        // 寻找模块so的地址
        var lib_fridatestapp_addr = Module.findBaseAddress("libfridatestapp.so");
        console.log("native_lib_addr -> ", lib_fridatestapp_addr);
        // 寻找导出函数的地址
        var staticString_addr = Module.findExportByName("libfridatestapp.so", "Java_com_forgotten_fridatestapp_MainActivity_staticString");
        console.log("staticString() addr -> ", staticString_addr);
        // 对函数进行attach
        Interceptor.attach(staticString_addr, {
            // 函数进入时,参数为函数的参数
            onEnter: function (args) {
                /* 打印native函数调用栈,有Backtracer.ACCURATE和Backtracer.FUZZY两种模式切换 */
                console.log("CCCryptorCreate called from:\n" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n") + "\n");

                // 打印三个参数地址
                console.log("Interceptor.attach staticString() args:", args[0], args[1], args[2]);
                // 将 参数三传进去的jstring字符串,转换为char*再用readCString()得到JavaScript字符串来输出
                console.log("jstring is", Java.vm.getEnv().getStringUtfChars(args[2], null).readCString());

                // 可以对参数进行修改
                var new_arg2 = Java.vm.getEnv().newStringUtf("new arg2 from Frida");
                args[2] = new_arg2;
            },
            // 函数执行完的时候,参数为函数的返回值
            onLeave: function (reval) {
                console.log("Interceptor.attach staticString() retval", reval);
                console.log("Interceptor.attach staticString() retval", Java.vm.getEnv().getStringUtfChars(reval, null).readCString());

                // 对函数的返回值进行替换
                var new_reval = Java.vm.getEnv().newStringUtf("HaHa Frida!!!");
                reval.replace(new_reval);
            },
        });
    });
}

主动调用静态注册(或导出)函数

注册native函数:new NativeFunction(address, returnType, argTypes[, abi])

function main1() {
    /* 主动调用 可导出的native函数 */
    Java.perform(function invoke_justAdd_func() {
        // 寻找模块so的地址
        var lib_fridatestapp_addr = Module.findBaseAddress("libfridatestapp.so");
        console.log("native_lib_addr -> ", lib_fridatestapp_addr);
        // 寻找导出函数的地址
        var justAdd_addr = Module.findExportByName("libfridatestapp.so", "_Z7justAddii");
        console.log("justAdd() addr -> ", justAdd_addr);
        // 新建一个Native函数,参数分别为 已存在函数地址,函数返回值类型,函数参数列表
        var justAdd_func = new NativeFunction(justAdd_addr, "int", ["int", "int"]);
        // 执行函数,获得函数返回值
        var justAdd_result = justAdd_func(10, 2);
        console.log("invoke justAdd(10,2) result-> ", justAdd_result);
    });

    Java.perform(function invoke_nativeString_func() {
        /* 大部分代码同 hook函数中的 */
        // 寻找模块so的地址
        var lib_fridatestapp_addr = Module.findBaseAddress("libfridatestapp.so");
        console.log("native_lib_addr -> ", lib_fridatestapp_addr);
        // 寻找导出函数的地址
        var staticString_addr = Module.findExportByName("libfridatestapp.so", "Java_com_forgotten_fridatestapp_MainActivity_staticString");
        console.log("staticString() addr -> ", staticString_addr);

        /* 声明该native函数,返回值和参数env、jobject等都是"pointer" */
        var nativeString_func = new NativeFunction(staticString_addr, "pointer", ["pointer", "pointer", "pointer"]);

        // 对函数进行attach
        Interceptor.attach(staticString_addr, {
            // 函数进入时,参数为函数的参数
            onEnter: function (args) {
                // 打印三个参数地址
                console.log("Interceptor.attach staticString() args:", args[0], args[1], args[2]);
                // 将 参数三传进去的jstring字符串,转换为char*再用readCString()得到JavaScript字符串来输出
                console.log("jstring is", Java.vm.getEnv().getStringUtfChars(args[2], null).readCString());

                /* 主动调用方法,打印函数结果 */
                console.log("==> invoke stringfunc(): ", Java.vm.getEnv().getStringUtfChars(nativeString_func(args[0], args[1], args[2]), null).readCString());

                // 可以对参数进行修改
                var new_arg2 = Java.vm.getEnv().newStringUtf("new arg2 from Frida");
                args[2] = new_arg2;
            },
            // 函数执行完的时候,参数为函数的返回值
            onLeave: function (reval) {
                console.log("Interceptor.attach staticString() retval", reval);
                console.log("Interceptor.attach staticString() retval", Java.vm.getEnv().getStringUtfChars(reval, null).readCString());

                // 对函数的返回值进行替换
                var new_reval = Java.vm.getEnv().newStringUtf("HaHa Frida!!!");
                reval.replace(new_reval);
            },
        });
    });
}

replace静态注册(或导出)函数

function main2() {
    /* 替换 justAdd函数 */
    Java.perform(function replace_func() {
        // 寻找模块so的地址
        var lib_fridatestapp_addr = Module.findBaseAddress("libfridatestapp.so");
        console.log("native_lib_addr -> ", lib_fridatestapp_addr);
        // 寻找导出函数的地址
        var justAdd_addr = Module.findExportByName("libfridatestapp.so", "_Z7justAddii");
        console.log("justAdd() addr -> ", justAdd_addr);
        // 对原native函数进行替换,参数1为替换的地址,参数2为一个NativeCallback
        Interceptor.replace(
            justAdd_addr,
            new NativeCallback(
                // 参数分别为,替换执行的函数,返回值类型,参数类型列表
                function (a, b) {
                    console.log("justAdd args: ", a, b);
                    var result = a * (b + 5);
                    console.log("new Func Result: ", result);
                    return result;
                },
                "int",
                ["int", "int"]
            )
        );
    });
}

通过地址偏移操作未导出函数

通过ida找到函数偏移

function main3() {
    /* 靠地址偏移hook未导出函数  */
    Java.perform(function () {
        // 寻找模块so的地址
        var lib_fridatestapp_addr = Module.findBaseAddress("libfridatestapp.so");
        console.log("native_lib_addr -> ", lib_fridatestapp_addr);
        // 通过函数偏移+模块的地址,得到函数的地址
        var dynamicString_addr = lib_fridatestapp_addr.add(0xa48);
        console.log("dynamicString() addr -> ", dynamicString_addr);
        // 对函数进行attach
        Interceptor.attach(dynamicString_addr, {
            // 函数进入时,参数为函数的参数
            onEnter: function (args) {
                /* 打印native函数调用栈,有Backtracer.ACCURATE和Backtracer.FUZZY两种模式切换 */
                // console.log("CCCryptorCreate called from:\n" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n") + "\n");

                // 打印三个参数地址
                console.log("Interceptor.attach dynamicString() args:", args[0], args[1], args[2]);
                // 将 参数三传进去的jstring字符串,转换为char*再用readCString()得到JavaScript字符串来输出
                // console.log("jstring is", Java.vm.getEnv().getStringUtfChars(args[2], null).readCString());

                // 可以对参数进行修改
                var new_arg2 = Java.vm.getEnv().newStringUtf("new arg2 from Frida");
                args[2] = new_arg2;
            },
            // 函数执行完的时候,参数为函数的返回值
            onLeave: function (reval) {
                console.log("Interceptor.attach dynamicString() retval", reval);
                console.log("Interceptor.attach dynamicString() retval", Java.vm.getEnv().getStringUtfChars(reval, null).readCString());

                // 对函数的返回值进行替换
                var new_reval = Java.vm.getEnv().newStringUtf("HaHa Frida!!!");
                // reval.replace(new_reval);
            },
        });
    });
}

模块相关操作

枚举出所有模块的所有导出符号

/* 枚举出所有模块的所有导出符号 */
function EnumerateAllExports() {
    var modules = Process.enumerateModules();
    //print all modules
    //console.log("Process.enumerateModules->",JSON.stringify(modules));
    for (var i = 0; i < modules.length; i++) {
        var module = modules[i];
        var module_name = modules[i].name;
        var exports = module.enumerateExports();
        console.log("module.enumerateeExports", JSON.stringify(exports));
    }
}

遍历某模块符号/导出/导入

function look_module(module_name){
    // 根据模块名称寻找地址;根据地址找到模块返回Module对象
    var native_lib_addr = Process.findModuleByAddress(Module.findBaseAddress(module_name));
    console.log("native_lib_addr => ",JSON.stringify(native_lib_addr));
    // 遍历模块的所有Symbols
    console.log("enumerateImports=>",JSON.stringify(native_lib_addr.enumerateSymbols()));

}
look_module("linker64");

JNI框架层的利用

JNI框架hook:

/* hook jni函数GetStringUTFChars */
function hook_getStringUTFChars_func() {
    var GetStringUTFChars_addr = null;
    // 该函数在这个so里面,遍历里面的所有符号
    var symbools = Process.findModuleByName("libart.so").enumerateSymbols();
    //console.log(JSON.stringify(symbool));
    for (var i = 0; i < symbools.length; i++) {
        // 取到符号的name
        var symbol = symbools[i].name;
        // 过滤一下,因为还有一个checkjni类中有该函数
        if (symbol.indexOf("CheckJNI") == -1 && symbol.indexOf("JNI") >= 0) {
            if (symbol.indexOf("GetStringUTFChars") >= 0) {
                console.log("finally found GetStringUTFChars name:", symbol);
                // 保存该函数的地址
                GetStringUTFChars_addr = symbools[i].address;
                console.log("finally found GetStringUTFChars address :", GetStringUTFChars_addr);
            }
        }
    }
    /* 开始附加该函数 */
    Interceptor.attach(GetStringUTFChars_addr, {
        onEnter: function (args) {
            console.log("art::JNI::GetStringUTFChars(_JNIEnv*,_jstring*,unsigned char*)->", args[0], Java.vm.getEnv().getStringUtfChars(args[1], null).readCString(), args[2]);
            // 打印栈回溯
            // console.log("CCCryptoCreate called from:\n" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n") + "\n");
        },
        onLeave: function (retval) {
            // 打印返回值,为c字符串
            console.log("retval is->", retval.readCString());
        },
    });
}

JNI框架replace:

/* 对NewStringUTF函数进行replace操作 */
function replace_NewStringUTF_func() {
    /* 同上 */
    var NewStringUTF_addr = null;
    // 该函数在这个so里面,遍历里面的所有符号
    var symbools = Process.findModuleByName("libart.so").enumerateSymbols();
    //console.log(JSON.stringify(symbool));
    for (var i = 0; i < symbools.length; i++) {
        // 取到符号的name
        var symbol = symbools[i].name;
        // 过滤一下,因为还有一个checkjni类中有该函数
        if (symbol.indexOf("CheckJNI") == -1 && symbol.indexOf("JNI") >= 0) {
            if (symbol.indexOf("NewStringUTF") >= 0) {
                console.log("finally found NewStringUTF_name:", symbol);
                // 保存该函数的地址
                NewStringUTF_addr = symbools[i].address;
                console.log("finally found NewStringUTF_address :", NewStringUTF_addr);
            }
        }
    }

    // new一个NewStringUTF的NativeFunction
    /* static jstring NewStringUTF(JNIEnv* env, const char* utf) */
    var NewStringUTF = new NativeFunction(NewStringUTF_addr, "pointer", ["pointer", "pointer"]);
    // 然后执行替换
    Interceptor.replace(
        NewStringUTF_addr,
        new NativeCallback(
            function (arg1, arg2) {
                // 打印原本的参数
                console.log("NewStringUTF arg1,arg2->", arg1, arg2.readCString());
                // new一个char*字符串
                var newARG2 = Memory.allocUtf8String("newPARG2");
                /* 将参数替换,然后执行原函数并返回结果
        var result=NewStringUTF(arg1,newARG2); // 不能随意修改,会导致崩溃*/
                var result = NewStringUTF(arg1, arg2);
                return result;
            },
            "pointer",
            ["pointer", "pointer"]
        )
    );
}

native层代码模板复用

Frida写文件

/**
 * 写文件
 * @param {*} path 写文件的路径
 * @param {*} contents 写文件的内容
 */
function writeSomething(path, contents) {
    var fopen_addr = Module.findExportByName("libc.so", "fopen");
    var fputs_addr = Module.findExportByName("libc.so", "fputs");
    var fclose_addr = Module.findExportByName("libc.so", "fclose");

    //console.log("fopen=>",fopen_addr,"  fputs=>",fputs_addr,"  fclose=>",fclose_addr);

    var fopen = new NativeFunction(fopen_addr, "pointer", ["pointer", "pointer"]);
    var fputs = new NativeFunction(fputs_addr, "int", ["pointer", "pointer"]);
    var fclose = new NativeFunction(fclose_addr, "int", ["pointer"]);

    //console.log(path,contents)

    var fileName = Memory.allocUtf8String(path);
    var mode = Memory.allocUtf8String("a+");

    var fp = fopen(fileName, mode);

    var contentHello = Memory.allocUtf8String(contents);
    var ret = fputs(contentHello, fp);

    fclose(fp);
}

遍历module的导出表与符号表

/**
 * 遍历导出表与符号表 Example
 */

/**
 * 写文件
 * @param {*} path 写文件的路径
 * @param {*} contents 写文件的内容
 */
function writeSomething(path, contents) {
    var fopen_addr = Module.findExportByName("libc.so", "fopen");
    var fputs_addr = Module.findExportByName("libc.so", "fputs");
    var fclose_addr = Module.findExportByName("libc.so", "fclose");

    //console.log("fopen=>",fopen_addr,"  fputs=>",fputs_addr,"  fclose=>",fclose_addr);

    var fopen = new NativeFunction(fopen_addr, "pointer", ["pointer", "pointer"]);
    var fputs = new NativeFunction(fputs_addr, "int", ["pointer", "pointer"]);
    var fclose = new NativeFunction(fclose_addr, "int", ["pointer"]);

    //console.log(path,contents)

    var fileName = Memory.allocUtf8String(path);
    var mode = Memory.allocUtf8String("a+");

    var fp = fopen(fileName, mode);

    var contentHello = Memory.allocUtf8String(contents);
    var ret = fputs(contentHello, fp);

    fclose(fp);
}

/** 对指定函数进行attachHOOK **/
function attach(name, address) {
    console.log("attaching ", name);
    Interceptor.attach(address, {
        onEnter: function (args) {
            console.log("Entering => ", name);
        },
        onLeave: function (retval) {
            //console.log("retval is => ",retval)
        },
    });
}

var app_packagename = "com.forgotten.learntest";

/* 遍历moudles的exports */
function traceNativeExport() {
    var modules = Process.enumerateModules();
    for (var i = 0; i < modules.length; i++) {
        var module = modules[i];

        if (module.name.indexOf("libssl.so") < 0) {
            continue;
        }
        var path = "/data/data/" + app_packagename + "/cache/" + module.name + "_exports.txt";
        var exports = module.enumerateExports();
        for (var j = 0; j < exports.length; j++) {
            console.log("module name is =>", module.name, " symbol name is =>", exports[j].name);

            writeSomething(path, "type: " + exports[j].type + " function name :" + exports[j].name + " address : " + exports[j].address + " offset => 0x" + exports[j].address.sub(modules[i].base) + "\n");
            if (exports[j].name.indexOf("SSL_write") >= 0) {
                attach(exports[j].name, exports[j].address);
            }
        }
    }
}

/* 遍历moudles的symbols */
function traceNativeSymbol() {
    var modules = Process.enumerateModules();
    for (var i = 0; i < modules.length; i++) {
        var module = modules[i];
        // console.log(JSON.stringify(module));
        /*可以对指定module进行过滤*/

        if (module.name.indexOf("linker64") < 0) {
            continue;
        }
        var path = "/data/data/" + app_packagename + "/cache/" + module.name + "_symbols.txt";
        var exports = module.enumerateSymbols();
        // console.log(JSON.stringify(exports))
        for (var j = 0; j < exports.length; j++) {
            if (exports[j] == null) {
                continue;
            }
            console.log("module name is =>", module.name, " symbol name is =>", exports[j].name);

            writeSomething(path, "type: " + exports[j].type + " function name :" + exports[j].name + " address : " + exports[j].address + " offset => 0x" + exports[j].address.sub(modules[i].base) + "\n");
        }
    }
}

function main() {
    console.log("Entering main");
    traceNativeExport();
    traceNativeSymbol();
}
setImmediate(main);

读写 std::string

function readStdString(str) {
    var isTiny = (str.readU8 & 1) === 0;
    if (isTiny) {
        return str.add(1).readUtf8String();
    }
    return str
        .add(2 * Process.pointerSize)
        .readPointer()
        .readUtf8String();
}

function writeStdString(str, content) {
    var isTiny = (str.readU8() & 1) === 0;
    if (isTiny) {
        str.add(1).writeUtf8String(content);
    } else {
        str.add(2 * Process.pointerSize)
            .readPointer()
            .writeUtf8String(content);
    }
}

相关脚本使用

Unpackers

FRIDA-DEXDump

hluwa/frida-dexdump: A frida tool to dump dex in memory to support security engineers analyzing malware. (github.com)

frida_fart

hanbinglengyue/FART: ART环境下自动化脱壳方案 (github.com)

frida_dump

lasting-yang/frida_dump: frida dump dex, frida dump so (github.com)

NetWork

r0capture

r0ysue/r0capture: 安卓应用层抓包通杀脚本 (github.com)

OkHttpLogger-Frida

siyujie/OkHttpLogger-Frida: Frida 实现拦截okhttp的脚本 (github.com)

frida_ssl_logger

BigFaceCat2017/frida_ssl_logger: ssl_logger based on frida (github.com)

okhttp-sslunpinning

bxl0608/okhttp-sslunpinning (github.com)

Trace

jnitrace

chame1eon/jnitrace: A Frida based tool that traces usage of the JNI API in Android apps. (github.com)

frida_hook_libart

lasting-yang/frida_hook_libart: Frida hook some jni functions (github.com)

frida-trace

frida-trace | Frida • A world-class dynamic instrumentation framework

Memory

Wallbreaker

hluwa/Wallbreaker: Break Java Reverse Engineering form Memory World! (github.com)

Or

  • Frida Android hook | Sakuraのblog (eternalsakura13.com)

  • 分类: frida | 凡墙总是门 (kevinspider.github.io)

  • 分类: frida | 凡墙总是门 (kevinspider.github.io)

你可能感兴趣的:(Android逆向,安全,安卓逆向,网络安全)