导语:保护你的so[滑稽]
开始之前,我们先进行思路梳理。实现在使用菜单进行hook时,你是否想过这些变量bool、float、const char的含义。一个int方法可以修改值,一个bool方法可以判断是否。那么更换到签名检验也是一样。我们首先需要定义一个方法用于获取签名检验且存储到变量之中。由于lgl菜单的特性,其在jni的使用是比较完善的,我们无需通过使用其他方法库来获取,按照原有的Java方法获取方式,我们写出下面这段c++代码用于获取签名哈希值,你可以将他视作一个变量const char* getSignatureCString(JNIEnv *env, jobject context, const char *packageName) { if (env == nullptr || packageName == nullptr) { __android_log_print(ANDROID_LOG_DEBUG, “YourAppTag”, “JNIEnv or packageName is null”); return nullptr; } // 获取Context和PackageManager类 jclass contextClass = env->FindClass(“android/content/Context”); jclass packageManagerClass = env->FindClass(“android/content/pm/PackageManager”); jclass packageInfoClass = env->FindClass(“android/content/pm/PackageInfo”); // 定义PackageManager的GET_SIGNATURES常量 static const int GET_SIGNATURES = 64; // 需要包含定义此宏的头文件或替换为实际的整数值 // 获取getPackageManager和getPackageInfo方法ID jmethodID getPackageManagerMethod = env->GetMethodID(contextClass, “getPackageManager”, “()Landroid/content/pm/PackageManager;”); jmethodID getPackageInfoMethod = env->GetMethodID(packageManagerClass, “getPackageInfo”, “(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;”); // 调用getPackageManager方法 jobject packageManager = env->CallObjectMethod(context, getPackageManagerMethod); if (env->ExceptionOccurred()) { env->ExceptionClear(); __android_log_print(ANDROID_LOG_DEBUG, “YourAppTag”, “Exception occurred when getting PackageManager”); return nullptr; } // 创建packageName的jstring对象 jstring packageNameStr = env->NewStringUTF(packageName); if (packageNameStr == nullptr) { __android_log_print(ANDROID_LOG_DEBUG, “YourAppTag”, “Failed to create jstring from packageName”); return nullptr; } // 调用getPackageInfo方法 jobject packageInfo = env->CallObjectMethod(packageManager, getPackageInfoMethod, packageNameStr, GET_SIGNATURES); env->DeleteLocalRef(packageNameStr); // 清理局部引用 if (env->ExceptionOccurred()) { env->ExceptionClear(); __android_log_print(ANDROID_LOG_DEBUG, “YourAppTag”, “Exception occurred when getting PackageInfo”); return nullptr; } // 获取PackageInfo类中的signatures字段ID jfieldID signaturesField = env->GetFieldID(packageInfoClass, “signatures”, “[Landroid/content/pm/Signature;”); // 获取signatures数组 jobjectArray signaturesArray = (jobjectArray)env->GetObjectField(packageInfo, signaturesField); if (env->ExceptionOccurred()) { env->ExceptionClear(); __android_log_print(ANDROID_LOG_DEBUG, “YourAppTag”, “Exception occurred when getting Signatures array”); return nullptr; } // 确保signatures数组不为空 if (signaturesArray == nullptr) { __android_log_print(ANDROID_LOG_DEBUG, “YourAppTag”, “Signatures array is null”); return nullptr; } // 获取第一个Signature对象 jobject signature = env->GetObjectArrayElement(signaturesArray, 0); if (signature == nullptr) { __android_log_print(ANDROID_LOG_DEBUG, “YourAppTag”, “Failed to get the first Signature object”); return nullptr; } // 获取Signature对象的hashCode方法ID jmethodID hashCodeMethod = env->GetMethodID(env->GetObjectClass(signature), “hashCode”, “()I”); // 调用hashCode方法 jint sigHashCode = env->CallIntMethod(signature, hashCodeMethod); if (env->ExceptionOccurred()) { env->ExceptionClear(); __android_log_print(ANDROID_LOG_DEBUG, “YourAppTag”, “Exception occurred when calling hashCode”); env->DeleteLocalRef(signature); return nullptr; } // 将哈希码转换为字符串 char* sigStr = (char*)malloc(12 * sizeof(char)); // 11位足以存储32位整数的字符串表示,加上结束符 if (sigStr == nullptr) { __android_log_print(ANDROID_LOG_DEBUG, “YourAppTag”, “Failed to allocate memory for signature string”); env->DeleteLocalRef(signature); return nullptr; } sprintf(sigStr, “%X”, sigHashCode); // 使用%X格式化为十六进制 // 清理本地引用 env->DeleteLocalRef(signature); env->DeleteLocalRef(signaturesArray); env->DeleteLocalRef(packageInfo); env->DeleteLocalRef(packageManager); env->DeleteLocalRef(packageInfoClass); env->DeleteLocalRef(packageManagerClass); env->DeleteLocalRef(contextClass); // 返回签名哈希码的字符串表示 return sigStr; // 注意:调用者需要释放内存}定义好了变量,那么我们只需传入特定是参数值,如包名进行获取,这里我选择的是菜单包名,可自行根据不同游戏包名进行更改,为了代码简洁我也就没有添加自动获取的代码,不然实在过多代码量。通过日志打印获取到的哈希值跟我们自己的进行对比发现是吻合的那么这个方法就是可以运行的
不知各位在阅读lgl菜单源码的Java层时,有没有发现许多native类型的方法,他们的实现其实都在main.cpp的文件之中,所以我们只需选择在其中加入我们下边的代码即可,我们选择的是菜单按钮逻辑的方法内加入对代码进行分析wshxz是我们自己的哈希值,检验是个简单的哈希对比,通过mt或者np你可以获取自己的哈希值。findclass是用于在so层调用Java层内的方法所用,大体逻辑为定位类名、调用该类名内的方法,不建议各位设置太多花里胡哨,直接一个结束运行即可。如果你对Java有一定理解也可以选择重写退出逻辑为弹出对话框。
所以你也要记得在你的类名中定义相关方法
到这里一个完善的检验逻辑就完成了,再说一下这个逻辑缺点,由于是我的一个失败的思路,所以是存在不少不足之处的。首先是无法用于被mt去签过的游戏软件,然后so层也没做相关混淆,所以别人可以直接复制哈希值在so层进行修改,当然这些问题都是我以自己为对手模板进行确定的一些逻辑问题,或许还有更多谁知道呢。既然失败的思路有了,成功的思路有吗,自然是有的,我也是排除了一些方法得出的。给各位具有一定开发基础的人一点点小小的建议,不要把检验对象定以为运行中的app。将app当做一个压缩包你的检验方式会更多。。。道阻且长··结束··
THE END