maoabc / nmmp

dex-vm used to protect the android classes.dex file
736 stars 260 forks source link

android5 对继承自android.view.MenuInflater的类中的getRealOwner方法进行重写,并vmp加固后出现错误,E/art ( 4011): Failed to register non-native method com.andpos.myapplication.TestMenuInflater.getRealOwner()Ljava/lang/Object; as native #29

Closed AdenTk closed 2 years ago

AdenTk commented 2 years ago

我在测试应用中创建了一个类为TestMenuInflater,继承自android.view.MenuInflater,并重写了getRealOwner方法,在MainActivity中调用了该方法,使用加固工程对该方法进行vmp化,发现出现Failed to register non-native method的错误。 1、对系统源码进行分析,发现在Android5下的art/runtime/jni_internal.cc的RegisterNativeMethods会抛出该错误 2、在RegisterNativeMethods方法中出现mirror::ArtMethod* m = c->FindDirectMethod(name, sig);跟踪进去

for (Class* klass = this; klass != nullptr; klass = klass->GetSuperClass()) {
   ArtMethod* method = klass->FindDeclaredDirectMethod(name, signature);
  if (method != nullptr) {
      return method;
   }
}
return nullptr;

发现会再jclass中寻找FindDeclaredDirectMethod,如果找不到会从klass = klass->GetSuperClass()父类中寻找,即MenuInflater获取得到getRealOwner,但是该方法中未发现accessflag为native,所以会报non-native method错误。 3、以上类似的问题相信会有很多,问题都是继承自android sdk中的公共类,然后重写其中的DirectMethod,进行vmp化。 4、经测试在android8未发现该问题,并且他在注册动态jni函数的函数与android5不一样。 5、请问下,针对在android5上面这个问题该如何解决呢?

AdenTk commented 2 years ago

以下为TestMenuInflater代码

import android.content.Context;
import android.view.MenuInflater;

public class TestMenuInflater extends MenuInflater {
    public TestMenuInflater(Context context) {
        super(context);
    }

    Object getRealOwner(){
        return null;
    }
}

在MainActivity中的调用方式为

TestMenuInflater testMenuInflater = new TestMenuInflater(MainActivity.this);
testMenuInflater.getRealOwner();
maoabc commented 2 years ago

写了个简单测试,复现了。只在api 21和22上有这个问题,这两个版本查找方法时先查找当前类的direct method,当前类没找到会查找父类的direct method,都没找到才会再查找当前类的virtual method。正确的查找逻辑应该是先查找当前类的direct method和virtual method,当前类找不到才会找父类。

目前想到的解决方法,感觉可以先解析manifest找到最低api版本,如果低于23则对所有class分析,找出所有类似的关系,处理时跳过这些方法

maoabc commented 2 years ago

还有一种方式是,找到有这类问题的方法,原方法不进行native而是生成一个不重名的新native方法,原方法调用这个native方法。

AdenTk commented 2 years ago

还有一种方式是,找到有这类问题的方法,原方法不进行native而是生成一个不重名的新native方法,原方法调用这个native方法。

嗯嗯。感觉这种方式会比较好,但是相应的也是一个体力活

maoabc commented 2 years ago

处理dex要比较大的改动,同时还得把安卓5的sdk一起引入找出这类方法,后面抽时间试试

maoabc commented 2 years ago

调整了dex处理, 目前能分析自身class里包含的这种问题的方法. 初步实现后面继续改, 后面需要把android 5的sdk用dx或者d8转换为dex,加入一起分析才能完全找出.

AdenTk commented 2 years ago

还有一种情况,不是直接继承androidsdk的类,而是一个类有多层继承关系,到继承最顶部的超类为android sdk,并且各个类分布在不同的dex中,则需要联合判断。

例如:
类A继承类B
类B继承类C
类C继承类D
类D继承类android.view.MenuInflater
类A,类B在classes.dex
类C,类D在classes2.dex
加固的方法还是为getRealOwner方法,则需要联合多个dex进行分析,不然还是会出现non-native

本来我想帮忙修复的,到这一步,感觉就有点难了,得改整个代码,而不是添加代码的问题

maoabc commented 2 years ago

已经能处理了,完整加载所有类就行,再进一步分析加入android sdk

AdenTk commented 2 years ago

请问android sdk你是准备怎么取得,直接使用android sdk中的source code还是使用源码编译的framework.jar,我目前是直接从android sdk中的source code中的文件逐个获取类名,然后在android工程里去加载类,并从中记录非final的类,及其对应的direct方法。

maoabc commented 2 years ago

直接用class吧,r8或者d8就能处理

maoabc commented 2 years ago

最极端也可以不去判断,直接对所以virtual method生成一个不重名native方法。

AdenTk commented 2 years ago

最极端也可以不去判断,直接对所以virtual method生成一个不重名native方法。

但如果类里面有很多方法需要加密,会导致生成较多的方法

AdenTk commented 2 years ago

对了,我有试过一个类里面有10000个方法去测试你的vmp加固,会出现stack size 最大为8M的错误,仅仅加固了那个类里的一个方法而已,就会报错