使用JNI获取Android apk签名hashcode

xiaoxiao2021-02-28  118

  最近在研究Android应用中的安全问题,貌似只有将核心代码写到JNI底层才是最安全的。通过底层来判断签名是否正确,如果正确则继续执行核心代码,否则退出程序,这样就可以防止别人恶意反编译,并进行二次打包。所以这里的关键就是如何在JNI中获得签名。

  我上网查了好多资料,都没有现成的答案,但是我慢慢的找到了一些思路,于是潜心研究,终于有了结果。不敢独享,所以过来分享给大家。

  大家都知道,在android中的Java代码里获得签名的哈希值,很简单,过程如下(此代码与下面下载地址的代码有所不同,已经修改了,多谢五楼 shysnower 提出的修改意见):

[java]  view plain  copy try {       PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 64);              Signature sign = info.signatures[0];              Log.i("test""hashCode : " + sign.hashCode());          catch (NameNotFoundException e) {       e.printStackTrace();   }  

  在JNI中提供了许多方法,可以反向调用java中的方法,比如下面一句代码:

    PackageInfo packageInfo = getPackageManager().getPackageInfo("com.klxx.as", PackageManager.GET_SIGNATURES);

  我们可以用JNI写成这样(此代码与下面下载地址的代码有所不同,已经修改了,多谢五楼 shysnower 提出的修改意见):

[cpp]  view plain  copy // 获得 Context 类   jclass native_clazz = (*env)->GetObjectClass(env, context);      // 得到 getPackageManager 方法的 ID   jmethodID methodID_func = (*env)->GetMethodID(env, native_clazz,           "getPackageManager""()Landroid/content/pm/PackageManager;");      // 获得应用包的管理器   jobject package_manager = (*env)->CallObjectMethod(env, thiz, methodID_func);      // 获得 PackageManager 类   jclass pm_clazz = (*env)->GetObjectClass(env, package_manager);      // 得到 getPackageInfo 方法的 ID   jmethodID methodID_pm = (*env)->GetMethodID(env, pm_clazz,           "getPackageInfo""(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");      // 得到 getPackageName 方法的 ID   jmethodID methodID_pack = (*env)->GetMethodID(env, native_clazz,           "getPackageName""()Ljava/lang/String;");         // 获得当前应用的包名   jstring application_package = (*env)->CallObjectMethod(env, context, methodID_pack);         // 获得应用包的信息   jobject package_info = (*env)->CallObjectMethod(env, package_manager,           methodID_pm, application_package, 64);  

  这种方法在java中叫做反射,更多的JNI反射方法可以参考博客《android开发之绝对安全(三) JNI方法集合》。

  通过类似于这种反射机制,我进行一步一步调试和解析,终于获得了应用的签名信息,并从签名信息中获得了签名的哈希值。

  我将这段代码传到了CSDN上,欢迎大家下载,如果有什么漏洞,也欢迎大家指点一下。

  下载地址:http://download.csdn.net/detail/iloveyoueveryday/6909583 。

  源码使用注意事项:一定要传过来正确的context参数;项目中的包名("com.example.hellojni")别忘了修改;JNI的代码使用不是很容易,请耐心修改调试,我也是调了好久才测试通过的。

转载:http://blog.csdn.net/i5suoi/article/details/19036975

1. [代码][Java]代码     

? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int checkAPP(Context context) {      try {          PackageInfo packageInfo = context.getPackageManager()                  .getPackageInfo(context.getPackageName(),                          PackageManager.GET_SIGNATURES);          Signature[] signs = packageInfo.signatures;          Signature sign = signs[ 0 ];                    int hashcode = sign.hashCode();          Log.i( "test" , "hashCode : " + hashcode);          return hashcode == - 82892576 ? 1 : 0 ;      } catch (Exception e) {          e.printStackTrace();      }      return - 1 ; }

2. [代码][C/C++]代码     

? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 #include <string.h> #include <stdio.h> #include <jni.h> #include<ALog.h>   jvalue JNU_CallMethodByName(JNIEnv *env, jboolean *hasException, jobject obj,          const char *name, const char *descriptor, ...) {      va_list args;      jclass clazz;      jmethodID mid;      jvalue result;      if ((*env)->EnsureLocalCapacity(env, 2) == JNI_OK) {          clazz = (*env)->GetObjectClass(env, obj);          mid = (*env)->GetMethodID(env, clazz, name, descriptor);          if (mid) {              const char *p = descriptor;              /* skip over argument types to find out the                return type */              while (*p != ')' )                  p++;              /* skip ')' */              p++;              va_start (args, descriptor);              switch (*p) {              case 'V' :                  (*env)->CallVoidMethodV(env, obj, mid, args);                  break ;              case '[' :              case 'L' :                  result.l = (*env)->CallObjectMethodV(env, obj, mid, args);                  break ;              case 'Z' :                  result.z = (*env)->CallBooleanMethodV(env, obj, mid, args);                  break ;              case 'B' :                  result.b = (*env)->CallByteMethodV(env, obj, mid, args);                  break ;              case 'C' :                  result.c = (*env)->CallCharMethodV(env, obj, mid, args);                  break ;              case 'S' :                  result.s = (*env)->CallShortMethodV(env, obj, mid, args);                  break ;              case 'I' :                  result.i = (*env)->CallIntMethodV(env, obj, mid, args);                  break ;              case 'J' :                  result.j = (*env)->CallLongMethodV(env, obj, mid, args);                  break ;              case 'F' :                  result.f = (*env)->CallFloatMethodV(env, obj, mid, args);                  break ;              case 'D' :                  result.d = (*env)->CallDoubleMethodV(env, obj, mid, args);                  break ;              default :                  (*env)->FatalError(env, "illegaldescriptor" );              }              va_end (args);          }          (*env)->DeleteLocalRef(env, clazz);      }      if (hasException) {          *hasException = (*env)->ExceptionCheck(env);      }      return result; }   //合法的APP包名 const char *global_app_packageName = "com.example.abc" ; //合法的hashcode const int global_app_signature_hash_code = -82892576; //合法标记 int legitimate = 0; jint Java_com_example_abc_MainActivity_jniCheckAPP(JNIEnv* env, jobject context,          jobject thiz) {      LOGI( "start jniCheckAPP" );        // 获得 Context 类      jboolean hasException;        //获取包名      jstring jstr_packageName = (jstring) JNU_CallMethodByName(env,              &hasException, thiz, "getPackageName" , "()Ljava/lang/String;" ).l;        if ((*env)->ExceptionCheck(env) || jstr_packageName == NULL) {          LOGI( "can't get jstr of getPackageName" );          return -1;      }      //获取包名的字符串      const char * loc_str_app_packageName = (*env)->GetStringUTFChars(env,              jstr_packageName, NULL);      if (loc_str_app_packageName == NULL) {          LOGI( "can't get packagename from jstring" );          return -2;      }      //当前应用包名与合法包名对比      if ( strcmp (loc_str_app_packageName, global_app_packageName) != 0) {          LOGI( "this app is illegal" );          return -3;      }        //释放loc_str_app_packageName      (*env)->ReleaseStringUTFChars(env, jstr_packageName,              loc_str_app_packageName);        // 获得应用包的管理器      jobject package_manager = JNU_CallMethodByName(env, &hasException, thiz,              "getPackageManager" , "()Landroid/content/pm/PackageManager;" ).l;      if ((*env)->ExceptionCheck(env) || package_manager == NULL) {          LOGI( "can't get obj of getPackageManager" );          return -4;      }        // 获得应用包的信息      jobject package_info = JNU_CallMethodByName(env, &hasException,              package_manager, "getPackageInfo" ,              "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;" ,              (*env)->NewStringUTF(env, global_app_packageName), 64).l;      if ((*env)->ExceptionCheck(env) || package_info == NULL) {          (*env)->ExceptionClear(env);          LOGI( "can't get obj of package_info" );          return -5;      }        // 获得 PackageInfo 类      jclass pi_clazz = (*env)->GetObjectClass(env, package_info);        // 获得签名数组属性的 ID      jfieldID fieldID_signatures = (*env)->GetFieldID(env, pi_clazz,              "signatures" , "[Landroid/content/pm/Signature;" );      (*env)->DeleteLocalRef(env, pi_clazz);      // 得到签名数组,待修改      jobjectArray signatures = (*env)->GetObjectField(env, package_info,              fieldID_signatures);        if ((*env)->ExceptionCheck(env) || signatures == NULL) {          LOGI( "can't get jobjectArray of signatures" );          return -6;      }        // 得到签名      jobject signature = (*env)->GetObjectArrayElement(env, signatures, 0);      if ((*env)->ExceptionCheck(env) || signature == NULL) {          LOGI( "can't get obj of signature" );          return -7;      }      //获取当前应用hashcode      int hash_code = JNU_CallMethodByName(env, &hasException, signature,              "hashCode" , "()I" ).i;      if ((*env)->ExceptionCheck(env) || package_manager == NULL) {          LOGI( "can't get hash_code of signature" );          return -8;      }        LOGI( "this app hash_code of signature is %d" , hash_code);      //合法返回1,否则返回0,并改变legitimate的值      return legitimate = (hash_code == global_app_signature_hash_code); }   //===================================================================== typedef union {      JNIEnv* env;      void * venv; } UnionJNIEnvToVoid; static JNINativeMethod methods[] = { { "jniCheckAPP" ,          "(Landroid/content/Context;)I" ,          ( void *) Java_com_example_abc_MainActivity_jniCheckAPP } };   static const char *classPathName = "com/example/abc/MainActivity" ;   static int registerNativeMethods(JNIEnv* env, const char * className,          JNINativeMethod* gMethods, int numMethods) {      jclass clazz;      clazz = (*env)->FindClass(env, className);      if (clazz == NULL) {          return JNI_FALSE;      }      if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {          LOGE( "RegisterNatives failed" );          return JNI_FALSE;      }      return JNI_TRUE; } static int registerNatives(JNIEnv* env) {      if (!registerNativeMethods(env, classPathName, methods,              sizeof (methods) / sizeof (methods[0]))) {          return JNI_FALSE;      }      return JNI_TRUE; }   jint JNI_OnLoad(JavaVM* vm, void * reserved) {      UnionJNIEnvToVoid uenv;      uenv.venv = NULL;      jint result = -1;      JNIEnv* env = NULL;      if ((*vm)->GetEnv(vm, &uenv.venv, JNI_VERSION_1_4) != JNI_OK) {          goto bail;      }      env = uenv.env;      if (registerNatives(env) != JNI_TRUE) {            goto bail;      }      result = JNI_VERSION_1_4;      bail: LOGI( "JNI_ONload result '%d' " , result);      return result; }

3. [代码][C/C++]代码     

? 1 2 3 4 5 6 7 8 #pragma once   #include<android/log.h>   #define LOG_TAG "debug log" #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##args) #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args) #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##args)

4. [代码]Android.mk     

? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # #      http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # LOCAL_PATH := $(call my-dir)   include $(CLEAR_VARS)   LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog   LOCAL_MODULE    := checkapp-jni LOCAL_SRC_FILES := jni.c   include $(BUILD_SHARED_LIBRARY)

5. [代码]Application.mk     

? 1 APP_ABI := armeabi armeabi-v7a x86 转载:https://www.oschina.net/code/snippet_196085_37562

转载请注明原文地址: https://www.6miu.com/read-37864.html

最新回复(0)