首页
社区
课程
招聘
[原创]frida 学习记录
发表于: 2019-6-29 14:46 27200

[原创]frida 学习记录

2019-6-29 14:46
27200
上次记录了下xposed hook java层的学习,这次记录下frida
例子是在上次写的apk上稍加改动,添加几个简单的native方法;
  • MainActivity
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button bt=(Button)findViewById(R.id.bt1);

        bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                HookGoal h=new HookGoal(0);
                h.editnum(1);
                Log.i("jni str",h.strtest("abcdefg"));
                DiyClass[] array={new DiyClass(1),new DiyClass(2)};
                Log.i("jni array","jni调用前 参数数组 成员0: "+array[0].getData()+" 成员1: "+array[1].getData());
                DiyClass[] myarray=h.arraytest(array);
                Log.i("jni array","jni调用后 返回新数组 成员0: "+myarray[0].getData()+" 成员1: "+myarray[1].getData());
                h.show();
                TextView t=(TextView)findViewById(R.id.sample_text);
                //t.setText("ooo");
                t.setText(h.sayhello());
                //Log.i("jni ","");
                Toast.makeText(MainActivity.this,"HOOKGOAL",Toast.LENGTH_LONG).show();
            }
        });
    }
}

  • 类文件
abstract class person {
    public int age = 0;

    public void eat(String food) {
    }
}

public class HookGoal {
    static {
        System.loadLibrary("native-lib");
    }
    private static String TAG = "HookGoal:";
    private  int hookGoalNumber;

    public HookGoal(int number) {
        hookGoalNumber = number;
        Log.i(TAG, "HookGoal hookGoalNumber:" + hookGoalNumber);
    }

    public void func0() {
        Log.i(TAG, "welcome");
    }

    private void func1() {
        new person() {
            @Override
            public void eat(String food) {
                Log.i(TAG, "eat " + food);
            }
        }.eat("apple");
    }

    private static void func2(String s) {
        Log.i(TAG, "func2 " + s);
    }

    private void func3(DiyClass[] arry) {
        for (int i = 0; i < arry.length; i++)
            Log.i(TAG, "DiyClass[" + i + "].getData:" + arry[i].getData());
    }

    private class InnerClass {
        private int innerNumber;

        public InnerClass(String s) {
            innerNumber = 0;
            Log.i(TAG, "InnerClass 构造函数 " + s);
            Log.i(TAG, "InnerClass innerNumber:" + innerNumber);
        }

        private void innerFunc(String s) {
            Log.i(TAG, "InnerClass innerFunc " + s);
        }
    }
    public native String sayhello();
    public native String strtest(String s);
    public native DiyClass[] arraytest(DiyClass[] array);
    public native void editnum(int n);
    public void show() {
        Log.i(TAG,"hookGoalNumber:"+hookGoalNumber);
        func1();
        func2("私有静态方法");
        DiyClass[] arry = {new DiyClass(0), new DiyClass(0), new DiyClass(0)};
        func3(arry);
        InnerClass inner = new InnerClass("私有内部类");
        inner.innerFunc("内部类方法调用");
    }

}


public class DiyClass{
    private int data;
    public DiyClass(int data){
        this.data=data;
    }
    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }
}

  • native方法,动态注册
int add(int a,int b){
    return a+b;
}

static jstring say(JNIEnv *env, jobject) {
    LOGI("native 函数say()返回字符串my jni hhh");
    string hello = "my jni hhh";
    return env->NewStringUTF(hello.c_str());
}
static jstring mystr(JNIEnv *env, jobject,jstring s) {
    char buf[256];
    env->GetStringUTFRegion(s,0,env->GetStringLength(s),buf);
    LOGI("GetStringUTFRegion 截取jstring保存到本地buf中:%s",buf);

    const char* ptr_c=env->GetStringUTFChars(s,NULL);
    LOGI("GetStringUTFChars 返回const char* 类型:%s",ptr_c);
    env->ReleaseStringUTFChars(s,ptr_c);

    const jchar *wc=env->GetStringChars(s, NULL);
    LOGI("GetStringChars 返回const jchar* 类型");
    env->ReleaseStringChars(s,wc);

    jstring js=env->NewStringUTF(buf);
/*  jstring NewStringUTF(const char* bytes)   返回jstring类型
    访问java.lang.String对应的JNI类型jstring时,不能像访问基本数据类型一样直接使用,因为它在Java是一个引用类型,所以在本地代码中只能通过GetStringUTFChars这样的JNI函数来访问字符串的内容*/
    LOGI("NewStringUTF(buf) 返回jstring类型:%s",env->GetStringUTFChars(js,NULL));
    return env->NewString(wc,env->GetStringLength(s));
}
static void edit(JNIEnv *env, jobject obj,jint n) {
    jint x=add(n,n);
    LOGI("edit()内调用了add(),原参数:%d",n);
    jclass clazz=env->GetObjectClass(obj);
    jfieldID numberid=env->GetFieldID(clazz,"hookGoalNumber","I");
    env->SetIntField(obj,numberid,x);
    LOGI("edit()内设置hookGoalNumber字段为:%d",x);

}
static jobjectArray myarray(JNIEnv *env, jobject obj,jobjectArray array){
    jclass diyclass=env->FindClass("com/example/goal/DiyClass");
    jmethodID initid=env->GetMethodID(diyclass,"<init>","(I)V");
    jmethodID getdataid=env->GetMethodID(diyclass,"getData","()I");
    jobject a=env->GetObjectArrayElement(array,0);
    jobject a1=env->GetObjectArrayElement(array,1);
    jint d=env->CallIntMethod(a,getdataid);
    jint d1=env->CallIntMethod(a1,getdataid);
    jobject diy=env->NewObject(diyclass,initid,d);
    jobject diy2=env->NewObject(diyclass,initid,d1);
    jobjectArray myarray=env->NewObjectArray(2,diyclass,0);
    env->SetObjectArrayElement(myarray,0,diy2);
    env->SetObjectArrayElement(myarray,1,diy);
    LOGI("myarray()使用array参数,返回交换元素位置的新数组");
    return myarray;
//    return env->NewObjectArray(2,diyclass,diy2);

}

static const char *className = "com/example/goal/HookGoal";

static JNINativeMethod gJni_Methods_table[] = {
        {"editnum", "(I)V", (void*)edit},
        {"sayhello", "()Ljava/lang/String;",(void*)say},
        {"strtest", "(Ljava/lang/String;)Ljava/lang/String;",(void*)mystr},
        {"arraytest","([Lcom/example/goal/DiyClass;)[Lcom/example/goal/DiyClass;",(void*)myarray}

};

static int jniRegisterNativeMethods(JNIEnv* env, const char* className,
                                    const JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;

    clazz = (env)->FindClass( className);
    if (clazz == NULL) {
        return -1;
    }

    int result = 0;
    if ((env)->RegisterNatives(clazz, gJni_Methods_table, numMethods) < 0) {
        result = -1;
    }

    (env)->DeleteLocalRef(clazz);
    return result;
}

jint JNI_OnLoad(JavaVM* vm, void* reserved){
    JNIEnv* env = NULL;
    jint result = -1;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return result;
    }
    jniRegisterNativeMethods(env, className, gJni_Methods_table, sizeof(gJni_Methods_table) / sizeof(JNINativeMethod));
    return JNI_VERSION_1_4;
}

开始frida hook
  • 首先是java层hook
setImmediate(function () {
    Java.perform(function () {
        console.log("start");
        //java层hook
        var hookgoal = Java.use("com.example.goal.HookGoal");
        var clazz = Java.use("java.lang.Class");
        var obj = Java.use("java.lang.Object");
        var Exception = Java.use("java.lang.Exception");
        var str = Java.use("java.lang.String");
        //hook 构造方法
        hookgoal.$init.overload("int").implementation = function (number) {
            send("HookGoal构造函数的参数number:" + number);
            send("HookGoal构造函数的参数修改为666");
            return this.$init(666);
        };

        //hook 静态变量TAG
        var reflectField = Java.cast(hookgoal.class, clazz).getDeclaredField("TAG");
        reflectField.setAccessible(true);
        reflectField.set("java.lang.String", "frida hooking");
        send("修改HookGoal的静态变量TAG为:frida hooking");


        //实例化对象way1
        var newhg = hookgoal.$new(0);
        send("new HookGoal instance newhg: " + newhg);
        // 实例化对象way2
        var newhg1 = hookgoal.$new.overload("int").call(hookgoal, 0);
        send("new HookGoal instance newhg1: " + newhg1);

        //hook匿名内部类,修改参数
        var nminner = Java.use("com.example.goal.HookGoal$1");
        nminner.eat.overload("java.lang.String").implementation = function (s) {
            var arg = arguments[0];
            send("eat参数获取 way1:" + arg);
            send("eat参数获取 way2:" + s);
            //修改参数
            return this.eat("is hooking");
        };
        var diy = Java.use("com.example.goal.DiyClass");
        hookgoal.func2.implementation = function (s) {
            //func2为静态方法
            var arg = arguments[0];
            send("func2()参数获取:" + s);
            //调用成员方法func0()在静态方法内只能通过创建的实例访问
            newhg.func0();
            send("func2()内调用func0()  通过创建实例newhg调用");
            newhg1.func0();
            send("func2()内调用func0()  通过创建实例newhg1调用");

            //修改实例的hookGoalNumber值,前面hook构造函数时已经将值改为666
            //修改字段值 通过反射得到字段,
            //var num1 = Java.cast(newhg1.getClass(), clazz).getDeclaredField("hookGoalNumber");
            var num1 = Java.cast(hookgoal.class, clazz).getDeclaredField("hookGoalNumber");
            num1.setAccessible(true);
            send("实例newhg1的hookGoalNumber:" + num1.get(newhg1));
            num1.setInt(newhg1, 777);
            send("修改实例newhg1的hookGoalNumber:" + num1.get(newhg1));

            send("实例newhg的hookGoalNumber:" + num1.get(newhg));

            // 反射调用方法
            var func = hookgoal.class.getDeclaredMethod("func0", null);
            send("func0:" + func);
            //var funcs = hookgoal.class.getDeclaredMethods();
            //for(var i=0;i<funcs.length;i++)
            //    send(""+i+" "+funcs[i]);

            //invoke(instance,args)调用成员方法
            func.invoke(newhg1, null);
            send("func2()内调用func0()  way2 通过反射调用");

            //调用DiyClass内的getData()
            var d = diy.$new(666);
            var x = d.getData();
            send("func2()内调用DiyClass下的getData() 通过创建实例d调用 返回:" + x);
            //修改func2的参数
            return this.func2("is hooking");
        };

        //修改func3参数
        hookgoal.func3.implementation = function (array) {
            //在成员方法func3内调用func0()
            this.func0();
            send("func3()内调用func0()  way2 成员方法中直接调用其他成员方法");
            //修改数组参数
            send("func3参数:" + array);
            var a = Java.array("com.example.goal.DiyClass", [diy.$new(111), diy.$new(222), diy.$new(333)]);
            send("func3参数修改:" + a);
            return this.func3(a);
        };

        var inner = Java.use("com.example.goal.HookGoal$InnerClass");
        //hook内部类
        inner.$init.overload("com.example.goal.HookGoal", "java.lang.String").implementation = function (clas, arg) {
            send("innerClass构造函数的参数:" + arg);
            return this.$init(clas, "frida is hooking");
        };
        //hook 内部类方法
        inner.innerFunc.implementation = function (s) {
            send("frida hook 前innerFunc()的参数:" + arguments[0]);
            var num = inner.class.getDeclaredField("innerNumber");
            num.setAccessible(true);

            //内部类成员方法中修改成员属性,way1 通过this.xxx.value 访问、修改
            send("通过this.innerNumber.value获取值:" + this.innerNumber.value);
            this.innerNumber.value = 1;
            send("通过this.innerNumber.value设置值后:" + this.innerNumber.value);
            //way2 先通过反射得到字段,
            send("反射方式 innerNumber修改前:" + num.get(this));
            num.setInt(this, 2);
            send("反射方式 innerNumber修改后:" + num.get(this));
            return this.innerFunc("frida is hooking");
        };

  • native层hook
 //so层hook
        //导出函数
        //var exports = Module.enumerateExportsSync("libnative-lib.so");
        //for(var i=0;i<exports.length;i++){
        //    send("name:"+exports[i].name+"  address:"+exports[i].address);
        // }

        //遍历模块找基址
        Process.enumerateModules({
            onMatch: function (exp) {
                if (exp.name == 'libnative-lib.so') {
                    send('enumerateModules find');
                    send(exp.name + "|" + exp.base + "|" + exp.size + "|" + exp.path);
                    send(exp);
                    return 'stop';
                }
            },
            onComplete: function () {
                send('enumerateModules stop');
            }
        });

        //通过模块名直接查找基址
        var soAddr = Module.findBaseAddress("libnative-lib.so");
        send("soAddr:" + soAddr);


        //   hook导出函数 通过函数名
        send("findExportByName add():" + Module.findExportByName("libnative-lib.so", "_Z3addii"));
        //send("findExportByName edit():"+Module.findExportByName("libnative-lib.so", "_ZL4editP7_JNIEnvP8_jobjecti"))
        // Interceptor.attach(Module.findExportByName("xxx.so", "xxxx"), {
        //     onEnter: function (args) {
        //         send("open(" + Memory.readCString(args[0]) + "," + args[1] + ")");
        //     },
        //     onLeave: function (retval) {
        //
        //     }
        // });


        //say(JNIEnv *env, jobject)
        //edit(JNIEnv *env, jobject,int)
        //mystr(JNIEnv *env, jobject,jstring s)
        //myarray(JNIEnv *env, jobject obj, jobjectArray array)

        // arm64-v8a 下地址
        //var fadd = 0xED7C;
        //var fsay=0xF12C;
        //var fedit=0xF04C;
        //var fmystr=0xF328;
        //var fmyarray=0xF4DC;

        //下面为x86模拟器中地址偏移  arm真机下thumb指令下地址+1
        var fadd = 0x8AD0;
        var fsay = 0x8FA0;
        var fedit = 0x8E70;
        var fmystr = 0x9200;
        var fmyarray = 0x9420;

        //armeabi-v7a
        //var fadd = 0x839C;


        var faddptr = new NativePointer(soAddr).add(fadd);//得到内存地址
        send("函数add() faddptr:" + faddptr);
        //调用add(5,6)
        var funadd = new NativeFunction(faddptr, "int", ['int', 'int']);
        var t = funadd(5, 6);
        send("调用native 方法fun():" + t);
        Interceptor.attach(faddptr, {
            onEnter: function (args) {
                send("onEnter add()");
                x = args[0];
                y = args[1];
                args[0] = ptr(x * 2);
                args[1] = ptr(y * 2);
                send("hook add()修改参数为原来的两倍 args[0]:" + args[0].toInt32() + "  args[1]:" + args[1].toInt32());
            },
            onLeave: function (retval) {
                send("onLeave  add()");
                //retval.replace(678);
                //send("add()修改返回值为:"+retval.toInt32())
            }

        });

        var fsayptr = new NativePointer(soAddr).add(fsay);
        Interceptor.attach(fsayptr, {
            onEnter: function (args) {
                send("onEnter say()");
            },
            onLeave: function (retval) {
                send("onLeave say()");
                //jstring类型无法直接输出显示,可以类型转换到java.lang.String
                var s = Java.cast(retval, str);
                send("say() 原返回值:" + s);
                //调用env下的方法,构造jstring类型
                var env = Java.vm.getEnv();
                var jstring = env.newStringUtf("frida hook native");
                retval.replace(ptr(jstring));
                send("修改say()返回值:" + Java.cast(jstring, str));
            }
        });


        var feditptr = new NativePointer(soAddr).add(fedit);
        Interceptor.attach(feditptr, {
            onEnter: function (args) {
                send("onEnter edit()");
                send("edit() env:" + args[0] + "  jobject:" + args[1] + " jint:" + args[2].toInt32());
                //参数修改使用new NativePointer(s)  简写ptr(s)
                args[2] = ptr(4);
                send("hook edit() 修改后的参数jint:" + args[2]);
            },
            onLeave: function (retval) {
                send("onLeave edit()");
            }
        });

        var fmystrptr = new NativePointer(soAddr).add(fmystr);
        send("fmystrptr:" + fmystrptr);
        Interceptor.attach(fmystrptr, {
            onEnter: function (args) {
                send("onEnter mystr()");
                send("mystr() env:" + args[0] + "  jobject:" + args[1] + " jstring:" + args[2]);
                var s = Java.cast(args[2], str);
                send("mystr() jstring参数:" + s);

                //send("mystr:"+Memory.readUtf16String(args[2],7));
                //send("mystr:"+Memory.readUtf8String(args[2],7));
            },
            onLeave: function (retval) {
                send("onLeave mystr()");
                var env = Java.vm.getEnv();
                var jstring = env.newStringUtf("frida hook native");
                send("修改返回值jstring:" + jstring);
                retval.replace(ptr(jstring));
            }
        });
        // Java.choose("com.example.goal.DiyClass",{
        //     onMatch:function(instance){
        //         send("DiyClass instance:"+instance);
        //     },
        //     onComplete:function(){
        //
        //     }
        //
        // });
        var fmyarrayptr = ptr(soAddr).add(fmyarray);
        //var fmyarrayptr = new NativePointer(soAddr).add(fmyarray);
        send("fmyarrayptr:" + fmyarrayptr);
        //var argptr;
        Interceptor.attach(fmyarrayptr, {
            onEnter: function (args) {
                send("onEnter myarray()");
                send("mystr() env:" + args[0] + "  jobject:" + args[1] + " jobjectArray:" + args[2]);
                send("jobjectArray参数:" + args[2].toString());
                //可以在onEnter中通过this.xxx保存变量 在onLeave中通过this.xxx读取
                this.argptr = args[2]

                //jstring 不同于wchar_t* (jchar*) 与 char*
                //send("mystr:"+Memory.readUtf16String(args[2],7));
                //send("mystr:"+Memory.readUtf8String(args[2],7));
            },
            onLeave: function (retval) {
                send("onLeave myarray()");
                send("argptr:" + this.argptr);

                var env = Java.vm.getEnv();
                var cla = env.findClass("com/example/goal/DiyClass");
                send("clazz:" + cla);
                var initid = env.getMethodId(cla, "<init>", "(I)V");
                send("initid:" + initid);
                var setid = env.getMethodId(cla, "setData", "(I)V");
                send("setid:" + setid);
                var getid = env.getMethodId(cla, "getData", "()I");
                send("getid:" + getid);
                //frida 中env 方法参考frida-java/lib/env.js  本人能力有限,有些方法确实搞不懂
                //调用env中的allocObject()方法创建对象,未初始化,
                var obj1 = env.allocObject(cla);
                send("obj1:" + obj1);

                var obj2 = env.allocObject(cla);
                send("obj2:" + obj2);

                var rtarray = env.newObjectArray(2, cla, ptr(0));
                send("env.newObjectArray:" + rtarray);

                //获取DiyClass类中public void setData(int data)方法
                var nvmethod = env.nonvirtualVaMethod("void", ["int"]);
                //NativeType CallNonvirtual<type>Method(JNIEnv *env, jobject obj,jclass clazz, jmethodID methodID, ...);
                //设置obj1中data值
                nvmethod(env, obj1, cla, setid, 11);
                //设置obj2中data值
                nvmethod(env, obj2, cla, setid, 22);
                send("env.nonvirtualVaMethod(JNIEnv,jobject,jclass,jmethodid,args):" + nvmethod);
                //设置数组中的元素
                env.setObjectArrayElement(rtarray, 0, obj1);
                env.setObjectArrayElement(rtarray, 1, obj2);
                send("env.newObjectArray:" + rtarray);

                send("原retval:" + retval);
                retval.replace(ptr(rtarray));
                send("修改后retval:" + retval);

                // //堆中分配空间
                // var memo=Memory.alloc(4);
                // //写入数据
                // Memory.writeInt(memo,0x40302010);
                // // 读取数据
                // console.log(hexdump(memo, {
                //         offset: 0,
                //         length: 64,
                //         header: true,
                //         ansi: true
                // }));
            }
        });

    });
});
我喜欢写好js后放到python中用
import frida, sys


def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)


jscode = """
<js代码放到这里>

"""
# 启动时hook   //在测试时出现了问题
# devices=frida.get_usb_device()
# pid=devices.spawn(['com.example.goal'])   #以挂起方式创建进程 真机报错frida.PermissionDeniedError: unable to access  process
#找到原因了,我的是Android8.0 使用了Magisk,默认开启了Magisk Hide选项与zygote64冲突。
#解决方法:Magisk   -->设置-->Magisk下 去掉勾选Magisk Hide 
  
#模拟器报错Failed to spawn: the connection is closed
# session=devices.attach(pid)
# devices.resume(pid)    #创建完脚本, 恢复进程运行
# script=session.create_script(jscode)

# 命令行frida -U -f com.example.goal --no-pause -l <hook.js>

# 运行中hook
process = frida.get_usb_device().attach('com.example.goal')
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running test')
script.load()
sys.stdin.read()

也可以使用frida命令行 打开.js  (打印使用console.log()
frida -U <进程名或pid> -l <js文件>

代码中涉及到的一些点大部分给出了注释,当然你只能这里看到一丢丢的frida方法的使用,更多的方法还要请参考官方文档、源代码···
例如native层会用到大量frida JavaScript API 的Memory 操作,
frida中的env方法与jni开发中的方法并不完全相同,请参考源码frida-java/lib/env.js,
代码中涉及到的地址偏移请根据实际情况修改
  • hook前log输出
4513-4513/com.example.goal I/frida hooking: HookGoal hookGoalNumber:0
4513-4513/com.example.goal I/jni test: edit()内调用了add(),原参数:1
4513-4513/com.example.goal I/jni test: edit()内设置hookGoalNumber字段为:2
4513-4513/com.example.goal I/jni test: GetStringUTFRegion 截取jstring保存到本地buf中:abcdefg
4513-4513/com.example.goal I/jni test: GetStringUTFChars 返回const char* 类型:abcdefg
4513-4513/com.example.goal I/jni test: GetStringChars 返回const jchar* 类型
4513-4513/com.example.goal I/jni str: abcdefg
4513-4513/com.example.goal I/jni array: jni调用前 参数数组 成员0: 1 成员1: 2
4513-4513/com.example.goal I/jni test: myarray()使用array参数,返回交换元素位置的新数组
4513-4513/com.example.goal I/jni array: jni调用后 返回新数组 成员0: 2 成员1: 1
4513-4513/com.example.goal I/frida hooking: hookGoalNumber:2
4513-4513/com.example.goal I/frida hooking: eat apple
4513-4513/com.example.goal I/frida hooking: func2 私有静态方法
4513-4513/com.example.goal I/frida hooking: DiyClass[0].getData:0
4513-4513/com.example.goal I/frida hooking: DiyClass[1].getData:0
4513-4513/com.example.goal I/frida hooking: DiyClass[2].getData:0
4513-4513/com.example.goal I/frida hooking: InnerClass 构造函数 私有内部类
4513-4513/com.example.goal I/frida hooking: InnerClass innerNumber:0
4513-4513/com.example.goal I/frida hooking: InnerClass innerFunc 内部类方法调用
4513-4513/com.example.goal I/jni test: native 函数say()返回字符串my jni hhh

  • hook后log输出
4513-4513/com.example.goal I/frida hooking: HookGoal hookGoalNumber:666
4513-4513/com.example.goal I/jni test: edit()内调用了add(),原参数:4
4513-4513/com.example.goal I/jni test: edit()内设置hookGoalNumber字段为:16
4513-4513/com.example.goal I/jni test: GetStringUTFRegion 截取jstring保存到本地buf中:abcdefg
4513-4513/com.example.goal I/jni test: GetStringUTFChars 返回const char* 类型:abcdefg
4513-4513/com.example.goal I/jni test: GetStringChars 返回const jchar* 类型
4513-4513/com.example.goal I/jni str: frida hook native
4513-4513/com.example.goal I/jni array: jni调用前 参数数组 成员0: 1 成员1: 2
4513-4513/com.example.goal I/jni test: myarray()使用array参数,返回交换元素位置的新数组
4513-4513/com.example.goal I/jni array: jni调用后 返回新数组 成员0: 11 成员1: 22
4513-4513/com.example.goal I/frida hooking: hookGoalNumber:16
4513-4513/com.example.goal I/frida hooking: eat is hooking
4513-4513/com.example.goal I/frida hooking: welcome
4513-4513/com.example.goal I/frida hooking: welcome
4513-4513/com.example.goal I/frida hooking: welcome
4513-4513/com.example.goal I/frida hooking: func2 is hooking
4513-4513/com.example.goal I/frida hooking: welcome
4513-4513/com.example.goal I/frida hooking: DiyClass[0].getData:111
4513-4513/com.example.goal I/frida hooking: DiyClass[1].getData:222
4513-4513/com.example.goal I/frida hooking: DiyClass[2].getData:333
4513-4513/com.example.goal I/frida hooking: InnerClass 构造函数 frida is hooking
4513-4513/com.example.goal I/frida hooking: InnerClass innerNumber:0
4513-4513/com.example.goal I/frida hooking: InnerClass innerFunc frida is hooking
4513-4513/com.example.goal I/jni test: native 函数say()返回字符串my jni hhh

  • hook脚本的输出
[*] Running test
start
[*] 修改HookGoal的静态变量TAG为:frida hooking
[*] HookGoal构造函数的参数number:0
[*] HookGoal构造函数的参数修改为666
[*] new HookGoal instance newhg: com.example.goal.HookGoal@4a854cb4
[*] HookGoal构造函数的参数number:0
[*] HookGoal构造函数的参数修改为666
[*] new HookGoal instance newhg1: com.example.goal.HookGoal@4a854f70
[*] enumerateModules find
[*] libnative-lib.so|0x7d0b0000|221184|/data/app-lib/com.example.goal-1/libnative-lib.so
[*] {'name': 'libnative-lib.so', 'base': '0x7d0b0000', 'size': 221184, 'path': '/data/app-lib/com.example.goal-1/libnative-lib.so'}
[*] enumerateModules stop
[*] soAddr:0x7d0b0000
[*] 函数add() nativeptr:0x7d0b8ad0
[*] 调用native 方法fun():11
[*] findExportByName add():0x7d0b8ad0
[*] fmystrptr:0x7d0b9200
[*] fmyarrayptr:0x7d0b9420
[*] HookGoal构造函数的参数number:0
[*] HookGoal构造函数的参数修改为666
[*] onEnter edit()
[*] edit() env:0xb83a6e50  jobject:0xf7c00025 jint:1
[*] hook edit() 修改后的参数jint:0x4
[*] onEnter add()
[*] hook add()修改参数为原来的两倍 args[0]:8  args[1]:8
[*] onLeave  add()
[*] onLeave edit()
[*] onEnter mystr()
[*] mystr() env:0xb83a6e50  jobject:0xf7d00025 jstring:0x3d300029
[*] mystr() jstring参数:abcdefg
[*] onLeave mystr()
[*] 修改返回值jstring:0xc5b00035
[*] onEnter myarray()
[*] mystr() env:0xb83a6e50  jobject:0xf8000025 jobjectArray:0x3d600029
[*] jobjectArray参数:0x3d600029
[*] onLeave myarray()
[*] argptr:0x3d600029
[*] clazz:0x26800045
[*] initid:0x881d1ea8
[*] setid:0x881d1f20
[*] getid:0x881d1ee8
[*] obj1:0x1e000049
[*] obj2:0x1e00004d
[*] env.newObjectArray:0x1dc00051
[*] env.nonvirtualVaMethod(JNIEnv,jobject,jclass,jmethodid,args):0xb4d88bb0
[*] env.newObjectArray:0x1dc00051
[*] 原retval:0x2f900041
[*] 修改后retval:0x1dc00051
[*] eat参数获取 way1:apple
[*] eat参数获取 way2:apple
[*] func2()参数获取:私有静态方法
[*] func2()内调用func0()  通过创建实例newhg调用
[*] func2()内调用func0()  通过创建实例newhg1调用
[*] 实例newhg1的hookGoalNumber:666
[*] 修改实例newhg1的hookGoalNumber:777
[*] 实例newhg的hookGoalNumber:666
[*] func0:public void com.example.goal.HookGoal.func0()
[*] func2()内调用func0()  way2 通过反射调用
[*] func2()内调用DiyClass下的getData() 通过创建实例d调用 返回:666
[*] func3()内调用func0()  way2 成员方法中直接调用其他成员方法
[*] func3参数:com.example.goal.DiyClass@4a8596b4,com.example.goal.DiyClass@4a8596bc,com.example.goal.DiyClass@4a8596c4
[*] func3参数修改:com.example.goal.DiyClass@4a859a08,com.example.goal.DiyClass@4a859a78,com.example.goal.DiyClass@4a859ae8
[*] innerClass构造函数的参数:私有内部类
[*] frida hook 前innerFunc()的参数:内部类方法调用
[*] 通过this.innerNumber.value获取值:0
[*] 通过this.innerNumber.value设置值后:1
[*] 反射方式 innerNumber修改前:1
[*] 反射方式 innerNumber修改后:2
[*] onEnter say()
[*] onLeave say()
[*] say() 原返回值:my jni hhh
[*] 修改say()返回值:frida hook native
frida功能十分强大,在这里我作为初学者简单的记录了下学习经历,当然这仅仅是一些常规操作,是基础,日后的学习应该是在实战中,实践出真知。

合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下

我在学习的过程之中也遇到了各种各样的坑,困惑过,苦恼过。解决问题绝对是一种很棒的学习方式,
希望各位能够提出自己的问题,大家一起交流,共同成长!

同时文中难免会有错误,欢迎各位坛友指出,不胜感激!


链接:https://pan.baidu.com/s/1KX1fY16NgaYB1FnCrpu0lQ 
提取码:t38k 
(打好基础,改日实战一番!)

[课程]FART 脱壳王!加量不加价!FART作者讲授!

最后于 2019-11-22 17:11 被堂前燕编辑 ,原因: 修改注释
收藏
免费 4
支持
分享
最新回复 (36)
雪    币: 2375
活跃值: (433)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
楼主电脑装的是linux 么
2019-6-29 17:01
0
雪    币: 10477
活跃值: (4167)
能力值: ( LV12,RANK:329 )
在线值:
发帖
回帖
粉丝
3
petersonhz 楼主电脑装的是linux 么[em_1]
不是呀,就windows
2019-6-29 17:13
0
雪    币: 2375
活跃值: (433)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
堂前燕 不是呀,就windows
win10 wsl么?
2019-6-29 18:08
0
雪    币: 1534
活跃值: (748)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
5
//hook 静态变量TAG
var reflectField = Java.cast(hookgoal.class, clazz).getDeclaredField("TAG");

访问静态变量用hookgoal.TAG.value比较方便吧。
还有很多方法调用别使用反射效率太低,对于访问不了的报错使用Java.cast强制转换:
var castNewhg1 = Java.cast(newhg1, hookgoal)
castNewhg1.func0()
最后于 2019-6-30 22:00 被金罡编辑 ,原因:
2019-6-30 21:59
1
雪    币: 10477
活跃值: (4167)
能力值: ( LV12,RANK:329 )
在线值:
发帖
回帖
粉丝
6
金罡 //hook&nbsp;静态变量TAGvar&nbsp;reflectField&nbsp;=&nbsp;Java.cast(hookgoal.class,&n ...
感谢大佬,学习了
2019-7-1 08:23
0
雪    币: 24
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
大佬我想问一下为什么我用Java.cast进行数据类型转换都会报错,我本来是想把java.lang.Object转为[B但是失败了,然后我尝试string转为stirng也会报错,大佬这是什么情况啊
2019-7-27 15:56
0
雪    币: 10477
活跃值: (4167)
能力值: ( LV12,RANK:329 )
在线值:
发帖
回帖
粉丝
8
mb_gygeohyt 大佬我想问一下为什么我用Java.cast进行数据类型转换都会报错,我本来是想把java.lang.Object转为[B但是失败了,然后我尝试string转为stirng也会报错,大佬这是什么情况啊
呃,我也不清楚,可以参考下官方文档:Java.cast(handle, klass): create a JavaScript wrapper given the existing instance at handle of given class klass as returned from Java.use(). Such a wrapper also has a class property for getting a wrapper for its class, and a $className property for getting a string representation of its class-name.我想你是否想从js传递byte array到java https://zhiwei.li/text/2017/11/03/frida%E5%A6%82%E4%BD%95%E4%BB%8Ejs%E4%BC%A0%E9%80%92byte-array%E5%88%B0java/
条条大路通罗马,遇到问题记录下来,暂时无法解决就换条路试试,希望你能解决问题,分享心得。
2019-7-28 15:42
0
雪    币: 266
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
gqm
9
可以加您微信吗
2019-8-19 02:13
0
雪    币: 348
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10
mark
2019-8-21 23:46
0
雪    币: 110
活跃值: (1515)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
很全面的教程,非常感谢分享。
2019-10-14 17:30
0
雪    币: 11
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
这个地方写的是这个吧,不改一直报错
jstring js=env->NewStringUTF(ptr_c);
2019-11-21 17:13
0
雪    币: 10477
活跃值: (4167)
能力值: ( LV12,RANK:329 )
在线值:
发帖
回帖
粉丝
13
ubbuzjbsa 这个地方写的是这个吧,不改一直报错jstring js=env-&gt;NewStringUTF(ptr_c);
一开始
LOGI("NewStringUTF(buf) 返回jstring类型:%s",j's);
打印jstring是错误的,所以注释掉了,
访问java.lang.String对应的JNI类型jstring时,不能像访问基本数据类型一样直接使用,因为它在Java是一个引用类型,所以在本地代码中只能通过GetStringUTFChars这样的JNI函数来访问字符串的内容
可以修改为:
LOGI("NewStringUTF(buf) 返回jstring类型:%s",env->GetStringUTFChars(js,NULL));
2019-11-22 17:07
0
雪    币: 11
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
堂前燕 一开始LOGI("NewStringUTF(buf)&nbsp;返回jstring类型:%s",j's); 打印jstring是错误的,所以注释掉了,访问java.lan ...
不是这个LOGI的问题,是jstring js=env->NewStringUTF(buf);这里在虚拟机上会引发错误

最后于 2019-11-22 17:42 被ubbuzjbsa编辑 ,原因:
2019-11-22 17:41
0
雪    币: 1700
活跃值: (676)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
最后于 2019-12-1 00:59 被弱冠甕卿还仓编辑 ,原因:
2019-12-1 00:47
0
雪    币: 340
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
ubbuzjbsa 堂前燕 一开始LOGI("NewStringUTF(buf)&amp;nbsp;返回jstring类型:%s",j's); 打印 ...
问下,在虚拟机里的话这个错误怎么解决呢,谢谢
2019-12-4 11:55
0
雪    币: 10477
活跃值: (4167)
能力值: ( LV12,RANK:329 )
在线值:
发帖
回帖
粉丝
17
Misirluo 问下,在虚拟机里的话这个错误怎么解决呢,谢谢
链接:https://pan.baidu.com/s/1gFVCakD-iDF84a1kLFteuA 
提取码:7qia 
这个项目试试看,我用的夜神模拟器,真机用的Android8.0,本地测试没报错。之前测试出现过这个问题,后来又不报错了,我也很迷。关键就NewStringUTF()这个函数,具体原因我也不清楚了。可能安卓版本有影响?待解决。
2019-12-4 18:53
0
雪    币: 340
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
好的,谢谢
2019-12-4 22:00
0
雪    币: 8
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
19
大哥,这个一串字母,是函数名吗,怎么是这样命名的
2019-12-30 17:46
0
雪    币: 10477
活跃值: (4167)
能力值: ( LV12,RANK:329 )
在线值:
发帖
回帖
粉丝
20
mb_gxlcqjan 大哥,这个一串字母,是函数名吗,怎么是这样命名的
编译器对函数名的修饰,保证函数的唯一性。
可以看看:https://blog.csdn.net/weiwangchao_/article/details/7165467
https://blog.csdn.net/roland_sun/article/details/43233565
2019-12-30 19:48
0
雪    币: 8
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
21
堂前燕 编译器对函数名的修饰,保证函数的唯一性。 可以看看:https://blog.csdn.net/weiwangchao_/article/details/7165467 https://blog. ...
嗯,明白了,大哥,还有个问题,get_usb_device这个方法我这跑不通,我用的get_remote_device 这个方法,不知道这两个方法有什么区别,官网上好像没有说明
2019-12-30 21:48
0
雪    币: 2559
活跃值: (509)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
22
也是刚接触frida,尝试hook so,发现找不到so文件,不知道这种情况要怎么继续下一步。
2019-12-30 22:53
0
雪    币: 399
活跃值: (2619)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
frida 学习了
2020-1-2 14:06
0
雪    币: 3968
活跃值: (140)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
金罡 //hook&nbsp;静态变量TAGvar&nbsp;reflectField&nbsp;=&nbsp;Java.cast(hookgoal.class,&n ...
金罡牛牛解答就是简单高效.
2020-1-7 09:44
0
雪    币: 4508
活跃值: (1525)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
mb_gxlcqjan 嗯,明白了,大哥,还有个问题,get_usb_device这个方法我这跑不通,我用的get_remote_device 这个方法,不知道这两个方法有什么区别,官网上好像没有说明
这个函数命名在哪儿可以看到,或者说 用什么工具看到的啊
2020-1-8 15:29
0
游客
登录 | 注册 方可回帖
返回
//