Frida使用

准备-所有练习apk下载

1.一个简单固定的Frida写法

//定义一个名为fridaTest1的函数
function fridaTest1(){
    Java.perform(function(){
    
        console.log("测试-为所欲为区-这里执行所有的js逻辑");

    });
}

//延迟多少秒执行fridaTest1()函数-这里有个新手非常容易弄混的地方,就是其实setTimeout可有可无
//如果有就是把js注入手机的时候,马上执行fridaTest1()函数,如果没有,需要我们自己在命令行主动执行
//还有一个需要注意的是在命令行主动执行js脚本内的函数的时候,一定要有后面的括号()
setTimeout(fridaTest1,1000);//1000 = 1秒

 2.拦截普通方法,修改值并返回

//正向关键代码
//按钮1执行   我们需要用frida中hook这个函数
private void add(String num1, String num2) {
       double sum = number1 + number2;
}
//1.先通过Java.use函数得到MainActivity类            
    var Jfrida01 = Java.use("com.zero.frida01.MainActivity"); 
    
    //2.通过类名定位到add函数,通过implementation关键字访问和修改方法的具体实现
    //3.其中function(a,b)可以看作成add(a,b),function()括号里面参数是可变的,
    //具体个数和我们要修改的方法有关,如果add函数需要传3个参数,那么就是function(a,b,c)
    Jfrida01.add.implementation = function(a,b){   
        
    //4.为所欲为区,我们可以在这里做任何我们想做的事情,劫持,修改,打印数据为明文 

        console.log("打印传入的a的值 = " + a);     
        console.log("打印传入的b的值 = " + b);   
        
    //5.修改传入的数据

        a = "3";  //add('java.lang.String', 'java.lang.String')
        b = "9";  
    
    //6.使用原add方法,重新计算,相当于不管用户传入什么数据,我只执行3+9的命令  
	
        this.add(a,b);//此时apk内,不管输入框输入什么数据都是12        
                   
    //7.根据hook的函数是否有返回值,决定是否写return,如果有返回值不写会报错              
		//return res;  
	}                          

3.拦截构造函数

//正向代码
class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

Student student = new Student("小翼", 12);
//1.通过包名+类名获取User类对象            
	var JStudent = Java.use("com.zero.frida02.Student");  
            
  //2.和普通方法差不多,唯一区别就是用$init代替了方法名,注意$init格式固定,统一代指构造函数  
  //3.大家注意到没,下面的function(name,age),其实用function(a,b)也行,不影响,其他的也一样
            
    JStudent.$init.implementation = function(name,age){                 
                
    console.log("打印原始传入的name的值 = " + name);     
    console.log("打印原始传入的age的值 = " + age);   

   //4.修改值的时候要注意格式一样,不然会出错
    name = "小翼-hook修改";                               
    age  = 18;      


   //5.最终要执行原本的init方法否则运行时会报异常导致原程序无法正常运行。                    
    this.$init(name,age);               
	}

4.拦截方法重载

当原程序有几个相同方法名,但形参个数不同时,使用overload,如果不用会报错

//正向代码: printInfo方法名相同,传入的形参不同
public class StudentInfo {
    private String Sname;
    private int Iage;

    // 设置学生姓名和年龄
    public void setInfo(String name, int age) {
        Sname = name;
        Iage = age;
    }
    // 设置年龄
    public void setInfo(int age) {
        Sname = "无名者";
        Iage = age;
    }
}
 //1.通过包名+类名获取User类对象            
	var JStudent = Java.use("com.zero.frida04.StudentInfo");  
     
	//2.通过overload关键字重载定位 setInfo 方法
    JStudent.setInfo.overload('java.lang.String', 'int').implementation = function (name, age){                 
                
    console.log("打印原始传入的name的值 = " + name);     
    console.log("打印原始传入的age的值 = " + age);   

   //3.修改值的时候要注意格式一样,不然会出错
    name = "小翼-hook修改";                               
    age  = 18;      

   //4.执行原本的setInfo方法               
    this.setInfo(name, age);       
	}

5.主动调用静态函数和主动调用非静态函数的区别

ps:主动调用和hook不同,hook是程序逻辑执行到那才会执行,而主动调用则是程序逻辑即使不走那里,也会执行

主动调用在逆向中的运用:

  1. 跳过复杂逻辑:在某些程序中,可能存在复杂的验证或初始化过程,这些过程对于逆向分析来说可能是冗余的。通过主动调用,逆向工程师可以直接调用关键功能,跳过这些复杂逻辑,从而提高分析效率。

  2. 访问未公开接口:程序可能有一些内部函数或接口并未对外公开,但在逆向分析时,我们可能需要访问这些功能。通过主动调用,可以强制执行这些未公开的函数,以便更好地理解程序的工作原理。

  3. 调试困难代码:有些程序可能包含难以调试的代码,如硬件相关的操作、系统调用等。通过主动调用,可以在控制条件下执行这些代码,便于观察其行为和效果。

  4. 绕过保护措施:在一些加壳或加密的程序中,可能存在各种保护措施,如反调试、代码混淆等。通过主动调用,可以在一定程度上绕过这些保护措施,直接执行关键代码。

  5. 功能验证:在逆向某个功能时,通过主动调用可以验证我们对程序逻辑的理解是否正确。这有助于确保逆向分析的结果准确可靠。

  6. 模块化分析:通过主动调用,可以将程序拆分成多个模块进行独立分析。这样有助于降低分析的复杂度,使逆向工作更加有序。

  7. 自动化测试:在逆向过程中,可以通过编写脚本实现主动调用,从而自动化测试程序的不同功能,提高逆向分析的效率。

    //正向代码-
    private static String str = "初识文本,无修改";
    
     // 非静态成员函数
    private void SetNonStaticStr() {
        str = "非静态成员函数-主动调用";
    }

    // 静态成员函数
    private static void SetStaticStr() {
        str = "静态成员函数-主动调用";
    }

    //主动调用静态函数 
    //1.通过包名+类名获取User类对象            
	var MainActivity = Java.use("com.zero.frida05.MainActivity");  
     
	//2.不用和hook一样使用implementation等关键字,直接类名+方法名就行
    MainActivity.SetStaticStr();      
 // 使用 Java.choose 来枚举所有活动的 MainActivity 实例  这里选择用choose是因为这个程序打开的界面就是MainActivity,
// 属于是系统已经帮我们实例化好了,直接拿来用就行,所以选择用choose,如果没有系统帮忙实例化,需要自己用其他方法
    Java.choose('com.zero.frida05.MainActivity', {
        onMatch: function (instance) {
            // 当找到一个实例时,调用它的非静态方法
            instance.SetNonStaticStr();
        },
        onComplete: function () {
            // 当所有实例都被枚举完毕时调用,这里一般不用,但一定要有
            console.log("Finished enumrating instances");
        }
	 });

6.主动调用静态成员和主动调用非静态成员的区别

//正向代码
private static String str1 = "静态初始文本,无修改";
private String str2 = "非静态初始文本,无修改";
//逆向代码
 var MainActivity = Java.use("com.zero.frida06.MainActivity");
   //访问或者修改成员的时候.value 一定要有,非常关键!!!
   MainActivity.str1.value = "主动修改静态成员";
//逆向代码
//使用现有实例
    Java.choose('com.zero.frida06.MainActivity', {
        onMatch: function (instance) {
			 //访问或者修改成员的时候.value 一定要有,非常关键!!!
            instance.str2.value = "主动修改非静态成员";
        },
        onComplete: function () {
            console.log("Finished enumrating instances");
        }
	 });

7.hook导出函数和非导出函数

①确定手机架构,获取对应so,以及要hook的函数-IDA定位拿到函数名

adb shell getprop ro.product.cpu.abi

//正向代码
// 声明 native 方法-都是导出函数-JAVA层
//需要注意的是mul函数会调用so里面的非导出函数,因为安卓不能直接调用非导出函数
    public native int add(int num1, int num2);
    public native int mul(int num1, int num2);

//底下都为c或者C++内的函数
extern "C"
JNIEXPORT jint JNICALL
Java_com_zero_frida07_MainActivity_add(JNIEnv *env, jobject thiz, jint num1, jint num2) {
    return num1 + num2;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_zero_frida07_MainActivity_mul(JNIEnv *env, jobject thiz, jint num1, jint num2) {
    return multiply(num1,num2);
}

jint multiply(jint num1, jint num2) {
    return num1 * num2;
}
//hoo导出函数 
// 1.指定要Hook的so文件名和要Hook的函数名,函数名即IDA导出表中显示的函数名 		
    var nativePointer = Module.findExportByName("libfrida07.so", "Java_com_zero_frida07_MainActivity_add"); // 查找so库中的导出函数
    // 2.使用 Interceptor.attach 挂钩到这个函数
    Interceptor.attach(nativePointer, { 
        onEnter: function(args){ 
           // 函数被调用时执行的代码
           // args 是一个包含函数参数的数组args[0] args[1]是系统相关,一般不用管,            
           //args[2]args[3]之后的都是我们传入的参数
            console.log("args[0] = " + args[0]); 
            console.log("args[1] = " + args[1]);
            console.log("args[2] = " + args[2].toInt32()); 
            console.log("args[3] = " + args[3].toInt32());
        }, 
        onLeave: function(retval){ 
			 // 函数即将返回时执行的代码
            // retval 是函数的返回值
			// 你可以修改返回值,例如:retval.replace(1234);
            console.log("返回值修改前 = " + retval.toInt32()); 
            retval.replace(10000); 
            console.log("返回值修改后 = " + retval.toInt32()); 
        } 
    });
//逆向js代码-非导出函数
  // 1.获取目标模块的基地址
    var base_address = Module.findBaseAddress('libfrida07.so');
	// 2.寻找非导出函数的偏移量,通常通过分析 IDA Pro、Ghidra 或其他逆向工程工具获得
        var offset = 0x238A0; // 替换 0x12345 为你的函数偏移量
		  // 3.计算函数的绝对地址,有时候会因为某些情况offset + 1 或者+n 例如:Thumb指令集
        var function_address = base_address.add(offset);

        // 使用 Interceptor API 来 hook 函数-之后和导出函数差不多
		//唯一区别就是非导出函数需要自己修正地址
        Interceptor.attach(function_address, {
            // 在函数执行前调用
            onEnter: function (args) {
              //和非导出函数差不多
			   console.log("非导出函数-args[2] = " + args[2].toInt32()); 
			   console.log("非导出函数-args[3] = " + args[3].toInt32());
            },
            // 在函数执行后调用
            onLeave: function (retval) {
				retval.replace(10088);
            }
        });

8.主动调用so内的函数-

关键字:

①findExportByName详解

②NativeFunction详解

    //libc.so是系统so库只要具备so库的apk,基本都会有libc.so这个so库
    var fopen_addr = Module.findExportByName("libc.so", "fopen");
    var fopen = new NativeFunction(fopen_addr, "pointer", ["pointer", "pointer"]);
    var fputs_addr = Module.findExportByName("libc.so", "fputs");
    var fputs = new NativeFunction(fputs_addr, "int", ["pointer", "pointer"]);
    var fclose_addr = Module.findExportByName("libc.so", "fclose");
    var fclose = new NativeFunction(fclose_addr, "int", ["pointer"]);

    var filename = Memory.allocUtf8String("/sdcard/frida08.txt");
    var mod = Memory.allocUtf8String("a");
    var neirong = Memory.allocUtf8String("frida练习\r\n这是第二行,恭喜逆向成功\r\b");

    var fp = fopen(filename, mod);
    fputs(neirong, fp);
    fclose(fp);

9.打印函数的调用栈-Java与So层

//Java调用栈 
//1.获取MainActivity类
    var MainActivity = Java.use('com.zero.frida09.MainActivity');
    //2.挂钩z函数
    MainActivity.z.implementation = function () {
    //3.通过Exception打印当前调用栈
      
	var Exception = Java.use("java.lang.Exception");
    var instance = Exception.$new("print_stack");
    var stack = instance.getStackTrace();
        console.log(stack);
		
	//4.只要调用了$new,就应该调用这个来释放掉	
        instance.$dispose();
	  
    //5.调用原始的z函数实现
        this.z();
	//6.打印堆栈信息后用文本工具打开,(\r)代替(,)可以让调用信息看的更舒服ps:search mode->extended	
	 };
//so调用栈
var base_address = Module.findBaseAddress('libfrida09.so');
	var offset = 0x23AB0;
	var function_address = base_address.add(offset);
	 Interceptor.attach(function_address, {
            // 在函数执行前调用
            onEnter: function (args) {
			   console.log("\r\n=============================" + " Stack int =======================\r\n"); 
			   console.log("base_address:", base_address);//获得地址要减去base_address基址
			   console.log(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n'));
			   console.log("\r\n=============================" + " Stack out =======================\r\n");
            },
            // 在函数执行后调用
            onLeave: function (retval) {
				
            }
        });

### 使用 Frida Attach 模式进行动态分析或调试 Frida 是一种强大的工具,允许开发者和安全研究人员在运行的应用程序上下文中执行 JavaScript 或 Python 脚本。Attach 模式特别适用于已经启动并正在运行的目标进程。 #### 启动目标应用并与之连接 为了使用 attach 模式,在命令行界面输入 `frida-attach` 命令后面跟上要附加的目标进程名称或 PID (进程 ID)[^1]: ```bash frida-attach <process_name_or_pid> ``` 如果希望指定脚本来加载,则可以这样做: ```bash frida-attach -l script.js <process_name_or_pid> ``` 这会告诉 Frida 将给定的 JavaScript 文件注入到选定进程中去。 #### 编写用于动态分析的脚本 编写一个简单的 JavaScript 来展示如何拦截函数调用。下面的例子展示了怎样挂钩 Android 设备上的 Java 方法: ```javascript Java.perform(function () { var MainActivity = Java.use('com.example.app.MainActivity'); // Hook the 'onCreate' method of MainActivity class. MainActivity.onCreate.implementation = function (savedInstanceState) { console.log('[*] onCreate() got called!'); this.onCreate(savedInstanceState); // Call original implementation. }; }); ``` 这段代码会在每次应用程序的主要活动被创建时打印一条消息至控制台。 对于 iOS 应用来说, 可以采用类似的策略来 hook Objective-C/C++ 的方法. #### 实现更复杂的交互逻辑 当需要实现更加复杂的功能比如修改参数值、绕过某些验证机制或是获取敏感数据的时候,可以通过调整上述例子中的 JavaScript 代码完成更多操作。例如改变传入参数或者返回不同的结果等。 需要注意的是,在实际环境中应当合法合规地运用此类技术手段,并遵循道德准则以及法律法规的要求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值