前言
在Android應(yīng)用中昭雌,出于對安全性的考慮,開發(fā)者會把一些重要的邏輯放到native層佛纫,即so庫中。但是so庫也并非絕對的安全呈宇,在強大的IDA反編譯下,so庫中的邏輯也將無所遁形存炮。今天蜈漓,我們要說的就是混淆so庫中的方法名。
正文
我們先來看下為什么要這么做享完。我們新建一個Android項目衣形,添加NDK支持姿鸿,默認項目中會有一個方法如下
external fun stringFromJNI(): String
companion object {
// Used to load the 'native-lib' library on application startup.
init {
System.loadLibrary("native-lib")
}
}
然后我們編譯運行苛预,在app/build/intermediates/cmake/debug/obj/armeabi/libnative-lib.so
目錄下找到我們編譯出來的so庫,在IDA中打開腻菇,我們可以很輕易的找到這個方法對應(yīng)的地方
為什么會這樣的筹吐?因為我們在寫
JNI
的時候秘遏,采用javah命令生成的頭文件,里面的方法名默認就是java_com_xxxx
這種形式邦危。所以我們現(xiàn)在要做的就是改變這種映射關(guān)系倦蚪。
當(dāng)虛擬機加載這個這個庫的時候,從java
層進入本地層首先會執(zhí)行JNI_Onload
這個函數(shù)裁僧,java層的方法就是在這個方法中注冊的,使用的方法為registerNativeMethods
播急,我們在這一步將注冊我們自定義的方法售睹,這樣方法名就可以由我們自己來定義。接下來看代碼實現(xiàn)捶枢。
- 重寫
JNI_Onload
方法
extern "C"
jint JNI_OnLoad(JavaVM* vm,void* reserved){
JNIEnv* env;
if (vm->GetEnv((void**)(&env), JNI_VERSION_1_6) != JNI_OK)
{
return -1;
}
//這一步飞崖,注冊我們的方法
if (!registerNatives(env)) {
return -1;
}
return JNI_VERSION_1_6;
}
- 接下來看
registerNatives
方法的實現(xiàn)
static JNINativeMethod gMethods[] = {
//第一個參數(shù)為Java層的方法名
//第二個參數(shù)為方法的簽名,括號內(nèi)為參數(shù)類型固歪,后面為返回類型
//第三個參數(shù)即為我們需要重新注冊的方法名
{ "stringFromJNI", "()Ljava/lang/String;", (void*)getStr},
};
extern "C"
int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
extern "C"
int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, JNIREG_CLASS, gMethods,
sizeof(gMethods) / sizeof(gMethods[0])))
return JNI_FALSE;
return JNI_TRUE;
}
到這里我們成功的替換了注冊的方法蒜鸡,重新用IDA打開so庫,發(fā)現(xiàn)我們這次找不到形如java_com_xxxx
的方法了最后牢裳,還有一步逢防,我們還得實現(xiàn)替換后的方法
extern "C"
//這里指定該代碼所在的段,編譯的時候就會把這個函數(shù)編譯到自定義的名叫”.mytext“的section里面
//由于我們在java層沒有定義這個函數(shù)因此要寫到一個自定義的section里面蒲讯。
__attribute__((section (".mytext"))) JNICALL jstring getStr
(JNIEnv *env, jobject obj)
{
return (env)->NewStringUTF("abc");
}
至此忘朝,我們就完成了所有混淆方法的操作。
后記
這只是一步基礎(chǔ)的安全操作判帮,也很容破解局嘁。比如通過IDA動態(tài)調(diào)試找到RegisterNatives方法,就能直接找到所對應(yīng)的方法晦墙,也能通過直接找額外的段來找到這個方法,但是這些就不在本文詳細說明了晌畅,有興趣的同學(xué)可以自己去嘗試下旱捧。