
androidのclasses.dexファイルを保護するために使用されるdex-vm
dex-vm上でdalvikバイトコードを実行することでdexを保護し、逆コンパイルの難易度を高めます。 プロジェクトは2つの部分に分かれており、nmm-protectは純粋なJavaプロジェクトで、dexを変換し、dex内のデータをC構造体に変換し、オペコードをランダム化してNDKプロジェクトを生成し、コンパイル後に保護されたapkを生成します。nmmvmはAndroidプロジェクトで、dex-vmの実装や各種dalvik命令のテストなどを含みます。
nmm-protectをコンパイルせずに、直接それを使ってプロジェクトと最終的なapkを生成する方法を見ることができます。apkを処理する例。
例はLinux環境でテストされていますが、Windowsでも問題ないはずです。まずJDK、Android SDK、NDKをインストールしてください。
vm-protect.jarをダウンロードし、環境変数ANDROID_SDK_HOME、ANDROID_NDK_HOMEを設定します:
export ANDROID_SDK_HOME=/opt/android-sdk
export ANDROID_NDK_HOME=/opt/android-sdk/ndk/22.1.7171670
export CMAKE_PATH=/opt/android-sdk/cmake/3.18.1/ #可选,不配置的话直接使用/bin/cmake
java -jar vm-protect-xxx.jar apk input.apk convertRules.txt mapping.txt
実行が完了すると、input.apk があるディレクトリに build ディレクトリが生成され、その中には最終出力の apk(build/input-protect.apk)、完全な C プロジェクト dex2c(cmake ベース)、および処理過程で生成された .dex などが含まれます。
初回実行後、jar の位置に tools ディレクトリが生成され、その中の config.json を編集して Android SDK、NDK 関連のパスを設定できます。
友人がGUIインターフェースを作ってくれて、より便利に使えるようになりました。必要な方は試してみてください。https://github.com/TimScriptov/nmmp
生成された apk は、zipalign でアライメント(新しいバージョンでは zipflinger で apk を処理済みのため、個別の zipalign は不要)し、apksigner で署名しないとインストールして使用できません
apksigner sign --ks ~/.myapp.jks build/input-protect-align.apk
java -jar vm-protect-xxx.jar aab test.aab convertRules.txt
その後、jarsignerを使用して署名する必要があります。signflingerを統合して署名することもできます。
jarsigner -keystore ~/.myapp.jks -storepass pass -keypass pass test-protect.aab keyAlias
java -jar vm-protect-xxx.jar aar testModule.aar convertRules.txt
git clone https://github.com/maoabc/nmmp.git
cd nmmp/nmm-protect
./gradlew arsc:build
./gradlew build
成功后会在build/libs生成可直接执行的fatjar。
无转换规则文件,则会转换dex里所有class里的方法(除了构造方法和静态初始化方法)。规则只支持一些简单的情况:
//支持的规则比较简单,*只是被转成正则表达式的.*,支持一些简单的继承关系
class * extends android.app.Activity
class * implements java.io.Serializable
class my.package.AClass
class my.package.* { *; }
class * extends java.util.ArrayList {
if*;
}
class A {
}
class B extends A {
}
class C extends B {
}
//比如'class * extends A' 只会匹配B而不会再匹配C
nmmvmはdex仮想マシンの具体的な実装であり、エントリポイントは1つの関数のみです:
jvalue vmInterpret(
JNIEnv *env,
const vmCode *code,
const vmResolver *dvmResolver
);
typedef struct {
const u2 *insns; //指令
const u4 insnsSize; //指令大小
regptr_t *regs; //寄存器
u1 *reg_flags; //寄存器数据类型标记,主要标记是否为对象
const u1 *triesHandlers; //异常表
} vmCode;
typedef struct {
const vmField *(*dvmResolveField)(JNIEnv *env, u4 idx, bool isStatic);
const vmMethod *(*dvmResolveMethod)(JNIEnv *env, u4 idx, bool isStatic);
//从类型常量池取得类型名
const char *(*dvmResolveTypeUtf)(JNIEnv *env, u4 idx);
//直接返回jclass对象,本地引用需要释放引用
jclass (*dvmResolveClass)(JNIEnv *env, u4 idx);
//根据类型名得到class
jclass (*dvmFindClass)(JNIEnv *env, const char *type);
//const_string指令加载的字符串对象
jstring (*dvmConstantString)(JNIEnv *env, u4 idx);
} vmResolver;
vmCodeは実行に必要な命令、例外テーブル、レジスタ空間を提供し、vmResolverはfieldやmethodなどの実行時シンボルを提供する関数ポインタの集合を含みます。この2つのパラメータをカスタマイズすることで異なる難読化方式を実現できます。例えば、プロジェクト内のtest.cppにはlibdexベースの簡単なvmResolverがあり、主に開発テスト用です。一方、nmm-protectは.dex関連データをC構造体に変換するもので、opcodeのランダム化なども含み、基本的に実用可能です。
現在、モジュール関連の難読化を実装済みです。使い方はapkの難読化と同様です。問題があればissueを提出してください。
nmm-protectはGPLライセンスで公開されています。nmm-protect licence、dex-vm部分はApacheライセンスで公開されています。nmmvm licence。vm部分のみがapkにパッケージされ、nmm-protectはdexを変換するだけなので、ライセンスは生成結果に影響しません。