JNI Hook java层方法


测试应用程序界面:
点击“SayHello”按钮时,旁边的文本标签显示字符串“MainActivity hello”,点击“GetMac”按钮时,旁边的文本标签显示当前mac地址。
代码:
package com.example.jnihookjava; import android.app.Activity; import android.content.Context; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; importandroid.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; importandroid.view.ViewGroup; import android.os.Build; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity { public staticfinal String TAG = "MainActivity"; static { Log.e(TAG, "static code"); System.loadLibrary("so"); } private Button btn_hook; private Button btn_hello; private Button btn_mac; private TextView tv_hello; private TextView tv_mac; private TextView tv_hook; private String hello() { return TAG + " hello"; } private native String hook(); @Override protected void onCreate(Bundle savedInstanceState) { Log.e(TAG, "onCreate"); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_hook = (Button)findViewById(R.id.btn_hook); btn_hello = (Button)findViewById(R.id.btn_hello); btn_mac = (Button)findViewById(R.id.btn_mac); tv_hello = (TextView)findViewById(R.id.tv_hello); tv_mac = (TextView)findViewById(R.id.tv_mac); tv_hook = (TextView)findViewById(R.id.tv_hook); btn_hook.setOnClickListener(newView.OnClickListener() { @Override public void onClick(View v) { tv_hook.setText(hook()); } }); btn_hello.setOnClickListener(new View.OnClickListener() { @Override publicvoid onClick(View v) { tv_hello.setText(hello()); } }); btn_mac.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE); WifiInfo info = wifi.getConnectionInfo(); tv_mac.setText(info.getMacAddress()); } }); } @Override public booleanonCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } }
activity_main布局文件:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Buttonandroid:layout_width="wrap_content" android:layout_height="wrap_content" android:text="SayHello" android:id="@+id/btn_hello" /> <TextView android:id="@+id/tv_hello"android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> <LinearLayout android:layout_width="match_parent"android:layout_height="wrap_content"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="GetMac" android:id="@+id/btn_mac" /><TextView android:id="@+id/tv_mac" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> <LinearLayoutandroid:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content"android:text="hook" android:id="@+id/btn_hook" /> <TextView android:id="@+id/tv_hook" android:layout_width="wrap_content" android:layout_height="wrap_content" /></LinearLayout> </LinearLayout>
AndroidManifest.xml需要添加权限:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

按钮“hook”调用libso.so导出的函数hook:
extern "C" JNIEXPORT jstring JNICALL Java_com_example_jnihookjava_MainActivity_hook(JNIEnv *env, jclass clazz) { Hook(); return env->NewStringUTF("hook ok");; }

#include "MethodHooker.h" #include "jni.h" #include "android_runtime/AndroidRuntime.h" #include "android/log.h" #include "stdio.h" #include "stdlib.h" #include "native.h"#include <dlfcn.h> #define ANDROID_SMP 0 #include "Dalvik.h" #include "alloc/Alloc.h" #define ALOG(...) __android_log_print(ANDROID_LOG_VERBOSE, __VA_ARGS__) static boolg_bAttatedT; static JavaVM *g_JavaVM; void init() { g_bAttatedT = false; g_JavaVM = android::AndroidRuntime::getJavaVM(); } static JNIEnv *GetEnv() { int status; JNIEnv *envnow = NULL; status = g_JavaVM->GetEnv((void **)&envnow, JNI_VERSION_1_4); if(status < 0) { status = g_JavaVM->AttachCurrentThread(&envnow, NULL); if(status < 0) {return NULL; } g_bAttatedT = true; } return envnow; } static void DetachCurrent() { if(g_bAttatedT) { g_JavaVM->DetachCurrentThread(); } } static intcomputeJniArgInfo(const DexProto* proto) { const char* sig = dexProtoGetShorty(proto); int returnType, jniArgInfo; u4 hints; /* The first shorty character is the return type. */ switch (*(sig++)) { case 'V': returnType = DALVIK_JNI_RETURN_VOID; breakcase 'F': returnType = DALVIK_JNI_RETURN_FLOAT; breakcase 'D': returnType = DALVIK_JNI_RETURN_DOUBLE; breakcase 'J': returnType = DALVIK_JNI_RETURN_S8; breakcase 'Z'case 'B': returnType = DALVIK_JNI_RETURN_S1; breakcase 'C': returnType = DALVIK_JNI_RETURN_U2; breakcase 'S': returnType = DALVIK_JNI_RETURN_S2; breakdefault: returnType = DALVIK_JNI_RETURN_S4; break; } jniArgInfo = returnType << DALVIK_JNI_RETURN_SHIFT; hints = dvmPlatformInvokeHints(proto); if (hints & DALVIK_JNI_NO_ARG_INFO) { jniArgInfo |= DALVIK_JNI_NO_ARG_INFO; } else { assert((hints & DALVIK_JNI_RETURN_MASK) == 0); jniArgInfo |= hints; } return jniArgInfo; } int ClearException(JNIEnv *jenv){ jthrowable exception = jenv->ExceptionOccurred(); if(exception != NULL) { jenv->ExceptionDescribe(); jenv->ExceptionClear(); return true; } return false; } bool isArt(){ return false; } static jclass findAppClass(JNIEnv *jenv,const char *apn){ //获取Loaders jclass clazzApplicationLoaders = jenv->FindClass("android/app/ApplicationLoaders"); jthrowable exception = jenv->ExceptionOccurred();if (ClearException(jenv)) { ALOG("Exception","No class : %s""android/app/ApplicationLoaders"); return NULL; } jfieldID fieldApplicationLoaders = jenv->GetStaticFieldID(clazzApplicationLoaders,"gApplicationLoaders","Landroid/app/ApplicationLoaders;"); if (ClearException(jenv)) { ALOG("Exception","No Static Field :%s","gApplicationLoaders"); return NULL; } jobject objApplicationLoaders = jenv->GetStaticObjectField(clazzApplicationLoaders,fieldApplicationLoaders); if(ClearException(jenv)) { ALOG("Exception","GetStaticObjectField is failed [%s","gApplicationLoaders"); return NULL; } jfieldID fieldLoaders = jenv->GetFieldID(clazzApplicationLoaders,"mLoaders","Ljava/util/Map;"); if (ClearException(jenv)) { ALOG("Exception","No Field :%s","mLoaders"); return NULL; } jobject objLoaders = jenv->GetObjectField(objApplicationLoaders,fieldLoaders); if (ClearException(jenv)) { ALOG("Exception","No object :%s","mLoaders"); return NULL; } //提取map中的values jclass clazzHashMap = jenv->GetObjectClass(objLoaders); jmethodID methodValues = jenv->GetMethodID(clazzHashMap,"values","()Ljava/util/Collection;"); jobject values = jenv->CallObjectMethod(objLoaders,methodValues); jclass clazzValues = jenv->GetObjectClass(values); jmethodID methodToArray = jenv->GetMethodID(clazzValues,"toArray","()[Ljava/lang/Object;"); if (ClearException(jenv)) { ALOG("Exception","No Method:%s","toArray"); return NULL; } jobjectArray classLoaders = (jobjectArray)jenv->CallObjectMethod(values,methodToArray); if (ClearException(jenv)) { ALOG("Exception","CallObjectMethod failed :%s","toArray"); returnNULL; } int size = jenv->GetArrayLength(classLoaders); for(int i = 0 ; i < size ; i ++){ jobject classLoader = jenv->GetObjectArrayElement(classLoaders,i); jclass clazzCL = jenv->GetObjectClass(classLoader); jmethodID loadClass = jenv->GetMethodID(clazzCL,"loadClass","(Ljava/lang/String;)Ljava/lang/Class;"); jstring param = jenv->NewStringUTF(apn); jclass tClazz = (jclass)jenv->CallObjectMethod(classLoader,loadClass,param); if (ClearException(jenv)) { ALOG("Exception","No"); continue; } returntClazz; } ALOG("Exception","No"); return NULL; } bool HookDalvikMethod(jmethodID jmethod){ Method *method = (Method*)jmethod; //关键!!将目标方法修改为native方法SET_METHOD_FLAG(method, ACC_NATIVE); int argsSize = dvmComputeMethodArgsSize(method); if (!dvmIsStaticMethod(method)) argsSize++; method->registersSize = method->insSize = argsSize; if (dvmIsNativeMethod(method)) { method->nativeFunc = dvmResolveNativeMethod; method->jniArgInfo = computeJniArgInfo(&method->prototype); } } boolClassMethodHook(HookInfo info){ JNIEnv *jenv = GetEnv(); jclass clazzTarget = jenv->FindClass(info.tClazz); if (ClearException(jenv)) { ALOG("Exception","ClassMethodHook[Can't find class:%s in bootclassloader",info.tClazz); clazzTarget = findAppClass(jenv,info.tClazz); if(clazzTarget == NULL){ ALOG("Exception","%s","Error in findAppClass"); return false; } } jmethodID method = jenv->GetMethodID(clazzTarget,info.tMethod,info.tMeihodSig); if(method==NULL){ ALOG("Exception","ClassMethodHook[Can't find method:%s",info.tMethod); return false; } if(isArt()){ //HookArtMethod(jenv,method); }else{ HookDalvikMethod(method); } JNINativeMethod gMethod[] = { {info.tMethod, info.tMeihodSig, info.handleFunc}, }; //func为NULL时不自行绑定,后面扩展吧 if(info.handleFunc != NULL){ //关键!!将目标方法关联到自定义的native方法 if (jenv->RegisterNatives(clazzTarget, gMethod, 1) < 0) { ALOG("RegisterNatives","err"); return false; } } DetachCurrent(); return true; } JNIEXPORT jstring JNICALL hello(JNIEnv *env, jclass clazz) { //__android_log_print(ANDROID_LOG_VERBOSE, "tag", "call <native_printf> in java"); return env->NewStringUTF("hello from native");; } JNIEXPORT jstring JNICALL getmac(JNIEnv *env, jclass clazz) { //__android_log_print(ANDROID_LOG_VERBOSE, "tag", "call <native_printf> in java"); return env->NewStringUTF("getmac from native");; } HookInfo hookInfos[] = { {"android/net/wifi/WifiInfo","getMacAddress","()Ljava/lang/String;",(void*)getmac}, {"com/example/jnihookjava/MainActivity","hello","()Ljava/lang/String;",(void*)hello}, //{"android/app/ApplicationLoaders","getText","()Ljava/lang/CharSequence;",(void*)test},}; int getpHookInfo(HookInfo** pInfo){ *pInfo = hookInfos; return sizeof(hookInfos) / sizeof(hookInfos[0]); } int Hook(){ init(); ClassMethodHook(hookInfos[0]); ClassMethodHook(hookInfos[1]); }

测试效果:
点击“hook”按钮显示“hook ok”说明hook成功,再次点击“SayHello”、“GetMac”按钮分别显示出底层native函数返回的结果:

由于是在当前程序中做的替换修改,因此手机无需root。如果是注入到其他进程则需要root,注入其他进程并hook其java层方法的流程:
使用注入器inject将libso.so注入并调用其一个函数,该函数再调用Hook函数完成函数修改,后面代码相同。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

asmcvc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值