Android之Frida框架完全使用指南

安卓逆向 专栏收录该内容
23 篇文章 11 订阅

Frida简介

Frida是一款基于Python + JavaScript 的hook框架,本质是一种动态插桩技术。可以用于Android、Windows、iOS等各大平台,其执行脚本基于Python或者Node.js写成,而注入代码用JavaScript写成,所以有必要了解一些这些语言的语法。本文主要讲述了Android上Frida框架的使用。

环境搭建

python安装与虚拟环境配置

使用Frida需要Python 3环境,首先需要安装python3.7版本,安装完成之后再安装虚拟环境。

安装virtualenvwrapper

pip install virtualenvwrapper-win -i https://pypi.doubanio.com/simple

配置虚拟环境变量->WORKON_HOME

在这里插入图片描述

将WORKON_HOME添加到用户变量,可以修改虚拟环境的存储路径

创建虚拟环境

mkvirtualenv --python=C:\Python37\python.exe FridaHook

进入虚拟环境

workon FridaHook

建立好虚拟环境以后,所有的修改都是针对虚拟环境

Frida安装

pip install frida -i https://pypi.mirrors.ustc.edu.cn/simple/ 
pip install frida-tools -i https://pypi.mirrors.ustc.edu.cn/simple/ 

使用pip命令可以直接安装frida,但是速度会比较慢。坐下来喝杯茶慢慢等就行了。

(FridaHook) C:\Users\87321\Desktop>frida --version
15.1.4

(FridaHook) C:\Users\87321\Desktop>

安装完成之后可以查看版本表示安装成功

配置代码提示

安装vscode,配置vscode语言中文简体。打开"vscode" , 按快捷键"Ctrl+Shift+P",在顶部搜索框中输入"configure language"

安装nodejs,新建一个文件夹,在目录下执行下面这条命令

npm i @types/frida-gum

在这里插入图片描述

执行完成之后在文件夹中新建js脚本

在这里插入图片描述

此时就会出现相关的代码提示

Server环境配置

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

下载Frida的Server端->frida-server-15.1.4-android-arm64.xz,下载完成之后解压。然后push到手机的data/local/tmp目录下,和IDA的动态调试有点类似

adb push .\frida-server-15.1.4-android-arm64 /data/local/tmp/frida

然后修改权限

# chmod 777 frida

直接运行frida服务

./frida

开启端口转发

adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043

PC端输入

frida-ps -U

如果能显示进程列表说明环境搭建完成

在这里插入图片描述

注意这条命令需要在python虚拟环境中执行

安装低版本Frida

由于Frida的稳定性较差,我用Android6.0的系统安装Frida最新版本出现了问题。解决方案是安装低版本的Frida,用下面的命令即可。

pip install frida==12.8.0
pip install frida-tools==5.3.0
pip install objection==1.8.4

Frida Hook

执行HOOK

首先来说一下如何执行hook代码

  1. 启动时hook,CMD输入
frida -U -f com.test.apk -l test.js --no-pause

将 com.test.apk 换成你手机里安装好的任意apk包名

  1. 启动apk后 hook
frida -U -f com.test.apk --no-pause
%load test.js

看到这个提示说明hook成功

在这里插入图片描述

Java层hook

Frida的Java层重要函数

  1. 使用java平台—>Java.perform(function () {}
  2. 获取Java类 —>Java.use(className)
  3. 当我们获取到Java类之后,我们直接通过 <wrapper>.<method>.implementations =function() {}的方式来hook wrapper类的method方法,不管是实例方法还是静态方法都可以

Hook普通方法

被hook的代码如下;

package com.example.hookdemo01;


public class Student {
    static public int Add(int a,int b)
    {
        return a+b;
    }

}

接着编写Hook代码

function main()
{
     //使用java平台
     Java.perform(
        function() {
            //获取java类
            var student=Java.use("com.example.hookdemo01.Student");
            //hook Add方法(重写Add方法) 
            student.Add.implementation=function(a,b)
            {
                //修改参数
                a=123;
                b=456;
                //调用原来的函数
                var res = this.Add(a,b);
                //输出结果
                console.log(a,b,res);
                return res;
            }
        }

     );
    
}

setImmediate(main)

实际效果:

在这里插入图片描述

接着我们执行hook,可以看到Frida成功打印出了我们修改后的参数

重载方法

在这里插入图片描述

修改一下目标代码,新增三个重载函数,接着编写hook代码

//hook重载方法
function hookTest1()
{
    //获取java类
    var student=Java.use("com.example.hookdemo01.Student");
    //hook test
    student.test.overload('int').implementation=function(a)
    {
        //修改参数
        a=123;
        //调用原来的函数 
        var res = this.test(a);
       
        //输出结果
        console.log(a,res);
        return res;
    }
}

hook重载函数和hook普通方法有所区别,需要在方法名后调用overload()函数,并在括号内传入需要hook的重载函数的参数。

实际效果:

在这里插入图片描述

执行hook,同样可以对函数参数进行修改。

//hook所有重载函数
function hookTest2()
{
    //获取java类
    var student=Java.use("com.example.hookdemo01.Student");
    //重载方法的个数
    var overlength=student.test.overloads.length;
    //循环hook所有重载方法
    for(var i=0;i<overlength;i++)
    {
        student.test.overloads[i].implementation=function()
        {
            //打印参数个数
            console.log(arguments.length);
            return this.test.apply(this,arguments);
        }
    }
    
}

同时,我们也可以用overloads来对所有的重载函数进行hook,每一个重载函数只需要用arguments.length来进行区分即可。

构造方法

在这里插入图片描述

再次修改代码,添加一个构造函数,接着编写hook代码

//hook构造函数
function hookTest3()
{
    //获取java类
    var student=Java.use("com.example.hookdemo01.Student");
    
    student.$init.implementation=function(name,age)
    {
        //输出参数
        console.log(name,age);

        //调用原函数
        this.$init(name,age);

        //调用构造函数
        //student.$new("guishou",888);

    }
      
}

hook构造函数需要用$init来代替函数名,另外,可以用$new方法来调用构造函数,并且获取一个新的对象

修改类字段

在这里插入图片描述

修改代码,添加一个静态字段和一个私有字段,接着编写Hook代码

//修改类字段
function hookTest4()
{
    //获取java类
    var student=Java.use("com.example.hookdemo01.Student");
     //修改静态字段
    student.nickname.value="GuiShouFlags";
    console.log(student.nickname.value);
    
    //修改非静态字段
    Java.choose("com.example.hookdemo01.Student",{
        //每遍历一个对象都会调用onMatch
        onMatch:function(obj)
        {
            //修改每个对象的字段
            obj.number.value=999;
            console.log(obj.number.value);
            
            //字段名和函数名相同需要加下划线
            //obj._number.value=999;
        },
        //遍历完成后调用onComplete
        onComplete:function()
        {

        }
    }); 
      
}

修改静态字段的方法较为简单,直接用类名点出字段名再加上value值即可直接修改,修改非静态字段需要调用choose函数,传入类名和回调函数进行修改

在这里插入图片描述

修改后效果如图

hook内部类和匿名类

修改目标代码

在这里插入图片描述

添加一个内部类,然后hook内部类中的innerPrint

//hook内部类
function hookTest5()
{
    //获取java类
    var inner=Java.use("com.example.hookdemo01.Student$innerClass");
    
    student.innerPrint.implementation=function()
    {
        //其他操作相同

    }
      
}

hook内部类需要在获取java类时,在类名后加上$符号连接内部类,这个完整的类名可以在smali代码中看到。匿名类的hook方法和内部类一样,可以通过查看smali代码的类名来hook。

枚举所有类和方法

代码如下:

function hookTest6()
{
    //枚举已经加载的类 异步方式
    Java.enumerateLoadedClasses({
        //每枚举一个类调用一次
        onMatch:function(name,handler)
        {
            //对类名进行过滤 
            if(name.indexOf("com.example.hookdemo01")!=-1)
            {
                //输出类名
                console.log(name);

                //根据类名获取java类
                var clz=Java.use(name);
                //获取类的所有方法
                var methods=clz.class.getDeclaredMethods();
                
                //循环输出所有方法
                for(var i=0;i<methods.length();i++)
                {
                    console.log(methods[i]);
                }
            }
            
        },
        //枚举完成以后调用
        onComplete:function()
        {

        }
    });  
    
    //枚举已经加载的类 同步方式
    var classes=Java.enumerateClassLoadersSync();
    for(var i=0;i<methods.classes();i++)
    {
        if(classes[i].indexOf("com.example.hookdemo01")!=-1)
        {
            console.log(classes[i]);
            //枚举方法同上...
        }
    }

  
}

在Frida中枚举所有已加载的需要用到enumerateLoadedClasses函数,在获取到所有类之后,再通过Java.use获取类,接着通过Java中的getDeclaredMethods函数,获取所有的函数名称,接着循环输出即可

hook动态加载的DexClass

对于一些使用DexClassLoader动态加载dex文件的apk来说,直接使用Java.user必然是找不到当前的类,因为当前apk的ClassLoader并不存在这个类。想要hook动态加载的类需要先解决ClassLoader的问题。


function hookTest7()
{
    //枚举ClassLoder
    Java.enumerateClassLoaders({
        onMatch:function(loader)
        {
            try 
            {
              //如果能找到需要hook的类
                if(loader.loadClass("com.example.hookdemo01.Student"))
                {
                    //替换当前的ClassLoader
                    Java.ClassFactory.loader=loader;

                    //实现hook代码
                    var inner=Java.use("com.example.hookdemo01.Student$innerClass");
                }   
            } catch (error) 
            {
                
            }   
        },

        onComplete:function()
        {

        }
    });
}

对于动态加载的类,Frida提供了enumerateClassLoaders函数来枚举ClassLoder,当枚举的ClassLoder中有需要Hook的类时,可以修改loader为当前的ClassLoader,然后再进行hook即可。

Native层hook

hook有导出函数

//hook有导出函数
function hookTest8()
{
    //so名称
    var so_name="libnative-lib.so";
    //要Hook的函数名
    var fun_name="test";

    //通过名称找到导出函数
    var add_fun=Module.findExportByName(so_name,fun_name);

    Interceptor.attach(add_fun,{
        //在hook函数之前执行
        onEnter:function(args)
        {
            //args[0],arg[1]
            console.log("hook enter");
        },
        //在hook函数之后执行
        onLeave:function(retval)
        {
            console.log("hook leaver");
        }

    });     
}

hook有导出函数直接通过导出函数名称找到函数地址即可进行hook

hook无导出函数

//hook无导出函数
function hookTest9()
{
    //so名称
    var so_name="libnative-lib.so";
    //要Hook的函数偏移
    var fun_off=0x7078;

   //加载到内存后,函数地址=so地址+函数偏移
   var so_add=Module.findBaseAddress(so_name);
   var add_func=parseInt(so_add,16)+fun_off;
   var ptr_fun=new NativePointer(add_func);

 
    Interceptor.attach(ptr_fun,{
        //在hook函数之前执行
        onEnter:function(args)
        {
            console.log("hook enter");
        },
        //在hook函数之后执行
        onLeave:function(retval)
        {
            console.log("hook leaver");
        }

    });     
}

未导出的函数我们需要手动的计算出函数地址,然后将其转化成一个NativePointer的对象然后进行hook操作

  • 1
    点赞
  • 0
    评论
  • 4
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值