frida hook android

news/2025/10/26 18:04:41/文章来源:https://www.cnblogs.com/msjs/p/19167104

因为大版本更新的原因,导致版本17之前的教程几乎都没有用了,所以现在写一个

我目前的版本是17.4.0

里面的题目来自于https://github.com/DERE-ad2001/Frida-Labs

一,指令:

frida-ps -Uai:


frida-ps : 这将显示有关 Android 设备上运行进程的信息。
-U : 此选项用于列出 USB 连接设备(物理设备或模拟器)上的进程。
-a : 此选项用于列出所有进程,而不仅仅是当前用户拥有的进程。
-i : 此选项用于包含每个进程的详细信息,例如进程 ID(PID)和进程名称。

我一般用这个来看包名和pid


frida -U -f <package_name>

附加应用进程

例如

frida -U -f com.ad2001.frida0x1 -l ./abc.js

参数详解

-U(USB)

  • 作用:连接到通过 USB 连接的设备
  • 说明:表示使用 USB 连接方式,通常用于连接 Android 手机或 iOS 设备
  • 替代选项
    • -D:连接到指定设备 ID
    • -R:连接到远程设备

-f(spawn)

  • 作用:启动指定的应用程序
  • 格式-f <包名>
  • 说明com.ad2001.frida0x1是目标应用的包名,Frida 会重新启动这个应用并注入脚本
  • 特点:从应用启动时就注入,可以捕获到应用的完整生命周期

-l(load)

  • 作用:加载指定的 JavaScript 脚本文件
  • 格式-l <脚本路径>
  • 说明./abc.js是你要注入的 Frida 脚本文件路径

cat /proc/<pid>/maps | grep '\.so'

查找软件调用的so包及其地址

可以修改\.so'来只输出特定的so库

例如

:/ # cat /proc/10169/maps | grep 'liba0x9\.so'
71f0f71d8000-71f0f71d9000 r-xp 00000000 08:23 2883799                    /data/app/~~hUUSBr8r9jj6H61oJ58Q3g==/com.ad2001.a0x9-lEXjMqmljssnJwl8opW1JA==/lib/x86_64/liba0x9.so
71f0f71d9000-71f0f71da000 r--p 00000000 08:23 2883799                    /data/app/~~hUUSBr8r9jj6H61oJ58Q3g==/com.ad2001.a0x9-lEXjMqmljssnJwl8opW1JA==/lib/x86_64/liba0x9.so

二,hook模板

1.函数替换

模板

Java.perform(function() {var <class_reference> = Java.use("<package_name>.<class>");<class_reference>.<method_to_hook>.implementation = function(<args>) {/*OUR OWN IMPLEMENTATION OF THE METHOD*/}})

Java.perform 是 Frida 中的一个函数,用于为您的脚本创建一个特殊的上下文,以与 Android 应用中的 Java 代码进行交互。

var <class_reference> = Java.use("<package_name>.<class>");

变量 <class_reference> 以表示目标 Android 应用中的 Java 类。您指定要与 Java.use 函数一起使用的类,该函数将类名作为参数。 <package_name> 表示 Android 应用的包名,而 <class> 表示您想要交互的类。<class_reference>可以随意命名例如a

<class_reference>.<method_to_hook>.implementation = function(<args>) {}

使用 <class_reference>.<method_to_hook> 符号来指定要钩取的方法。这是您可以定义自己逻辑的地方,当钩取的方法被调用时将执行该逻辑。 <args> 表示传递给函数的参数。

implementation是替换函数,替换为function后面定义的

例子

例如,我有一个包名为com.ad2001.frida0x1的程序

image

我要修改的是MainActivity类的get_random函数

image

那我的脚本就应该这样写:

Java.perform(function() {var a = Java.use("com.ad2001.frida0x1.MainActivity");a.get_random.implementation = function() {console.log("get_random is hooked")}
})

这个脚本的含义为如果hook住,就打印get_random is hooked

我把这个脚本命名为adb.js

然后我们这样运行,就完成了hook

frida -U -f com.ad2001.frida0x1 -l ./abc.js

2.函数定向hook

假设原始类中有多个重载方法,直接使用implementation就不行了,必须用overload来指定

例如

Java.perform(function() {var a = Java.use("com.ad2001.frida0x1.MainActivity");a.check.overload('int' ,'int').implementation = function(a,b) {console.log("First parameter:"+a)console.log("Sencond parameter:"+b)this.check(a,b)}})

在这个脚本中,指定了输入两个int变量的函数check

3.运行函数

这里有个代码,但没有任何引用这个函数的函数

public class MainActivity extends AppCompatActivity {static TextView t1;public static void get_flag(int a) {if(a == 4919) {try {SecretKeySpec secretKeySpec0 = new SecretKeySpec("HILLBILLWILLBINN".getBytes(), "AES");Cipher.getInstance("AES/CBC/PKCS5Padding").init(2, secretKeySpec0, new IvParameterSpec(new byte[16]));MainActivity.t1.setText("FLAG{BABY_HOOKS_0x2}");}catch(Exception e) {e.printStackTrace();}}}@Override  // androidx.fragment.app.FragmentActivityprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);this.setContentView(layout.activity_main);MainActivity.t1 = (TextView)this.findViewById(id.textview);}
}

如果我们想要获得flag,直接看就好了(JEB神力,用jadx就不是这样的了)

要想办法运行这个函数

直接frida运行这个脚本就行了

Java.perform(function() {var a = Java.use("com.ad2001.frida0x2.MainActivity");a.get_flag(4919);
})

在use后,直接调用那个函数

4.修改变量

先use,在后面直接a.变量名.value即可

a.code.value

5.创建实例

这里有一个类,但没有被创建实例,所以没法直接运行

image

这里要使用$new()方法来创建实例,然后运行

代码

Java.perform(function() {var a = Java.use("com.ad2001.frida0x4.Check");var a_obj = a.$new();var flag = a_obj.get_flag(0x539);console.log(flag);
})

6.创建MainActivity的实例

这里有个没有创建实例的

public class MainActivity extends AppCompatActivity {TextView t1;public void flag(int code) {if(code == 0x539) {try {SecretKeySpec secretKeySpec0 = new SecretKeySpec("WILLIWOMNKESAWEL".getBytes(), "AES");Cipher.getInstance("AES/CBC/PKCS5Padding").init(2, secretKeySpec0, new IvParameterSpec(new byte[16]));this.t1.setText("FRIDA{ON_MATCH_THIS_INSTANCE}");}catch(Exception e) {e.printStackTrace();}}}@Override  // androidx.fragment.app.FragmentActivityprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);this.setContentView(layout.activity_main);this.t1 = (TextView)this.findViewById(id.textview);}
}

如果我们像之前一样创建实例然后运行就会报错

[LGE AN10::com.ad2001.frida0x5 ]-> Java.perform(function() {var a = Java.use("com.ad2001.frida0x5.MainActivity");var a_obj = a.$new();a.flag(0x539);
})
Error: java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()at <anonymous> (/frida/bridges/java.js:1)at value (/frida/bridges/java.js:8)at e (/frida/bridges/java.js:8)at apply (native)at value (/frida/bridges/java.js:8)at e (/frida/bridges/java.js:8)at <anonymous> (<input>:4)at <anonymous> (/frida/bridges/java.js:1)at perform (/frida/bridges/java.js:8)at <eval> (<input>:6)at eval (native)at <anonymous> (/frida/repl/agent.js:1)at i (/frida/repl/agent.js:1)at fridaEvaluateExpression (/frida/repl/agent.js:1)at call (native)at handleRpcMessage (/frida/runtime/message-dispatcher.js:39)at handleMessage (/frida/runtime/message-dispatcher.js:25)

这个的原因相对复杂。大概就是frida创建的这个特殊MainActivity实例不在运行环境内,所以不行

这是全部解释:
直接使用 Frida 创建 MainActivity 或任何 Android 组件的实例可能会很棘手,因为 Android 的生命周期和线程规则。Android 组件,如 Activity 子类,依赖于应用程序上下文以正确运行。在 Frida 中,您可能缺少所需的上下文。Android UI 组件通常需要一个关联的 Looper 的特定线程。如果您正在处理 UI 任务,请确保您在主线程上,并且有一个活动的 Looper 。活动是更大 Android 应用程序生命周期的一部分。创建 MainActivity 的实例可能需要应用处于特定状态,而通过 Frida 管理整个生命周期可能并不简单。总之,为 MainActivity 创建实例并不是一个好主意。

解决

这里要用到一个新的模板

Java.choose

Java.performNow(function() {Java.choose('<Package>.<class_Name>', {onMatch: function(instance) {// TODO},onComplete: function() {}});
});

1. Java.performNow(function() { ... })

  • 作用:确保代码在 Java 运行时环境中执行(Frida 需要附加到 JVM)。
  • 细节
    • Java.performNow(或更常见的 Java.perform)是 Frida 的入口点,用于在目标进程的 Java 线程中安全执行代码。
    • 如果当前线程未附加到 JVM,它会自动附加并执行回调函数。

2. Java.choose('<Package>.<class_Name>', { ... })

  • 作用:扫描内存中目标类的所有存活实例
  • 参数说明
    • '<Package>.<class_Name>':目标类的完整名称(例如 com.example.MyClass)。
    • 回调对象:包含 onMatchonComplete两个回调函数。

3. onMatch: function(instance) { ... }

  • 作用:每找到一个目标类的实例,就调用此回调函数。
  • 参数
    • instance 参数表示目标类的每个匹配实例。您可以使用任何您想要的名称
  • 用途
    • 在此函数内操作实例(如调用方法、修改字段)。
    • 示例:

onMatch: function(instance) {console.log("Found instance: " + instance);instance.myMethod(); // 调用实例的方法instance.field.value = 123; // 修改字段值
}

4. onComplete: function() { ... }

  • 作用:当所有实例遍历完成后调用。可选,不一定要有
  • 用途
    • 用于执行清理操作或通知搜索结束。
    • 示例:

onComplete: function() {console.log("Instance search completed.");
}

所以创建main实例的hook脚本是这样的

Java.performNow(function() {Java.choose('com.ad2001.frida0x5.MainActivity', {onMatch: function(instance) {console.log("instance: " + instance);//可以没有instance.flag(0x539);},onComplete: function() {}});
});
[LGE AN10::com.ad2001.frida0x5 ]-> Java.performNow(function() {Java.choose('com.ad2001.frida0x5.MainActivity', {onMatch: function(instance) {console.log("instance: " + instance);instance.flag(0x539);},onComplete: function() {}});
});
instance: com.ad2001.frida0x5.MainActivity@3804e07
instance: com.ad2001.frida0x5.MainActivity@91f8bf4
instance: com.ad2001.frida0x5.MainActivity@4654592
[LGE AN10::com.ad2001.frida0x5 ]->

image

7,挂钩构造函数

用于在实例被创造的时候修改函数

模板

Java.perform(function() {var <class_reference> = Java.use("<package_name>.<class>");<class_reference>.$init.implementation = function(<args>){/**/}
});

这里有个MainActivity没有被实例化

package com.ad2001.frida0x7;import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;public class MainActivity extends AppCompatActivity {TextView t1;public void flag(Checker A) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {if(A.num1 > 0x200 && 0x200 < A.num2) {Cipher.getInstance("AES").init(2, new SecretKeySpec("MySecureKey12345".getBytes(), "AES"));this.t1.setText("FRIDA{HOOKING_CONSTRUCTORS}");}}@Override  // androidx.fragment.app.FragmentActivityprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);this.setContentView(layout.activity_main);this.t1 = (TextView)this.findViewById(id.textview);Checker checker0 = new Checker(0x7B, 321);try {this.flag(checker0);}catch(NoSuchPaddingException e) {throw new RuntimeException(e);}catch(NoSuchAlgorithmException e) {throw new RuntimeException(e);}catch(InvalidKeyException e) {throw new RuntimeException(e);}catch(IllegalBlockSizeException e) {throw new RuntimeException(e);}catch(BadPaddingException e) {throw new RuntimeException(e);}}
}

Checker类也没有

package com.ad2001.frida0x7;public class Checker {int num1;int num2;Checker(int a, int b) {this.num1 = a;this.num2 = b;}
}

这时有两种hook方法

第一种

Java.performNow(function() {Java.choose('com.ad2001.frida0x7.MainActivity', {onMatch: function(instance) {console.log("Instance found");var checker = Java.use("com.ad2001.frida0x7.Checker");var checker_obj  = checker.$new(600, 600); // Class Objectinstance.flag(checker_obj); // invoking the get_flag method},onComplete: function() {}});
});

第二种

也是就挂钩构造函数

在实例被创造的时候赋值

Java.perform(function() {var a =  Java.use("com.ad2001.frida0x7.Checker");a.$init.implementation = function(param){this.$init(999,999);}
});

8.hook so库静态函数

不是hook so cool

模板

Interceptor.attach(targetAddress, {onEnter: function (args) {console.log('Entering ' + functionName);// Modify or log arguments if needed},onLeave: function (retval) {console.log('Leaving ' + functionName);// Modify or log return value if needed}
});
  • Interceptor.attach : 将回调附加到指定函数地址。 targetAddress 应该是我们想要钩取的本地函数的地址。
    onEnter : 当钩住的函数被调用时,会调用此回调。它提供对函数参数的访问 ( args )。
  • to the return value (retval).
    onLeave : 当钩住的函数即将退出时,会调用此回调。它提供对返回值的访问 ( retval )。

既然关键是地址,那地址怎么找呢?

Process.getModuleByName("模块名").enumerateExports()

Process.getModuleByName("模块名").getExportByName()

Process.getModuleByName("模块名").findExportByName()

Process.getModuleByName("模块名").base

Process.getModuleByName("模块名").enumerateImports()

例子

public class MainActivity extends AppCompatActivity {private ActivityMainBinding binding;Button btn;EditText edt;static {System.loadLibrary("frida0x8");}public native int cmpstr(String arg1) {}@Override  // androidx.fragment.app.FragmentActivityprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityMainBinding activityMainBinding0 = ActivityMainBinding.inflate(this.getLayoutInflater());this.binding = activityMainBinding0;this.setContentView(activityMainBinding0.getRoot());this.edt = (EditText)this.findViewById(id.editTextText);Button button0 = (Button)this.findViewById(id.button);this.btn = button0;button0.setOnClickListener(new View.OnClickListener() {@Override  // android.view.View$OnClickListenerpublic void onClick(View v) {String s = MainActivity.this.edt.getText().toString();if(MainActivity.this.cmpstr(s) == 1) {Toast.makeText(MainActivity.this, "YEY YOU GOT THE FLAG " + s, 1).show();return;}Toast.makeText(MainActivity.this, "TRY AGAIN", 1).show();}});}
}

接受一个输入,放入cmpstr()函数中,然后判断输出是否为1

而这个函数却是个本地静态函数

 public native int cmpstr(String arg1) {}

所以看so库

一个相当简单逆向,直接写脚本或者动调都能得到,不过为了学frida,那就用frida

_BOOL8 __fastcall Java_com_ad2001_frida0x8_MainActivity_cmpstr(__int64 a1, __int64 a2, __int64 a3)
{int v4; // [rsp+30h] [rbp-C0h]int i; // [rsp+34h] [rbp-BCh]char *s1; // [rsp+40h] [rbp-B0h]char s2[104]; // [rsp+80h] [rbp-70h] BYREFunsigned __int64 v9; // [rsp+E8h] [rbp-8h]v9 = __readfsqword(0x28u);s1 = (char *)_JNIEnv::GetStringUTFChars(a1, a3, 0LL);for ( i = 0; i < (unsigned __int64)__strlen_chk("GSJEB|OBUJWF`MBOE~", -1LL); ++i )s2[i] = aGsjebObujwfMbo[i] - 1;s2[__strlen_chk("GSJEB|OBUJWF`MBOE~", -1LL)] = 0;v4 = strcmp(s1, s2);__android_log_print(3LL, "input ", &unk_6B0, s1);__android_log_print(3LL, "Password", &unk_6B0, s2);_JNIEnv::ReleaseStringUTFChars(a1, a3, s1);return v4 == 0;
}

先附加frida到app上,然后我们的目的是hook strcmp函数,获取两个参数的内容

经过简单分析,可以得知s1是我们输入的内容,s2是flag

那我们该如何做呢?

const libc = Process.getModuleByName("libc.so");
const strcmp = libc.getExportByName("strcmp");
console.log("strcmp address: " + strcmp);
Interceptor.attach(strcmp, {onEnter: function(args) {var str1 = args[0].readUtf8String();var str2 = args[1].readUtf8String();if (str1 && str1.includes("hello")) {console.log("strcmp: '" + str1 + "' vs '" + str2 + "'");}},onLeave: function(retval) {// 可选:处理返回值}
});

Process.getModuleByName是获取库的地址

getExportByName是获取函数的地址

args[0].readUtf8String();读取第一个参数转为字符串

中间使用if是因为如果不加判断,那只要一调用就弹出信息,而这个函数被调用的实在是太多了

9.修改native返回值

这里有一个check_flag native函数,如果这个返回值为0x539,就输出flag,我们来看一看这个东西

public class MainActivity extends AppCompatActivity {private ActivityMainBinding binding;Button btn;static {System.loadLibrary("a0x9");}public native int check_flag() {}@Override  // androidx.fragment.app.FragmentActivityprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityMainBinding activityMainBinding0 = ActivityMainBinding.inflate(this.getLayoutInflater());this.binding = activityMainBinding0;this.setContentView(activityMainBinding0.getRoot());Button button0 = (Button)this.findViewById(id.button);this.btn = button0;button0.setOnClickListener(new View.OnClickListener() {@Override  // android.view.View$OnClickListenerpublic void onClick(View v) {Cipher cipher0;if(MainActivity.this.check_flag() == 0x539) {try {cipher0 = Cipher.getInstance("AES");}catch(NoSuchAlgorithmException e) {throw new RuntimeException(e);}catch(NoSuchPaddingException e) {throw new RuntimeException(e);}SecretKeySpec secretKeySpec0 = new SecretKeySpec("3000300030003003".getBytes(), "AES");try {cipher0.init(2, secretKeySpec0);}catch(InvalidKeyException e) {throw new RuntimeException(e);}Base64.getDecoder().decode("hBCKKAqgxVhJMVTQS8JADelBUPUPyDiyO9dLSS3zho0=");Toast.makeText(MainActivity.this.getApplicationContext(), "You won FRIDA{NATIVE_LAND_0x2}", 1).show();return;}Toast.makeText(MainActivity.this.getApplicationContext(), "Try again", 1).show();}});}
}

这个函数永远返回1

__int64 Java_com_ad2001_a0x9_MainActivity_check_1flag()
{return 1LL;
}

我们先附加一下

然后查看地址

[LGE AN10::com.ad2001.a0x9 ]-> var fr09 = Process.getModuleByName('liba0x9.so');
console.log(fr09.base);
Error: unable to find module 'liba0x9.so'at value (/frida/runtime/core.js:315)at <eval> (<input>:1)
[LGE AN10::com.ad2001.a0x9 ]->

找不到库

我们再试试用maps

:/ # cat /proc/9134/maps |grep 'liba0x9\.so'
1|:/ #

也找不到,这时只能改包了

将这一行改为true

image

然后重新安装

然后我们就能用maps找到了

/ # cat /proc/10169/maps | grep 'liba0x9\.so'
71f0f71d8000-71f0f71d9000 r-xp 00000000 08:23 2883799                    /data/app/~~hUUSBr8r9jj6H61oJ58Q3g==/com.ad2001.a0x9-lEXjMqmljssnJwl8opW1JA==/lib/x86_64/liba0x9.so
71f0f71d9000-71f0f71da000 r--p 00000000 08:23 2883799                    /data/app/~~hUUSBr8r9jj6H61oJ58Q3g==/com.ad2001.a0x9-lEXjMqmljssnJwl8opW1JA==/lib/x86_64/liba0x9.so

然而frida仍然找不到,不过已经知道地址了,也可正常用了

脚本

var fr09 = 0x71f0f71d8000;
var offset =0x670;
var get_flag = ptr(fr09 + offset);
console.log("get_flag address: " + get_flag);
Interceptor.attach(get_flag, {onEnter: function(args) {},onLeave: function(retval) {console.log("retval: " + retval);retval.replace(0x539);console.log('retval:'+retval);}
});

这个 0x71f0f71d8000;就是上面maps看到的库的起始地址

这个 offset =0x670;就是在IDA中看到函数的偏移地址

.text:0000000000000670 ; Attributes: bp-based frame
.text:0000000000000670
.text:0000000000000670                 public Java_com_ad2001_a0x9_MainActivity_check_1flag
.text:0000000000000670 Java_com_ad2001_a0x9_MainActivity_check_1flag proc near
.text:0000000000000670                                         ; DATA XREF: LOAD:0000000000000358↑o
.text:0000000000000670
.text:0000000000000670 var_14          = dword ptr -14h
.text:0000000000000670 var_10          = qword ptr -10h
.text:0000000000000670 var_8           = qword ptr -8
.text:0000000000000670
.text:0000000000000670 ; __unwind {
.text:0000000000000670                 push    rbp
.text:0000000000000671                 mov     rbp, rsp
.text:0000000000000674                 mov     [rbp+var_8], rdi
.text:0000000000000678                 mov     [rbp+var_10], rsi
.text:000000000000067C                 mov     [rbp+var_14], 1
.text:0000000000000683                 mov     eax, [rbp+var_14]
.text:0000000000000686                 pop     rbp
.text:0000000000000687                 retn
.text:0000000000000687 ; } // starts at 670
.text:0000000000000687 Java_com_ad2001_a0x9_MainActivity_check_1flag endp
.text:0000000000000687
.text:0000000000000687 _text           ends
.text:0000000000000687

ptr()是把数字变成指针

retval.replace(0x539);修改返回值

10.hook未调用的native函数

这里并没有调用

@Metadata(d1 = {"\u0000&\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\u000E\n\u0002\b\u0002\u0018\u0000 \u000B2\u00020\u0001:\u0001\u000BB\u0005¢\u0006\u0002\u0010\u0002J\u0012\u0010\u0005\u001A\u00020\u00062\b\u0010\u0007\u001A\u0004\u0018\u00010\bH\u0014J\t\u0010\t\u001A\u00020\nH\u0086 R\u000E\u0010\u0003\u001A\u00020\u0004X\u0082.¢\u0006\u0002\n\u0000¨\u0006\f"}, d2 = {"Lcom/ad2001/frida0xa/MainActivity;", "Landroidx/appcompat/app/AppCompatActivity;", "()V", "binding", "Lcom/ad2001/frida0xa/databinding/ActivityMainBinding;", "onCreate", "", "savedInstanceState", "Landroid/os/Bundle;", "stringFromJNI", "", "Companion", "app_debug"}, k = 1, mv = {1, 8, 0}, xi = 0x30)
public final class MainActivity extends AppCompatActivity {@Metadata(d1 = {"\u0000\f\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002¨\u0006\u0003"}, d2 = {"Lcom/ad2001/frida0xa/MainActivity$Companion;", "", "()V", "app_debug"}, k = 1, mv = {1, 8, 0}, xi = 0x30)public static final class Companion {private Companion() {}public Companion(DefaultConstructorMarker defaultConstructorMarker0) {}}public static final Companion Companion;private ActivityMainBinding binding;static {MainActivity.Companion = new Companion(null);System.loadLibrary("frida0xa");}@Override  // androidx.fragment.app.FragmentActivityprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityMainBinding activityMainBinding0 = ActivityMainBinding.inflate(this.getLayoutInflater());Intrinsics.checkNotNullExpressionValue(activityMainBinding0, "inflate(layoutInflater)");this.binding = activityMainBinding0;ActivityMainBinding activityMainBinding1 = null;if(activityMainBinding0 == null) {Intrinsics.throwUninitializedPropertyAccessException("binding");activityMainBinding0 = null;}this.setContentView(activityMainBinding0.getRoot());ActivityMainBinding activityMainBinding2 = this.binding;if(activityMainBinding2 == null) {Intrinsics.throwUninitializedPropertyAccessException("binding");}else {activityMainBinding1 = activityMainBinding2;}CharSequence charSequence0 = this.stringFromJNI();activityMainBinding1.sampleText.setText(charSequence0);}public final native String stringFromJNI() {}
}

IDA中可以看到一个名为get_flag的函数

unsigned __int64 __fastcall get_flag(int a1, int a2)
{int i; // [rsp+Ch] [rbp-44h]char v4[24]; // [rsp+30h] [rbp-20h] BYREFunsigned __int64 v5; // [rsp+48h] [rbp-8h]v5 = __readfsqword(0x28u);if ( a2 + a1 == 3 ){for ( i = 0; i < (unsigned __int64)__strlen_chk("FPE>9q8A>BK-)20A-#Y", -1LL); ++i )v4[i] = 2 * i + aFpe9q8aBk20aY[i];v4[19] = 0;__android_log_print(3LL, "FLAG", "Decrypted Flag: %s", v4);}return __readfsqword(0x28u);
}

这是又有一个新的模板

模板

var native_adr = new NativePointer(<address_of_the_native_function>);
const native_function = new NativeFunction(native_adr, '<return type>', ['argument_data_type']);
native_function(<arguments>);

我们先附加和查找库的地址

/ # cat /proc/11102/maps | grep 'libfrida0xa.so'
71f0f5f54000-71f0f5f99000 r-xp 00000000 08:23 2883988                    /data/app/~~1zaEHlFt48C0MWU5A8AwHQ==/com.ad2001.frida0xa-I0MdndM9anwyj-E60VhaRQ==/lib/x86_64/libfrida0xa.so
71f0f5f99000-71f0f5f9d000 r--p 00044000 08:23 2883988                    /data/app/~~1zaEHlFt48C0MWU5A8AwHQ==/com.ad2001.frida0xa-I0MdndM9anwyj-E60VhaRQ==/lib/x86_64/libfrida0xa.so
71f0f5f9d000-71f0f5f9e000 rw-p 00047000 08:23 2883988                    /data/app/~~1zaEHlFt48C0MWU5A8AwHQ==/com.ad2001.frida0xa-I0MdndM9anwyj-E60VhaRQ==/lib/x86_64/libfrida0xa.so

函数偏移地址

:00000000000206B0 ; unsigned __int64 __fastcall get_flag(int, int)
.text:00000000000206B0                 public _Z8get_flagii
.text:00000000000206B0 _Z8get_flagii   proc near               ; DATA XREF: LOAD:0000000000001740↑o

脚本

var fr0a = 0x71f0f5f54000;
var offset =0x206B0;
var get_flag_ptr = new NativePointer(ptr(fr0a + offset));
const get_flag = new NativeFunction(get_flag_ptr, 'void', ['int','int']);
get_flag(1,2);

native 层要创建NativeFunction对象其参数包括地址,返回类型,和参数类型

运行后在logcat就能看到

adb logcat

11.修改native汇编

这里有个本地函数,使用ida看什么也没有

void Java_com_ad2001_frida0xb_MainActivity_getFlag()
{;
}

但看汇编就知道因为条件是永假的,所以IDA优化了

我们先修改一下,展示一下逻辑

image

void Java_com_ad2001_frida0xb_MainActivity_getFlag()
{char *v0; // [rsp+8h] [rbp-48h]unsigned __int64 i_1; // [rsp+10h] [rbp-40h]unsigned __int64 i; // [rsp+20h] [rbp-30h]i_1 = __strlen_chk("j~ehmWbmxezisdmogi~Q", -1LL);v0 = (char *)operator new[](i_1 + 1);for ( i = 0LL; i < i_1; ++i )v0[i] = aJEhmwbmxezisdm[i] ^ 0x2C;v0[i] = 0;__android_log_print(3LL, "FLAG :", "%s", v0);if ( v0 )operator delete[](v0);
}

我们只要把那个JNZ改为JZ就可以让函数正常

脚本

var fr0b = 0x71f0f7305000;
var offset =0x170CE;
var get_flag_ptr = ptr(fr0b + offset);
Memory.protect(get_flag_ptr, 1, 'rwx');
const original = get_flag_ptr.readU8();//读取当前的字节
console.log("[*] Original byte: 0x" + original.toString(16));
get_flag_ptr.writeU8(0x74);

Memory.protect(get_flag_ptr, 1, 'rwx');这个指令是临时修改内存为可修改的

1是要修改的长度,可以多,不可以少,'rwx'是权限

也可以这样写,Memory.patchCode的机制更全,更可读

var fr0b = 0x71f0f7305000;
var offset =0x170CE;
var get_flag_ptr = ptr(fr0b + offset);
Memory.patchCode(get_flag_ptr, 1, code => {var original = code.readU8();console.log("[*] Original byte: 0x" + original.toString(16));code.writeU8(0x75);
});

三,函数总结

以下是ai生成,我让ai总结了这里使用的每一个函数,方便回顾记录

1. Java.perform

函数名: Java.perform

参数: function (回调函数)

返回值:

作用: 确保代码在Java运行时环境中执行,为脚本创建特殊上下文以与Android应用中的Java代码交互

2. Java.use

函数名: Java.use

参数: string (类名)

返回值: 类引用对象

作用: 获取目标Android应用中指定Java类的引用

3. Java.performNow

函数名: Java.performNow

参数: function (回调函数)

返回值:

作用: 立即在Java运行时环境中执行代码,自动附加到JVM

4. Java.choose

函数名: Java.choose

参数: string (类名), object (回调对象)

返回值:

作用: 扫描内存中目标类的所有存活实例,对每个实例执行回调

5. Java.$new

函数名: $new

参数: 可变参数(构造函数参数)

返回值: 类实例对象

作用: 创建指定Java类的新实例

6. Interceptor.attach

函数名: Interceptor.attach

参数: NativePointer (目标地址), object (回调对象)

返回值:

作用: 将回调附加到指定的本地函数地址,hook本地函数

7. Process.getModuleByName

函数名: Process.getModuleByName

参数: string (模块名)

返回值: Module对象

作用: 根据名称获取进程中的模块信息

10. ptr

函数名: ptr

参数: number/string (地址)

返回值: NativePointer (指针对象)

作用: 将数字或字符串转换为NativePointer指针对象

11. NativePointer.readU8

函数名: readU8

参数:

返回值: number (读取的值)

作用: 从指针位置读取1字节无符号整数

12. NativePointer.writeU8

函数名: writeU8

参数: number (要写入的值)

返回值:

作用: 向指针位置写入1字节无符号整数

13. NativePointer.readUtf8String

函数名: readUtf8String

参数:

返回值: string (UTF-8字符串)

作用: 从指针位置读取UTF-8编码的字符串

14. Memory.protect

函数名: Memory.protect

参数: NativePointer (地址), number (大小), string (权限)

返回值: boolean (是否成功)

作用: 修改指定内存区域的保护权限

15. Memory.patchCode

函数名: Memory.patchCode

参数: NativePointer (地址), number (大小), function (补丁函数)

返回值:

作用: 安全地修改指定内存区域的代码

16. NativeFunction

函数名: NativeFunction (构造函数)

参数: NativePointer (地址), string (返回类型), array (参数类型数组)

返回值: NativeFunction对象

作用: 创建本地函数调用封装器,用于调用未导出的本地函数

17. retval.replace

函数名: replace

参数: 任意类型 (新返回值)

返回值:

作用: 修改函数的返回值(在Interceptor.attach的onLeave回调中使用)

这些函数涵盖了Frida在Android应用hook中的主要操作,包括Java层hook、本地层hook、内存操作和函数调用等。



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/946910.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

frida hook windows

一,前言 frida hook windows其实和 frida hook android 的 so库是差不多的 都是先找地址和偏移,然后通过模板来修改。 我的 frida版本是17.4.0,所以如果你的版本小于17.0,那这个的一些内容并不适用你,因为17.0后,…

Python图表库Matplotlib 组成部分介绍(Good)

来自: https://www.cnblogs.com/rustfisher/p/15042912.htmlPython图表库Matplotlib 组成部分介绍 图表有很多个组成部分,例如标题、x/y轴名称、大刻度小刻度、线条、数据点、注释说明等等。 我们来看官方给的图,图…

2025年饮料包装设备缠膜机厂家推荐排行榜:全自动缠膜机、饮料包装机、热收缩包装机、流水线缠膜设备源头厂家精选

2025年饮料包装设备缠膜机厂家推荐排行榜:全自动缠膜机、饮料包装机、热收缩包装机、流水线缠膜设备源头厂家精选 随着饮料行业的快速发展,包装设备的技术创新与性能提升成为行业关注焦点。饮料包装设备缠膜机作为生…

[Ubuntu] Ubuntu24.04环境配置(音视频开发)

[Ubuntu] Ubuntu24.04环境配置(音视频开发)$(".postTitle2").removeClass("postTitle2").addClass("singleposttitle");目录Ubuntu 24.04 开发环境初始化配置手册1. 系统信息2. 系统更…

Python图表库Matplotlib 组成部分介绍

来自: https://www.cnblogs.com/rustfisher/p/15042912.htmlPython图表库Matplotlib 组成部分介绍 图表有很多个组成部分,例如标题、x/y轴名称、大刻度小刻度、线条、数据点、注释说明等等。 我们来看官方给的图,图…

2025年自动上料机厂家权威推荐榜:螺旋上料机、真空上料机、粉末上料机、管链输送机全系列选购指南

2025年自动上料机厂家权威推荐榜:螺旋上料机、真空上料机、粉末上料机、管链输送机全系列选购指南 在工业自动化快速发展的今天,自动上料机作为生产线的重要设备,其性能直接影响生产效率和产品质量。随着技术的不断…

计数题合集

Arena 设 \(f_{i,j}\) 表示当前考虑 \(i\) 个英雄,这 \(i\) 个英雄初始血量最大值为 \(j\) 的方案数。 显然第一轮中,每个英雄血量都会减少 \(i-1\):若 \(i-1\ge j\),所有英雄都会在第一轮死亡,显然合法,方案数为…

ida动调pyd

首先我们要先写一个py脚本引入pyd 然后引入os和sys库 之后使用print(sys.executable) print(os.getpid()) 加载解释器和进程pid 再之后使用input暂停终端 在IDA中选择Local Windows debugger 再点击Debugger,选择atta…

so文件找不到却可以使用的解决

在用frida hook so库的时候,软件的so一直找不到,而程序却能正常运行 找了半天终于找到解决方法了。 参考https://bbs.kanxue.com/thread-281802.htm查看安装包的AndroidManifest.xml,观察里面的android:extractNati…

继承与多态动手动脑 - 20243867孙堃2405

实验代码// 祖父类 class GrandParent {public GrandParent() {System.out.println("GrandParent 无参构造方法");}public GrandParent(String msg) {System.out.println("GrandParent 带参构造方法:&…

pyarmor解密

一,介绍: pyarmor是一个对python代码的混淆工具,可以使用Pyarmor-Static-Unpack-1shot来解密 https://github.com/GTedd/Pyarmor-Static-Unpack-1shot-GT 二,解密工具使用 在工具文件夹里有一个名为oneshot的文件夹…

简单的CNN实现

import matplotlib.pyplot as plt import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torchvision import datasets, transforms import matplotlib.pyplot as p…

pyd逆向处理

一,初查: 1.查版本 先用die或者ida或者010查看pyd对应的版本号 假如版本不对的话是不能运行的 2.查函数和参数 可以使用 python自带的help()和dir()函数,查看里面有什么函数或者参数 二,静态分析 通过查字符串可以…

2025年包装机厂家权威推荐排行榜:自动包装机,食品包装机,真空包装机,颗粒包装机优质品牌深度解析

2025年包装机厂家权威推荐排行榜:自动包装机,食品包装机,真空包装机,颗粒包装机优质品牌深度解析 行业背景与发展趋势 包装机械行业作为制造业的重要支撑领域,近年来在智能化、自动化浪潮的推动下呈现出快速发展的…

ia16生成8086汇编

配置:Ubuntu-24.04 ia16 # 添加 PPA 仓库 sudo add-apt-repository ppa:tkchia/build-ia16 sudo apt-get update# 安装预编译包 sudo apt-get install gcc-ia16-elf libi86-ia16-elf# 验证安装 ia16-elf-gcc -v # 应该…

太突然!湘潭大学计算机学院刘昊霖教授不幸逝世,年仅37岁。

近日,我国计算机科学领域一颗冉冉升起的学术之星骤然陨落。湘潭大学计算机学院网络空间安全学院教授、博士生导师刘昊霖因突发疾病,经抢救无效不幸去世,年仅37岁。他的英年早逝,是学校、学界和我国科研事业的重大损…

解包魔改pyinstaller

在做NSSCTF 4th 的CrackMe&F***Me也是见到被魔改的pyinstaller生成的exe文件 直接用工具直接就报错了,这里来写一写这道题的魔改点,虽然我不会魔改pyinstaller,但 1.魔改判断标识: 正常文件结尾应该是这个: 但…

反编译解包微信小程序

做湾区杯时也是遇到了,记录一下 小程序的后缀应为.wxapg使用 npm i wedecode -g安装工具 运行 命令行直接输入 wedecode 即可运行, 全程自动引导wedecode 命令行直接指定参数 # 手动指定一个包wedecode ./name.wxapkg…

浅谈C++中的作用域

C++的作用域是每个初学者很容易混淆的一个知识点,C++ 中的作用域(scope)指的是变量、函数或其他标识符的可见和可访问的范围,如果对于C++的作用域没有一个清晰的认知的话很容易会出现空值或者空指针还有值未能及时…

2025年摩托车厂家权威推荐榜:覆盖街车、跑车、巡航车及越野车型的全方位选购指南与实力解析

2025年摩托车厂家权威推荐榜:覆盖街车、跑车、巡航车及越野车型的全方位选购指南与实力解析 摩托车作为现代交通工具与休闲生活方式的重要组成部分,其市场呈现出多元化、专业化的发展趋势。随着消费者对骑行体验要求…