一、环境

win10、python38、frida库、夜神模拟器
(1)安装frida环境
(a)frida

python -m pip install frida    //运行python38版本
//python -m module_name相当于python /path/to/module.py(当我们知道一个模块的名字,但不知道它的路径时,我们可以通过 -m 参数)

假如以上方法出错,建议本地下载frida包安装 https://pypi.org/project/frida/#files(选择frida-14.2.8-py3.8-win-amd64.egg),然后执行python -m easy_install xxx.egg,其中,easy_install需要安装setuptools库,且版本有要求,为pip install "setuptools==45.2.0"

(b) frida-tools

python -m pip install frida-tools 在win10终端执行frida-ps -U,显示进程表示成功

android frida hook脚本 frida hook原理_python


(2)搭建frida-sever端环境(这里以夜神模拟器为例)

https://github.com/frida/frida/releases中下载frida-server-14.2.8-android-x86.xz(通过adb shell getprop ro.product.cpu.abi查看相应版本),解压后将文件frida-server-14.2.8-android-x86放在夜神模拟器的/data/local/tmp目录,并且赋予权限,基于root执行./frida-server-14.2.8-android-x86

二、so源码实例
#include <string.h>
#include <jni.h>
#include"test.h"

jstring JNICALL Java_com_example_mi_demoso_JNITest_getStringFromJNI(JNIEnv* env, jobject jo)
{
    char str[] = "x HelloWorld from JNI12345!";
    int c = test_add(97,1);
    str[0] = (char)c;
    return (*env)->NewStringUTF(env, str);
}

int test_add(int a,int b){
    return a+b;
}

这里将对test_add函数进行hook

三、寻找要hook函数的偏移地址

将要hook的so(libjnitest.so)拖进ida

android frida hook脚本 frida hook原理_#include_02


上图可以看到:在导出函数窗口可以直接看到函数"test_add"的偏移地址和函数名(这里可以通过函数名或者地址进行hook),非导出函数只能通过地址hook;这里我们用地址hook的方法(图中表明hook的函数偏移为0x00000680,函数地址 = so基地址 + 函数偏移, so基地址在/proc/<pid>/maps中可查看)

android frida hook脚本 frida hook原理_Java_03

四、编写frida脚本
import frida
import sys

jscode = """
Java.perform(function(){
    var str_name_so = "libjnitest.so";    //需要hook的so名
    var n_addr_func_offset = 0x00000680;         //需要hook的函数的偏移
    var n_addr_so = Module.findBaseAddress(str_name_so); //加载到内存后 函数地址 = so地址 + 函数偏移
    var n_addr_func = parseInt(n_addr_so, 16) + n_addr_func_offset;
    var ptr_func = new NativePointer(n_addr_func);
    //var ptr_func = Module.findExportByName("libjnitest.so","test_add") //对函数名hook

    Interceptor.attach(ptr_func,{ 
        //onEnter: 进入该函数前要执行的代码,其中args是传入的参数,一般so层函数第一个参数都是JniEnv,第二个参数是jclass,从第三个参数开始是我们java层传入的参数
        onEnter: function(args) {
            send("Hook start");
            send("args[2]=" + args[2]); //第一个传入的参数
            send("args[3]=" + args[3]); //第二个参数
        },
        onLeave: function(retval){ //onLeave: 该函数执行结束要执行的代码,其中retval参数即是返回值
            send("return:"+retval); //返回值
            retval.replace(100); //替换返回值为100
        }
    });
});
"""
def printMessage(message,data):
    if message['type'] == 'send':
        print('[*] {0}'.format(message['payload']))
    else:
        print(message)

process = frida.get_remote_device().attach('com.example.testso') #进程名
script = process.create_script(jscode)
script.on('message',printMessage)
script.load()
sys.stdin.read()

注意:
返回字符串时,在retval.replace(100); //替换返回值为100之后需要:

var env = Java.vm.getEnv(); //获取env对象,即第一个参数
            var jstrings = env.newStringUtf("xxxx"); //返回的是字符串指针,构造一个newStringUtf对象用来代替这个指针
            retval.replace(jstrings); //替换返回值
五、效果验证

启动frida-server后进行,启动端口转发
adb forward tcp:27043 tcp:27043adb forward tcp:27042 tcp:27042 然后执行python脚本(frida脚本),接着启动应用:

android frida hook脚本 frida hook原理_python_04


符合retval.replace(100); //替换返回值为100预期,即小写x处显示为d,即100.