说明

前面基础部分,都是在通过反编译拿到源码的时候进行hook,如果源代码有加固,那么有没有其他办法能从内存中拿到所有的方法然后去hook呢?也是可行的,这就是本次的内容。

本次以Sieve这款APK为例吧

frida手册:https://frida.re/docs/javascript-api/#java

# 找到对应的进程
frida-ps -D emulator-5554

拿到所有类名

通过翻阅frida手册,可以看到要枚举出所有类,有2种方法Java.enumerateLoadedClasses(callbacks)Java.enumerateLoadedClassesSync()

我们用第一种,通过回调函数一个一个的查类;编写js脚本,通过setTimeout函数可以控制开始运行的时间。

setTimeout(function (){
  Java.perform(function (){
    Java.enumerateLoadedClasses({
      onMatch: function(_className){
        // _className为string类型
        console.log("[*] found instance of " + _className);
      },
      onComplete: function(){
        console.log("[*] class enuemration complete");
      }
    });
  });
}, 0);

运行

frida -D emulator-5554 -l test.js Sieve

因为上面的类结果实在是太多了,因此优化一下js只显示相关的类,这里我们以sieve为关键词进行过滤。

该app的包名为com.mwr.example.sieve,所以用sieve为关键词过滤,定位内部的类

setTimeout(function (){
  Java.perform(function (){
    Java.enumerateLoadedClasses({
      onMatch: function(_className){
        // _className为string类型
        if (_className.includes("sieve")){
          console.log("[*] found instance of " + _className);
        }
      },
      onComplete: function(){
        console.log("[*] class enuemration complete");
      }
    });
  });
}, 0);

结果如下,明显少了很多

[*] found instance of com.mwr.example.sieve.AuthServiceConnector$MessageHandler
[*] found instance of com.mwr.example.sieve.WelcomeActivity
[*] found instance of com.mwr.example.sieve.PWDBHelper
[*] found instance of com.mwr.example.sieve.CryptoServiceConnector$ResponseListener
[*] found instance of com.mwr.example.sieve.MainLoginActivity$1
[*] found instance of com.mwr.example.sieve.AuthService
[*] found instance of com.mwr.example.sieve.DBContentProvider
[*] found instance of com.mwr.example.sieve.PINActivity
[*] found instance of com.mwr.example.sieve.AuthServiceConnector$ResponseListener
[*] found instance of com.mwr.example.sieve.SettingsActivity
[*] found instance of com.mwr.example.sieve.MainLoginActivity
[*] found instance of com.mwr.example.sieve.NetBackupHandler$ResultListener
[*] found instance of com.mwr.example.sieve.PWList
[*] found instance of com.mwr.example.sieve.ShortLoginActivity
[*] found instance of com.mwr.example.sieve.FileBackupProvider
[*] found instance of com.mwr.example.sieve.AuthServiceConnector

获取类实例和方法

这里我们就hook com.mwr.example.sieve.MainLoginActivity 这个类吧。通过上述方式枚举出的结果是以字符串返回的,那么就需要通过其他手段拿到实例,有2种方法

  • Java.use(className)
  • Java.choose(className, callbacks)

区别在于:

  • Java.use 是实例化一个类,需要指定类的完整名称,一次只能实例化一个对象,可以直接对该对象进行操作。
  • Java.choose 用于寻找已加载的类实例,并执行回调函数,可以找到多个对象,并对它们进行操作。

这里我们通过Java.choose去定位类实例(Java.use也是可以的,见下方注释代码),然后通过java反射机制获取方法和字段等。

插曲:frida Java.choose() 有时候找不到实例,为什么?

  1. 目标类没有被加载。在调用Java.choose之前,确保目标类已经被加载。可以使用Java.enumerateLoadedClasses列出所有已经被加载的类。
  2. 目标类的实例数量为0。如果目标类的实例数量为0,Java.choose将不会触发onMatch回调函数。可以使用Java.use来创建一个新的类实例并进行操作。
  3. Java.choose的调用时机不对。在Java.choose调用之前,可能需要一些初始化操作,例如启动目标应用程序或者等待应用程序加载完毕。确保在执行Java.choose时,目标应用程序已经可以正常工作。
  4. 目标类的实例被隐藏或者被更改了名称。在一些情况下,开发者可能会对类的实例进行一些保护措施,例如将类名更改为随机字符串或者通过某些手段隐藏类的实例。这些保护措施可能导致Java.choose无法找到目标类的实例。
  5. 没有足够的权限。在某些情况下,应用程序可能会对某些类或者方法进行保护,例如通过ClassLoader进行保护。如果当前的用户没有足够的权限来访问这些受保护的类或者方法,Java.choose将无法找到目标类的实例。

编写脚本如下:

setTimeout(function (){
  Java.perform(function (){
    Java.choose("com.mwr.example.sieve.MainLoginActivity", {
      onMatch: function(instance){
        console.log("[*] found instance " + instance);
        // java反射部分
        var clazz = instance.getClass();
        var methods = clazz.getDeclaredMethods();
        methods.forEach(function(value, index){
          console.log(value);
        });
      },
      onComplete: function(){
        console.log("[*] instance find complete")
      }
    })
  });
}, 0);

// Java.use 方法
// setTimeout(function (){
//   Java.perform(function (){
//     var myClass = Java.use("com.mwr.example.sieve.MainLoginActivity");
//     var clazz = myClass.class;
//     console.log(clazz.getDeclaredMethods());
//   });
// }, 0);

获取到的方法如下:

public void com.mwr.example.sieve.MainLoginActivity.checkKeyResult(boolean)
public void com.mwr.example.sieve.MainLoginActivity.checkPinResult(boolean)
public void com.mwr.example.sieve.MainLoginActivity.connected()
public void com.mwr.example.sieve.MainLoginActivity.firstLaunchResult(int)
public void com.mwr.example.sieve.MainLoginActivity.login(android.view.View)
protected void com.mwr.example.sieve.MainLoginActivity.onActivityResult(int,int,android.content.Intent)
public void com.mwr.example.sieve.MainLoginActivity.onBackPressed()
protected void com.mwr.example.sieve.MainLoginActivity.onCreate(android.os.Bundle)
public boolean com.mwr.example.sieve.MainLoginActivity.onCreateOptionsMenu(android.view.Menu)
public boolean com.mwr.example.sieve.MainLoginActivity.onOptionsItemSelected(android.view.MenuItem)
public void com.mwr.example.sieve.MainLoginActivity.onPause()
public void com.mwr.example.sieve.MainLoginActivity.onResume()
protected void com.mwr.example.sieve.MainLoginActivity.onStart()
public void com.mwr.example.sieve.MainLoginActivity.sendFailed()
public void com.mwr.example.sieve.MainLoginActivity.setKeyResult(boolean)
public void com.mwr.example.sieve.MainLoginActivity.setPinResult(boolean)
private void com.mwr.example.sieve.MainLoginActivity.initaliseActivity()
private void com.mwr.example.sieve.MainLoginActivity.loginFailed()
private void com.mwr.example.sieve.MainLoginActivity.loginSuccessful()
private void com.mwr.example.sieve.MainLoginActivity.openSettings()
private void com.mwr.example.sieve.MainLoginActivity.setPin()
private void com.mwr.example.sieve.MainLoginActivity.unbind()
private void com.mwr.example.sieve.MainLoginActivity.welcomeUser()

调用方法

拿到方法后,可以去调用,这里我们调用com.mwr.example.sieve.MainLoginActivity.loginSuccessful()吧,因为获取到的是实例,直接调用方法即可。

setTimeout(function (){
  Java.perform(function (){
    Java.choose("com.mwr.example.sieve.MainLoginActivity", {
      onMatch: function(instance){
        console.log("[*] found instance " + instance);
        instance.loginSuccessful();
      },
      onComplete: function(){
        console.log("[*] instance find complete")
      }
    })
  });
}, 0);

调用后就直接登录成功了。

想要hook方法的话类似,通过Java.use就行。

Copyright © d4m1ts 2023 all right reserved,powered by Gitbook该文章修订时间: 2023-03-16 14:24:15

results matching ""

    No results matching ""