有两种广泛的方法可以声明JNI
函数。
第一个是JNI
函数必须遵循特定命名约定的更明显的方式,例如JNIEXPORT void JNICALL Java_com_app_foo_bar
. 您可以使用 轻松识别此类功能readelf
。
另一种不太明显的方法是使用RegisterNatives
. 在这里你的函数可以有任何签名,而且它们不需要从共享库中导出。通常,您会调用RegisterNatives
fromJNI_OnLoad
将本机函数注册到 Java 运行时。
对于您的 binary libcms.so
,它使用第二种方法。
RegisterNatives
有以下原型
jint RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint Methods);
如果你分析你的代码,JNI_OnLoad
你会遇到RegisterNatives
像下面这样的调用。
第三个参数指向一个JNINativeMethod
结构数组,它被声明为
typedef struct {
char *name;
char *signature;
void *fnPtr;
} JNINativeMethod;
第一个成员是指向表示函数名称的空终止字符串的指针。但是,在您的情况下,所有名称和签名都已加密。
这些在运行时由.datadiv_decodeXXXXXXXX
函数系列解密。该.init_array
部分包含指向这些解密函数的指针,这意味着它们将在启动时被调用。
然而,这还不是全部。二进制文件还始终采用控制流扁平化混淆,因此执行路径可能不容易识别,如下所示。
要分析二进制文件,最好使用诸如Frida 之类的工具求助于动态分析技术。
进一步阅读: