Android hook之Frida学习笔记

一、Frida介绍

Frida是一个跨平台的轻量级hook框架,通过向程序中注入JavaScript完成动态插桩。所以,他需要你懂一些JavaScript的知识。Frida提供了python、Node.js、Swift、.net、Qml等语言的接口封装,本文我们使用python+javascript完成一些简单程序的hook。

二、Frida安装

在服务端通过pip安装frida

pip install frida

根据手机处理器下载frida-server

https://github.com/frida/frida/releases

将frida-server push到手机并运行

adb push frida-server /data/local/tmp
adb shell
su
cd data/local/tmp
chmod 777 frida-server
./frida-server

一定要注意让frida-server在root权限下运行

另开一个命令行窗口进行端口转发

adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043
我们可以通过
frida-ps -R或frida-ps -U

测试是否搭建成功

正常情况下应该会显示我们手机上的所有进程


三、Frida使用

接下来我们分别通过hook java函数和native函数进行简单的使用介绍

我们的示例程序代码:

package com.example.a13251.fridatest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private TextView tv;
    private TextView textView;
    private Button button;
    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        tv = (TextView) findViewById(R.id.sample_text);
        textView=(TextView)findViewById(R.id.textView);
        button= (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                tv.setText(String.valueOf(jia(5)));
                textView.setText(stringFromJava());
            }
        });

    }

    private String stringFromJava() {
        return "hello java";
    }
    public native int jia(int k);
}
 jint  Java_com_example_a13251_fridatest_MainActivity_jia(JNIEnv *env, jobject obj,
                                                                          jint k) {
    return k + 1;
}

当我们点击button时会调用jia函数和stringFromJava函数,其中jia()返回参数加一,stringFromJava()返回“hello java”

java hook代码如下

import frida
import sys
def on_message(message,data):
    type = message["type"]
    msg = message
    if type == "send":
        msg = message["payload"]
    elif type == 'error':
        msg = message['stack']
    else:
        msg=message
    print msg
jscode="""
Java.perform(function(){
    var s=Java.use("com.example.a13251.fridatest.MainActivity");
    s.stringFromJava.implementation=function(){
        var ss=this.stringFromJava();
        send(ss);
        return ss;
        };
    });
"""
rdev=frida.get_remote_device()
session=rdev.attach("com.example.a13251.fridatest")
script=session.create_script(jscode)
script.on('message',on_message)
script.load()

我们对于MainActivity类中的函数stringFromJava进行覆盖,在覆盖的函数中,我们调用了原函数,并将返回值返回,这样并不会影响程序的运行,不过我们在覆盖的函数中将返回值进行了输出。

对于jia() hook如下:

import frida
import sys
def on_message(message,data):
    type = message["type"]
    msg = message
    if type == "send":
        msg = message["payload"]
    elif type == 'error':
        msg = message['stack']
    else:
        msg=message
    print msg


jjscode="""
Java.perform(function(){
var getJia=undefined;
exports=Module.enumerateExportsSync("libnative-lib.so");
for(i=0;i<exports.length;i++){
    if(exports[i].name=="Java_com_example_a13251_fridatest_MainActivity_jia"){
        getJia=exports[i].address;
        send(getJia);
        break;
    }
}
Interceptor.attach(getJia,
    {
        onEnter:function(args){
            send(args[2].toInt32());
        },
        onLeave:function(retval){
            retval.replace(9999);
        }
    });

});    
"""
rdev=frida.get_remote_device()
session=rdev.attach("com.example.a13251.fridatest")
script=session.create_script(jjscode)
script.on('message',on_message)
script.load()

通过Module.enumerateExportsSync("libnative-lib.so")获取so文件中所有的的函数,然后对其进行遍历,寻找名为Java_com_example_a13251_fridatest_MainActivity_jia的函数

其实

var getJia=undefined;
for(i=0;i<exports.length;i++){
    if(exports[i].name=="Java_com_example_a13251_fridatest_MainActivity_jia"){
        getJia=exports[i].address;
        send(getJia);
        break;
    }
}

这一段函数我们可以通过

Module.findExportByName("libnative-lib.so","Java_com_example_a13251_fridatest_MainActivity_jia");

来完成,该函数的第一个参数可以为空,frida会遍历所有的文件去寻找具有相同名字的函数。

接下来,我们通过Interceptor.attach对jia()进行附加,Interceptor.attach的第一个参数为要附加的函数的地址,第二个参数为附加后执行的方法,onEnter表示当调用被附加函数时,onLeave表示当结束调用被附加函数时。我们在onEnter中输出jia()的参数,在onLeave中将返回值替换为9999。


除本文所出现的所有函数外,个人认为比较常用的函数还有

1.Memory.alloc(size),在被附加的程序堆中开辟size的空间,然后我们可以通过Memory.writeInt(addr,value) writeDouble(addr,value) writeUtf8String(addr,str)写入开辟的空间,通过Memory.readInt(addr)对于指定内存空间进行读操作;

2.New NativeFunction(address,returnType,argument),address为原函数的地址,returnType为原函数返回值,argument为原函数参数list,支持的类型为

-   void
-   pointer
-   int
-   uint
-   long
-   ulong
-   char
-   uchar
-   float
-   double
-   int8
-   uint8
-   int16
-   uint16
-   int32
-   uint32
-   int64
-   uint64

frida对于参数和返回值类型并不支持string,而是使用pointer进行了替代。

四、后记

frida对于art的支持并不理想,最好是使用安卓4.4对程序进行hook,我们可以通过观看frida使用手册https://www.frida.re/docs/home/   来了解更多关于frida的使用说明。


    

猜你喜欢

转载自blog.csdn.net/Magic1an/article/details/79594744