Android JNI NDK C++ so敏感信息保护

一. 前言

这几天在做项目的时候,需要对敏感信息进行保护,防止反编译泄露,做个笔记吧。

二. 集成JNI

JNI的全称是Java Native Interface,它允许Java语言可以按照一定的规则去调用其他语言,与其进行交互。

1. 配置NDK环境

没有使用CMake,使用了NDK方式

NDK配置
为何要用到NDK?

概括来说主要分为以下几种情况:

  1. 代码的保护。由于 apk 的 java 层代码很容易被反编译,而 C/C++ 库反汇难度较大。
  2. 提高程序的执行效率。将要求高性能的应用逻辑使用 C 开发,从而提高应用程序的执行效率。
  3. 便于移植。用 C/C++ 写得库可以方便在其他的嵌入式平台上再次使用。
2. 配置app下的build.gradle
3. 在项目下gradle.properties文件添加
android.useDeprecatedNdk=true
4. 调用c的java类

这个工具类的地方必须跟你将来要调用so库文件的工具类的地方的包名必须一致

public class TestUtils {
    static {
        //加载本地库文件 (省略前缀lib和后缀.so)
        System.loadLibrary("testUtil");//testUtil就是上一步在build.gradle中配置的moduleName so的名字
    }
    public static native String test(Object obj, boolean isRelease);
}
5. 生成JNI的头文件

我们先了解下NDK与JNI的关系

在JNI开发中我们的Java Native方法都会对应到c语言中的Native方法。上一步我们在Java中已声明了native方法,现在需要用javah命令来生成头文件。

有两种方式:
1. 用javah命令生成C语言头文件

切换到源文件根目录下: cd app/src/main/java
生成C语言头文件: javah -d jni -jni com.ghp.test.TestUtils

2. 扩展工具生成C语言头文件

AS扩展工具, 可以让我们自定义一些命令行工具.比如说: 自动生成JNI头文件, 打debug/release包等等.
下面就来看看如何利用Android Studio External Tools机制类实现这个功能.

a. 操作步骤:

Perferences -> External Tools -> 点击"加号"添加一个扩展工具 -> 填写工具信息
如下图所示:

b. 工具使用:

操作步骤: 在native方法所在的类上右键 -> My Tools -> 点击javah (generate c header file), 如下图:


这时会在main目录下面生成jni目录且在此目录下生成C头文件.

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_ghp_test_TestUtils */

#ifndef _Included_com_ghp_test_TestUtils
#define _Included_com_ghp_test_TestUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_ghp_test_TestUtils
 * Method:    test
 * Signature: (Ljava/lang/Object;Z)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_ghp_test_TestUtils_test
  (JNIEnv *, jclass, jobject, jboolean);

#ifdef __cplusplus
}
#endif
#endif
6. 创建c/c++文件

在jni文件夹下创建一个c/c++文件

testUtils.c实现了com_ghp_test_TestUtils.h文件,验签ok返回ok。

注意路径不要写错了,生成的 .h 文件里包含自动生成的一些方法,方法名称一一应对 Java native 方法。

在这里include了sign.h,是验证签名方法的头文件,从目录看头文件的实现是sign.cpp。是c调用c++,需要在sign.h里添加extern "c",不然会报has a different language linkage。

还有个Android.mk文件,这里配置了so文件的name(同时ndk里配置moduleName将失效),和使用的c/c++文件

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := testUtil //so库名
LOCAL_SRC_FILES := testUtils.c sign.cpp //逻辑文件罗列

include $(BUILD_SHARED_LIBRARY)

同时需要在module的build.gradle里配置:

// ndk-build模式
   externalNativeBuild {
        ndkBuild {
            path "src/main/jni/Android.mk"
        }
    }

验签的实现部分:

  1. java先获取APP签名
public static String getSignature(Context context)
        {
            try {
                /** 通过包管理器获得指定包名包含签名的包信息 **/
                PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
                /******* 通过返回的包信息获得签名数组 *******/
                Signature[] signatures = packageInfo.signatures;
                /******* 循环遍历签名数组拼接应用签名 *******/
                return signatures[0].toCharsString();
                /************** 得到应用签名 **************/
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return null;
}
  1. c++代码的实现
#include <jni.h>
#include <string.h>
#include <stdio.h>
#include "sign.h"

/**
 * 发布的app 签名,只有和本签名一致的app 才会返回 JNI_TRUE
 * 这个RELEASE_SIGN的值是上一步用java代码获取的值
 */
const char *RELEASE_SIGN = "3082036f3……………e6d0516a";

int signResult(JNIEnv *env, jobject contextObject) {
    jclass native_class = env->GetObjectClass(contextObject);
    jmethodID pm_id = env->GetMethodID(native_class, "getPackageManager", "()Landroid/content/pm/PackageManager;");
    jobject pm_obj = env->CallObjectMethod(contextObject, pm_id);
    jclass pm_clazz = env->GetObjectClass(pm_obj);
    // 得到 getPackageInfo 方法的 ID
    jmethodID package_info_id = env->GetMethodID(pm_clazz, "getPackageInfo",
                                                 "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
    jclass native_classs = env->GetObjectClass(contextObject);
    jmethodID mId = env->GetMethodID(native_classs, "getPackageName", "()Ljava/lang/String;");
    jstring pkg_str = static_cast<jstring>(env->CallObjectMethod(contextObject, mId));
    // 获得应用包的信息
    jobject pi_obj = env->CallObjectMethod(pm_obj, package_info_id, pkg_str, 64);
    // 获得 PackageInfo 类
    jclass pi_clazz = env->GetObjectClass(pi_obj);
    // 获得签名数组属性的 ID
    jfieldID signatures_fieldId = env->GetFieldID(pi_clazz, "signatures", "[Landroid/content/pm/Signature;");
    jobject signatures_obj = env->GetObjectField(pi_obj, signatures_fieldId);
    jobjectArray signaturesArray = (jobjectArray) signatures_obj;
    jsize size = env->GetArrayLength(signaturesArray);
    jobject signature_obj = env->GetObjectArrayElement(signaturesArray, 0);
    jclass signature_clazz = env->GetObjectClass(signature_obj);

    //第一种方式--检查签名字符串的方式
    jmethodID string_id = env->GetMethodID(signature_clazz, "toCharsString", "()Ljava/lang/String;");
    jstring str = static_cast<jstring>(env->CallObjectMethod(signature_obj, string_id));
    char *c_msg = (char *) env->GetStringUTFChars(str, 0);

    return strcmp(c_msg, RELEASE_SIGN) == 0 ? JNI_TRUE : JNI_FALSE;//校验签名一致
}
7. 使用so文件

build项目在目录下找到so文件

将他们拷贝到APP的libs对应文件夹,去除C文件的使用,同时使用的项目里build.gradle也需要配置abiFilters。
在sign.cpp,可以添加判断当前 APP 是否为合法应用,对验签增加MD5。

到这里觉得可以了呢,当看这里《逆向札记-利用IDA简单过so签名校验》,给这篇文章跪了,详细的介绍了破解安卓NDK端native方法动态JNI反射so文件签名校验的方法,敏感信息放so文件只签验看来是不行了,好吧,其他敏感信息再加一层RSA加解密处理吧……

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容