本例仅在Android2.3模拟器跑通过,如果要适配其他机型,请自行研究,这里只是抛砖引玉。
0x00
在Android中的Apk的加固(加壳)原理解析和实现,一文中脱壳代码都写在了java层很容易被识别出来,很多需求需要把脱壳的程序转移到native层,其实转移的思路也很简单,就是在native层通过JNI调用Java层代码。
运行前,首先把ForceApkObj.apk放在/sdcard/payload_odex/目录下。
代码已上传至github,地址为https://github.com/jltxgcy/AndroidNativeShell。
0x01
public class ProxyApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
DexLoader.load("com.jltxgcy.dynamicdex");
}
@Override
public void onCreate() {
DexLoader.run();
}
}
还记得原来这两个方法有着一大堆代码,这里仅仅有一行代码就搞定。
我们再看来DexLoader类。
public class DexLoader {
static {
System.loadLibrary("dexloader");
}
public static native void load(String path);
public static native void run();
}
原来核心脱壳代码放在了native层。
0x02
JNI的实现如下,大家可以看到本质上就是把java层的代码通过JNI转移到native层了。
static void loadApk(JNIEnv * env, jclass clazz, jstring package) {
jclass activityThreadClazz;
jmethodID currentActivityThreadMethodID;
jobject activityThreadObject;
const char *packageName;
const char *className;
const char *methodName;
int codeoff;
jfieldID mPackagesFieldID;
jobject mPackagesJObject;
jclass mPackagesClazz;
jmethodID getMethodID;
jobject weakReferenceJObject;
jclass weakReferenceJClazz;
jmethodID getweakMethodID;
jobject loadedApkJObject;
jclass loadedApkJClazz;
jfieldID mClassLoaderFieldID;
jobject mClassLoaderJObject;
jstring dexPath;
jstring dexOptPath;
jclass dexClassLoaderClazz;
jmethodID initDexLoaderMethod;
jobject dexClassLoaderJObject;
activityThreadClazz = env->FindClass("android/app/ActivityThread");
currentActivityThreadMethodID = env->GetStaticMethodID(activityThreadClazz, "currentActivityThread",
"()Landroid/app/ActivityThread;");
activityThreadObject = env->CallStaticObjectMethod(activityThreadClazz, currentActivityThreadMethodID);
packageName = env->GetStringUTFChars(package, JNI_FALSE);
mPackagesFieldID = env->GetFieldID(activityThreadClazz, "mPackages", "Ljava/util/HashMap;");
mPackagesJObject = env->GetObjectField(activityThreadObject, mPackagesFieldID);
mPackagesClazz = env->GetObjectClass(mPackagesJObject);
getMethodID = env->GetMethodID(mPackagesClazz, "get",
"(Ljava/lang/Object;)Ljava/lang/Object;");
weakReferenceJObject = env->CallObjectMethod(mPackagesJObject, getMethodID, package);
weakReferenceJClazz = env->GetObjectClass(weakReferenceJObject);
getweakMethodID = env->GetMethodID(weakReferenceJClazz, "get",
"()Ljava/lang/Object;");
loadedApkJObject = env->CallObjectMethod(weakReferenceJObject, getweakMethodID);
loadedApkJClazz = env->GetObjectClass(loadedApkJObject);
mClassLoaderFieldID = env->GetFieldID(loadedApkJClazz, "mClassLoader", "Ljava/lang/ClassLoader;");
mClassLoaderJObject = env->GetObjectField(loadedApkJObject, mClassLoaderFieldID);
dexPath = env->NewStringUTF("/sdcard/payload_odex/ForceApkObj.apk");
dexOptPath = env->NewStringUTF("/sdcard/payload_odex/");
dexClassLoaderClazz = env->FindClass("dalvik/system/DexClassLoader");
initDexLoaderMethod = env->GetMethodID(dexClassLoaderClazz, "","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V");
dexClassLoaderJObject = env->NewObject(dexClassLoaderClazz,initDexLoaderMethod, dexPath, dexOptPath, NULL, mClassLoaderJObject);
env->SetObjectField(loadedApkJObject, mClassLoaderFieldID, dexClassLoaderJObject);
ALOGD("packageName:%s", packageName);
}
static void run(JNIEnv * env, jclass clazz) {
jclass activityThreadClazz;
jmethodID currentActivityThreadMethodID;
jobject activityThreadObject;
jfieldID mBoundApplicationFieldID;
jobject mBoundApplicationJObject;
jclass mBoundApplicationClazz;
jfieldID mInfoFieldID;
jobject mInfoJObject;
jclass mInfoClazz;
jfieldID mApplicationFieldID;
jobject mApplicationJObject;
jfieldID mInitialApplicationFieldID;
jobject mInitialApplicationJObject;
jfieldID mAllApplicationsFieldID;
jobject mAllApplicationsJObject;
jclass mAllApplicationsClazz;
jmethodID removeMethodID;
jfieldID mApplicationInfoFieldID;
jobject mApplicationInfoJObject;
jclass mApplicationInfoClazz;
jfieldID mBindApplicationInfoFieldID;
jobject mBindApplicationInfoJObject;
jclass mBindApplicationInfoClazz;
jfieldID classNameFieldID;
jfieldID mBindClassNameFieldID;
jstring applicationName;
jmethodID makeApplicationMethodID;
jobject ApplicationJObject;
jclass ApplicationClazz;
jmethodID onCreateMethodID;
activityThreadClazz = env->FindClass("android/app/ActivityThread");
currentActivityThreadMethodID = env->GetStaticMethodID(activityThreadClazz, "currentActivityThread",
"()Landroid/app/ActivityThread;");
activityThreadObject = env->CallStaticObjectMethod(activityThreadClazz, currentActivityThreadMethodID);
mBoundApplicationFieldID = env->GetFieldID(activityThreadClazz, "mBoundApplication", "Landroid/app/ActivityThread$AppBindData;");
mBoundApplicationJObject = env->GetObjectField(activityThreadObject, mBoundApplicationFieldID);
mBoundApplicationClazz = env->GetObjectClass(mBoundApplicationJObject);
mInfoFieldID = env->GetFieldID(mBoundApplicationClazz, "info", "Landroid/app/LoadedApk;");
mInfoJObject = env->GetObjectField(mBoundApplicationJObject, mInfoFieldID);
mInfoClazz = env->GetObjectClass(mInfoJObject);
mApplicationFieldID = env->GetFieldID(mInfoClazz, "mApplication", "Landroid/app/Application;");
mApplicationJObject = env->GetObjectField(mInfoJObject, mApplicationFieldID);
env->SetObjectField(mInfoJObject, mApplicationFieldID, NULL);
mInitialApplicationFieldID = env->GetFieldID(activityThreadClazz, "mInitialApplication", "Landroid/app/Application;");
mInitialApplicationJObject = env->GetObjectField(activityThreadObject, mInitialApplicationFieldID);
mAllApplicationsFieldID = env->GetFieldID(activityThreadClazz, "mAllApplications", "Ljava/util/ArrayList;");
mAllApplicationsJObject = env->GetObjectField(activityThreadObject, mAllApplicationsFieldID);
mAllApplicationsClazz = env->GetObjectClass(mAllApplicationsJObject);
removeMethodID = env->GetMethodID(mAllApplicationsClazz, "remove",
"(Ljava/lang/Object;)Z");
jboolean isTrue = env->CallBooleanMethod(mAllApplicationsJObject, removeMethodID, mInitialApplicationJObject);
mApplicationInfoFieldID = env->GetFieldID(mInfoClazz, "mApplicationInfo", "Landroid/content/pm/ApplicationInfo;");
mApplicationInfoJObject = env->GetObjectField(mInfoJObject, mApplicationInfoFieldID);
mApplicationInfoClazz = env->GetObjectClass(mApplicationInfoJObject);
mBindApplicationInfoFieldID = env->GetFieldID(mBoundApplicationClazz, "appInfo", "Landroid/content/pm/ApplicationInfo;");
mBindApplicationInfoJObject = env->GetObjectField(mBoundApplicationJObject, mBindApplicationInfoFieldID);
mBindApplicationInfoClazz = env->GetObjectClass(mBindApplicationInfoJObject);
classNameFieldID = env->GetFieldID(mApplicationInfoClazz, "className", "Ljava/lang/String;");
mBindClassNameFieldID = env->GetFieldID(mBindApplicationInfoClazz, "className", "Ljava/lang/String;");
applicationName = env->NewStringUTF("com.example.forceapkobj.MyApplication");
env->SetObjectField(mApplicationInfoJObject, classNameFieldID, applicationName);
env->SetObjectField(mBindApplicationInfoJObject, mBindClassNameFieldID, applicationName);
makeApplicationMethodID = env->GetMethodID(mInfoClazz, "makeApplication","(ZLandroid/app/Instrumentation;)Landroid/app/Application;");
ApplicationJObject = env->CallObjectMethod(mInfoJObject, makeApplicationMethodID, JNI_FALSE, NULL);
env->SetObjectField(activityThreadObject, mInitialApplicationFieldID, ApplicationJObject);
ApplicationClazz = env->GetObjectClass(ApplicationJObject);
onCreateMethodID = env->GetMethodID(ApplicationClazz, "onCreate","()V");
env->CallVoidMethod(ApplicationJObject, onCreateMethodID);
}
JNIEXPORT void JNICALL Java_com_jltxgcy_dynamicdex_DexLoader_load
(JNIEnv * env, jclass clazz, jstring packageName) {
loadApk(env, clazz, packageName);
ALOGD("Java_com_jltxgcy_dynamicdex_DexLoader_load");
}
JNIEXPORT void JNICALL Java_com_jltxgcy_dynamicdex_DexLoader_run
(JNIEnv * env, jclass clazz) {
run(env, clazz);
ALOGD("Java_com_jltxgcy_dynamicdex_DexLoader_run");
}