author : xingjun.xyf
 *  contact file time desc import { FCCommon } from "../FCCommon" ; import { DMLog } from "../dmlog" ; import { MethodData } from "./jni/method_data" ; import JNI_ENV_METHODS from "./jni/jni_env.json" ; const jni_struct_array = [ "reserved0" , "reserved1" , "reserved2" , "reserved3" , "GetVersion" , "DefineClass" , "FindClass" , "FromReflectedMethod" , "FromReflectedField" , "ToReflectedMethod" , "GetSuperclass" , "IsAssignableFrom" , "ToReflectedField" , "Throw" , "ThrowNew" , "ExceptionOccurred" , "ExceptionDescribe" , "ExceptionClear" , "FatalError" , "PushLocalFrame" , "PopLocalFrame" , "NewGlobalRef" , "DeleteGlobalRef" , "DeleteLocalRef" , "IsSameObject" , "NewLocalRef" , "EnsureLocalCapacity" , "AllocObject" , "NewObject" , "NewObjectV" , "NewObjectA" , "GetObjectClass" , "IsInstanceOf" , "GetMethodID" , "CallObjectMethod" , "CallObjectMethodV" , "CallObjectMethodA" , "CallBooleanMethod" , "CallBooleanMethodV" , "CallBooleanMethodA" , "CallByteMethod" , "CallByteMethodV" , "CallByteMethodA" , "CallCharMethod" , "CallCharMethodV" , "CallCharMethodA" , "CallShortMethod" , "CallShortMethodV" , "CallShortMethodA" , "CallIntMethod" , "CallIntMethodV" , "CallIntMethodA" , "CallLongMethod" , "CallLongMethodV" , "CallLongMethodA" , "CallFloatMethod" , "CallFloatMethodV" , "CallFloatMethodA" , "CallDoubleMethod" , "CallDoubleMethodV" , "CallDoubleMethodA" , "CallVoidMethod" , "CallVoidMethodV" , "CallVoidMethodA" , "CallNonvirtualObjectMethod" , "CallNonvirtualObjectMethodV" , "CallNonvirtualObjectMethodA" , "CallNonvirtualBooleanMethod" , "CallNonvirtualBooleanMethodV" , "CallNonvirtualBooleanMethodA" , "CallNonvirtualByteMethod" , "CallNonvirtualByteMethodV" , "CallNonvirtualByteMethodA" , "CallNonvirtualCharMethod" , "CallNonvirtualCharMethodV" , "CallNonvirtualCharMethodA" , "CallNonvirtualShortMethod" , "CallNonvirtualShortMethodV" , "CallNonvirtualShortMethodA" , "CallNonvirtualIntMethod" , "CallNonvirtualIntMethodV" , "CallNonvirtualIntMethodA" , "CallNonvirtualLongMethod" , "CallNonvirtualLongMethodV" , "CallNonvirtualLongMethodA" , "CallNonvirtualFloatMethod" , "CallNonvirtualFloatMethodV" , "CallNonvirtualFloatMethodA" , "CallNonvirtualDoubleMethod" , "CallNonvirtualDoubleMethodV" , "CallNonvirtualDoubleMethodA" , "CallNonvirtualVoidMethod" , "CallNonvirtualVoidMethodV" , "CallNonvirtualVoidMethodA" , "GetFieldID" , "GetObjectField" , "GetBooleanField" , "GetByteField" , "GetCharField" , "GetShortField" , "GetIntField" , "GetLongField" , "GetFloatField" , "GetDoubleField" , "SetObjectField" , "SetBooleanField" , "SetByteField" , "SetCharField" , "SetShortField" , "SetIntField" , "SetLongField" , "SetFloatField" , "SetDoubleField" , "GetStaticMethodID" , "CallStaticObjectMethod" , "CallStaticObjectMethodV" , "CallStaticObjectMethodA" , "CallStaticBooleanMethod" , "CallStaticBooleanMethodV" , "CallStaticBooleanMethodA" , "CallStaticByteMethod" , "CallStaticByteMethodV" , "CallStaticByteMethodA" , "CallStaticCharMethod" , "CallStaticCharMethodV" , "CallStaticCharMethodA" , "CallStaticShortMethod" , "CallStaticShortMethodV" , "CallStaticShortMethodA" , "CallStaticIntMethod" , "CallStaticIntMethodV" , "CallStaticIntMethodA" , "CallStaticLongMethod" , "CallStaticLongMethodV" , "CallStaticLongMethodA" , "CallStaticFloatMethod" , "CallStaticFloatMethodV" , "CallStaticFloatMethodA" , "CallStaticDoubleMethod" , "CallStaticDoubleMethodV" , "CallStaticDoubleMethodA" , "CallStaticVoidMethod" , "CallStaticVoidMethodV" , "CallStaticVoidMethodA" , "GetStaticFieldID" , "GetStaticObjectField" , "GetStaticBooleanField" , "GetStaticByteField" , "GetStaticCharField" , "GetStaticShortField" , "GetStaticIntField" , "GetStaticLongField" , "GetStaticFloatField" , "GetStaticDoubleField" , "SetStaticObjectField" , "SetStaticBooleanField" , "SetStaticByteField" , "SetStaticCharField" , "SetStaticShortField" , "SetStaticIntField" , "SetStaticLongField" , "SetStaticFloatField" , "SetStaticDoubleField" , "NewString" , "GetStringLength" , "GetStringChars" , "ReleaseStringChars" , "NewStringUTF" , "GetStringUTFLength" , "GetStringUTFChars" , "ReleaseStringUTFChars" , "GetArrayLength" , "NewObjectArray" , "GetObjectArrayElement" , "SetObjectArrayElement" , "NewBooleanArray" , "NewByteArray" , "NewCharArray" , "NewShortArray" , "NewIntArray" , "NewLongArray" , "NewFloatArray" , "NewDoubleArray" , "GetBooleanArrayElements" , "GetByteArrayElements" , "GetCharArrayElements" , "GetShortArrayElements" , "GetIntArrayElements" , "GetLongArrayElements" , "GetFloatArrayElements" , "GetDoubleArrayElements" , "ReleaseBooleanArrayElements" , "ReleaseByteArrayElements" , "ReleaseCharArrayElements" , "ReleaseShortArrayElements" , "ReleaseIntArrayElements" , "ReleaseLongArrayElements" , "ReleaseFloatArrayElements" , "ReleaseDoubleArrayElements" , "GetBooleanArrayRegion" , "GetByteArrayRegion" , "GetCharArrayRegion" , "GetShortArrayRegion" , "GetIntArrayRegion" , "GetLongArrayRegion" , "GetFloatArrayRegion" , "GetDoubleArrayRegion" , "SetBooleanArrayRegion" , "SetByteArrayRegion" , "SetCharArrayRegion" , "SetShortArrayRegion" , "SetIntArrayRegion" , "SetLongArrayRegion" , "SetFloatArrayRegion" , "SetDoubleArrayRegion" , "RegisterNatives" , "UnregisterNatives" , "MonitorEnter" , "MonitorExit" , "GetJavaVM" , "GetStringRegion" , "GetStringUTFRegion" , "GetPrimitiveArrayCritical" , "ReleasePrimitiveArrayCritical" , "GetStringCritical" , "ReleaseStringCritical" , "NewWeakGlobalRef" , "DeleteWeakGlobalRef" , "ExceptionCheck" , "NewDirectByteBuffer" , "GetDirectBufferAddress" , "GetDirectBufferCapacity" , "GetObjectRefType" ] ; export namespace Jni { export function getJNIFunctionAdress ( jnienv_addr , func_name ) { var offset = jni_struct_array . indexOf ( func_name ) * Process . pointerSize ; return jnienv_addr . add ( offset ) . readPointer ( ) ; } export function getJNIAddr ( name ) { var env = Java . vm . getEnv ( ) ; var env_ptr = env . handle . readPointer ( ) ; const addr = Jni . getJNIFunctionAdress ( env_ptr , name ) ; return addr ; } export function hookJNI ( name , callbacksOrProbe , data ? ) { const addr = Jni . getJNIAddr ( name ) ; return Interceptor . attach ( addr , callbacksOrProbe ) ; } /**
     * 分离仓库地址：https://github.com/deathmemory/fridaRegstNtv
     */ export function hook_registNatives ( ) { const tag = 'fridaRegstNtv' ; Jni . hookJNI ( "RegisterNatives" , { onEnter : function ( args ) { var env = Java . vm . getEnv ( ) ; var p_size = Process . pointerSize ; var methods = args [ 2 ] ; var methodcount = args [ 3 ] . toInt32 ( ) ; var name = env . getClassName ( args [ 1 ] ) ; DMLog . i ( tag , "==== class: " + name + " ====" ) ; DMLog . i ( tag , "==== methods: " + methods + " nMethods: " + methodcount + " ====" ) ; /** 根据函数结构原型遍历动态注册信息
                 typedef struct {
                    const char* name;
                    const char* signature;
                    void* fnPtr;
                 } JNINativeMethod;
                 jint RegisterNatives(JNIEnv* env, jclass clazz, const JNINativeMethod* methods, jint nMethods)
                 */ for ( var i = 0 ; i < methodcount ; i ++ ) { var idx = i * p_size * 3 ; var fnPtr = methods . add ( idx + p_size * 2 ) . readPointer ( ) ; const module = Process . getModuleByAddress ( fnPtr ) ; if ( module ) { const modulename = module . name ; const modulebase = module . base ; var logstr = "name: " + methods . add ( idx ) . readPointer ( ) . readCString ( ) + ", signature: " + methods . add ( idx + p_size ) . readPointer ( ) . readCString ( ) + ", fnPtr: " + fnPtr + ", modulename: " + modulename + " -> base: " + modulebase ; if ( null != modulebase ) { logstr += ", offset: " + fnPtr . sub ( modulebase ) ; } DMLog . i ( tag , logstr ) ; } else { DMLog . e ( tag , 'module is null' ) ; } } } } ) ; } /**
     * trace 所有 Jni 方法
     * 可以配合 `python/android/traceLogCleaner.py` 脚本，格式化输出日志
     */ export function traceAllJNISimply ( ) { jni_struct_array . forEach ( function ( func_name , idx ) { if ( ! func_name . includes ( "reserved" ) ) { Jni . hookJNI ( func_name , { onEnter ( args ) { let md = new MethodData ( this . context , func_name , JNI_ENV_METHODS [ idx ] , args ) ; this . md = md ; } , onLeave ( retval ) { this . md . setRetval ( retval ) ; send ( JSON . stringify ( { tid : this . threadId , status : "jnitrace" , data : this . md } ) ) ; } } ) ; } } ) ; } }