Android NDK jni开发,适当的给Android 代码加密

为什么使用ndk开发了,就是 稍微将代码隐藏一下。。Android 虽然打包能混淆,但是有些东西是不能混淆的,如下图,压缩密码把直勾勾的下载代码中。下面手把手稍微优化下。

將操作密码的部分我们通过 jni开发,在C++里面操作。

打开APP build.gradle进行配置NDK信息,配置CMake.

 defaultConfig {

        externalNativeBuild {
            cmake {
                cppFlags '-std=c++11'
                abiFilters "arm64-v8a", "armeabi-v7a"
            }
        }
    }
android{

    externalNativeBuild {
        cmake {
            path file('src/main/cpp/CMakeLists.txt') //这里可以使任意路径,我就放在跟Java代码同级了
            version '3.10.2'
        }
    }
    ndkVersion '21.0.6113669' //自己选一个你本机安装过的 ndk版本

}

接下来创建CMake文件,如果包含多个的话,就多赋值几个lib就好了。

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.10.2)

# Declares and names the project.

project("RiverwayApplication")

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.


add_library( # Sets the name of the library.
        xm-lib

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        xm-lib.cpp)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)


# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.


target_link_libraries( # Specifies the target library.
        xm-lib

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

基本配置好了我们接下来就开始写cpp了。

首先创建好我们的Java类,并设置好native方法,

下面是C++代码!!通过反射调用,这样我们就把密码隐藏到C中!本人只是在学习c++中,可能这里还有优化的地方!!! 

extern "C"
JNIEXPORT void JNICALL
Java_com_xm_j_XmJni_inputPassword(JNIEnv *env, jclass clazz, jobject zip_file) {
    //拿到调用的对象实例,根据实例 获取class,在根据class获取 isEncrypted方法,()Z 返回布尔型不懂的话就多百度看看方法签名
    jmethodID isEncryptedMethod = env->GetMethodID(env->GetObjectClass(zip_file), "isEncrypted",
                                                   "()Z");
    //执行方法验证
    jboolean isEncrypted = env->CallBooleanMethod(zip_file, isEncryptedMethod);

    if (isEncrypted) {
        std::string str = "258258258";//密码写在这里。
        cin >> str;
        int len = str.size() + 1;
        char *arr = new char[len];
        strcpy(arr, str.c_str());
        //1.反射Java类中的setPassword方法,(根据对象拿到class,根据class拿方法)
        jmethodID setPassword = env->GetMethodID(env->GetObjectClass(zip_file), "setPassword",
                                                 "([C)V");
        //2.因为调用Java方法,所以需要将密码转正 jchar。
        jcharArray array = env->NewCharArray(len);
        jchar *pArray;
        pArray = (jchar *) calloc(len, sizeof(jchar));
        for (int i = 0; i < len; i++) {
            *(pArray + i) = *(arr + i);
        }
        env->SetCharArrayRegion(array, 0, len, pArray);
        //3.反射调用方法。
        env->CallVoidMethod(zip_file, setPassword, array);

        free(pArray);
        delete[] arr;
    }
}

下面是我个人学习的Java类和完整的C++代码。后续有补充在说吧。

Java类。包名:com.xm.j

public class XmJni {

    static {
        System.loadLibrary("xm");
    }

    public static native long callTime();

    //这里的application 子类都行。你可以自定义更换这里的application
    public static native Application getApp();

    public static native void numberFunctionAddStr();

    public static native void cppPointer();//指针和应用相关。

    public static native void dataType();//c++数据结构

    public static native void jstring2char(String j);

    public static native void fstream();

}

c++类有点杂乱无章自己凑合看吧:

#include <jni.h>
#include <string>
#include <sstream>
#include <android/log.h>

using namespace std;

#include <iostream>

#include "cmath"


static const char *kTAG = "xiaoma_JNI";

#ifndef FINENGINE_LOG_H
#define FINENGINE_LOG_H
//#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,kTAG,__VA_ARGS__)
//#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,kTAG,__VA_ARGS__)
#define  MLog(...)  __android_log_print(ANDROID_LOG_ERROR,kTAG,__VA_ARGS__)
#endif

extern "C"
JNIEXPORT void JNICALL
Java_com_xm_j_XmJni_inputPassword(JNIEnv *env, jclass clazz, jobject zip_file) {
    //拿到调用的对象实例,根据实例 获取class,在根据class获取 isEncrypted方法,()Z 返回布尔型不懂的话就多百度看看方法签名
    jmethodID isEncryptedMethod = env->GetMethodID(env->GetObjectClass(zip_file), "isEncrypted",
                                                   "()Z");
    //执行方法验证
    jboolean isEncrypted = env->CallBooleanMethod(zip_file, isEncryptedMethod);

    if (isEncrypted) {
        std::string str = "258258258";//密码写在这里。
        cin >> str;
        int len = str.size() + 1;
        char *arr = new char[len];
        strcpy(arr, str.c_str());
        //1.反射Java类中的setPassword方法,(根据对象拿到class,根据class拿方法)
        jmethodID setPassword = env->GetMethodID(env->GetObjectClass(zip_file), "setPassword",
                                                 "([C)V");
        //2.因为调用Java方法,所以需要将密码转正 jchar。
        jcharArray array = env->NewCharArray(len);
        jchar *pArray;
        pArray = (jchar *) calloc(len, sizeof(jchar));
        for (int i = 0; i < len; i++) {
            *(pArray + i) = *(arr + i);
        }
        env->SetCharArrayRegion(array, 0, len, pArray);
        //3.反射调用方法。
        env->CallVoidMethod(zip_file, setPassword, array);

        free(pArray);
        delete[] arr;
    }
}

//使用int64_t 接收。这样才是java层的long!!
//unsigned 无符号正数,, signed有符号,+ - 是正数的一半
string longToString(int64_t t) {
    std::string result;
    stringstream ss;
    ss << t;
    ss >> result;
    return result;
}


extern "C"
JNIEXPORT jlong JNICALL
Java_com_xm_j_XmJni_callTime(JNIEnv *env, jclass clazz) {
    jclass j = env->FindClass("java/util/Date");
    jmethodID jm = env->GetMethodID(j, "<init>", "()V");
    jobject jNewObject = env->NewObject(j, jm);

    jmethodID jmetthodIdTime = env->GetMethodID(j, "getTime", "()J");
    jlong jresultTime = env->CallLongMethod(jNewObject, jmetthodIdTime);

    //如果类型对不上的话。那么看到的值肯定就是错误的。。一定要先确认类型对的上。通过Java反射创建的对象获取的long。。对应c++的 int64_t
    MLog("打印的%s", longToString(jresultTime).c_str());

    unsigned int d = 0;
    unsigned long f = 0;
    MLog("d定义,自定初始化%d,%lu", d, f);

    std::string s1 = "xiao ming";
    std::string s2 = "wo cao";
    MLog("%s", (s1 +
                s2).c_str()); //如果要传递给某个函数时候。则调用  some_c_api(s.c_str(), s.size());   some_c_api(char const *input, size_t length);

//    for (unsigned int i = 1, countI = 9; i <= countI; i++) {
//        for (unsigned int j = 1, countJ = i; j <= countJ; j++) {
//            MLog("%d * %d = %d", i, j, i * j);
//        }
//    }

    return jresultTime;
}


extern "C"
JNIEXPORT jobject JNICALL
Java_com_xm_j_XmJni_getApp(JNIEnv *env, jclass clazz) {
    jclass activityThread = env->FindClass("android/app/ActivityThread");
    jmethodID currentActivityThread = env->GetStaticMethodID(activityThread,
                                                             "currentActivityThread",
                                                             "()Landroid/app/ActivityThread;");
    jobject at = env->CallStaticObjectMethod(activityThread, currentActivityThread);
    //获取Application,也就是全局的Context
    jmethodID getApplication = env->GetMethodID(activityThread, "getApplication",
                                                "()Landroid/app/Application;");
    jobject context = env->CallObjectMethod(at, getApplication);

    return context;
}


extern "C"
JNIEXPORT void JNICALL
Java_com_xm_j_XmJni_numberFunctionAddStr(JNIEnv *env, jclass clazz) {
    //数学函数,Java也提供了    https://www.runoob.com/cplusplus/cpp-numbers.html
    int i, j;
    // 设置用时间做随机种子
    srand((unsigned) time(NULL));

    /* 生成 10 个随机数 */
    for (i = 0; i < 3; i++) {
        // 生成实际的随机数
        j = rand();
        MLog("当前随机数:%d", j);
    }

    //c++ 连接字符串
    std::string hello = "hello word";
    hello.append("111");
    MLog("c++的String类更简单%s", hello.c_str());

    //c 链接字符串
    char str1[] = "hello ";
    char str2[] = "word";
    //方法一
    char str3[strlen(str1) + strlen(str2) + 1];
    str3[0] = '\0';//没有初始化,第一个元素设置为空!!这样的话就不需要像下面那样 设置足够大的值了。
    strcat(str3, str1);
    strcat(str3, str2);
    //str2 追加到 str1里面。str1需要足够大!!!方法二
//    char *s = strcat(str1, str2);
    MLog("链接后的字符串:%s", str3);

    char *cat = strstr(str3, "wo"); //如果没有找到返回null,找到了就是返回这个字符串的指针
    if (cat == NULL) {
        MLog("查找字符串:%s,没有找到这个字符串", cat);
    } else {
        MLog("查找到字符串:%s", cat);
    }

}
extern "C"
JNIEXPORT void JNICALL
Java_com_xm_j_XmJni_jstring2char(JNIEnv *env, jclass clazz, jstring s) {
    //直接jni层就提供了。直接返回jstring 转到 c++的指针
    const char *rtn = env->GetStringUTFChars(s, NULL);

    MLog("jstring to char %s", rtn);
}

// 函数声明,求数组的平局值!!先定义在使用,在实现
double getAverage(const int *arr, int count);

//跟Java一模一样方法重载。根据形参不同
double getAverage(const int *arr);


extern "C"
JNIEXPORT void JNICALL
Java_com_xm_j_XmJni_cppPointer(JNIEnv *env, jclass clazz) {
    //学习c++指针
    //###################认识什么什么是指针地址,16进制的内存地址
    int var1;
    char var2[10];
    MLog("var1 变量的地址%p", &var1);
    MLog("var2 变量的地址%p", &var2);

    //######################### 操作指针!操作变量 var 或者操作 *ip 指针都是操作的一个东西
    //总结,在= 右边 &:取出内存地址。。。。。         * :取值。
    int var = 20;   // 实际变量的声明
    int *ip;        // 指针变量的声明
    ip = &var;       //将var的指针交给ip。此时 指针变量ip和 变量var指向的是同一块内存
    MLog("var 变量的值%d", var);
    MLog("var 变量的指针地址%p", &var);
    MLog("ip 指针变量的值%d", *ip);

    var = 15;//改变变量
    MLog("var 直接改变:变量的值%d", var);
    MLog("ip 直接改变:指针变量的值%d", *ip);

    *ip = 10;
    MLog("var 通过指针改变:变量的值%d", var);
    MLog("ip 通过指针改变:指针变量的值%d", *ip);

    //结尾用-1表示,判断个数的时候也是用 -1 来判断,,你不允许出现-1的值
    int varArr[] = {100, 200, 30, 50, 60, 0, 50, -1};
//    double average = getAverage(varArr, 5); //明确知道个数的这样处理。
    double average = getAverage(varArr);
    MLog("varArr 的平局值%f", average);

    //#####################################引用。 通过使用引用来替代指针,会使 C++ 程序更容易阅读和维护 类型后面跟&  是引用
    // 在 = 左边的  &:是引用。 后面操作这个变量都是直接操作实际的值
    int &update = varArr[0];
    update = 50;
    average = getAverage(varArr);
    MLog("varArr 修改第一个引用的的平局值%f", average);

    int *j = &varArr[5];//从第六个元素取出指针!
    average = getAverage(j);
    MLog("varArr 从第6个元素开始算平局值%f", average);


    time_t now = time(0);
    tm *time = localtime(&now);

    MLog("1970到现在的秒数%ld", now);

    MLog("当前时间:%d-%d-%d %d:%d:%d", time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
         time->tm_hour,
         time->tm_min,
         time->tm_sec);
}


double getAverage(const int *arr, int count) {
    int sum = 0;
    for (int i = 0; i < count; ++i) {
        sum += arr[i];
    }
    return double(sum) / count;
}

double getAverage(const int *arr) {
    int sum = 0;
    int count = 0;

    while (arr[count] != -1) {
        count++;
    }

    for (int i = 0; i < count; ++i) {
        sum += arr[i];
    }
    return double(sum) / count;
}

//定义了数据结构
struct Books {
    char title[50];
    char author[50];
    char subject[100];
    int book_id;
};

//定义了数据结构!! class全部都是private,但是你可以通过添加public 为公开的!
class BookClass {
public:
    //公开的字写在最上面就是全公开,将不需要公开的写在上面
    char title[50];
    char author[50];
    char subject[100];
protected:
    int book_id = 10;

//private:  //访问修饰符,默认是private!
//protected:
//public:
//    virtual char *toString(){ //virtual 必须有继承关系,默认调用子类的函数!
//
//    }
//virtual 多态,=0 纯虚函数,子类必须实现不然编译器报错!!也可以放方法体虚函数,这个写法有点像Java的方法(abstract)
    virtual char *toString() = 0;
};

class NumberBookClass : public BookClass {
public:
    int passNumber;

    NumberBookClass(const char *t = "未知书籍", const char *a = "未知作者") {
        //构造方法。。如果没有值就使用默认值。循环添加到 父类的变量中
        int count = 0;
        while (t[count] != '\0') {
            title[count] = t[count];
            count++;
        }
        title[count] = '\0';

        count = 0;
        while (a[count] != '\0') {
            author[count] = a[count];
            count++;
        }
        author[count] = '\0';
    }

    ~NumberBookClass() {
        //析构函数。当被调用delet的时候。
    }

    //将父类被保护的 book_id指针返给调用者
    int *getSuperBookId() {
        return &book_id;
    }

    char *toString() {
        std::string str = std::string("passNumber:") + std::to_string(passNumber) +
                          std::string(",书名:") + std::string(title) +
                          std::string(",作者:") + std::string(author) +
                          std::string(",book_id:") + std::to_string(book_id);
        return const_cast<char *>(str.c_str());
    }
};

#include <stdbool.h>

extern "C"
JNIEXPORT void JNICALL
Java_com_xm_j_XmJni_dataType(JNIEnv *env, jclass clazz) {

    //初步看有点像 Java的 JavaBean
    Books book1;
    strcpy(book1.title, "童话故事");
    strcpy(book1.author, "小马");
    strcpy(book1.subject, "这是可爱的童话故事哟");
    book1.book_id = 12345;

    //定义的数据类型直接可以 . 来进行操作他和赋值。。
    MLog("这本:%s的,作者是:%s,书的基本描述:%s", book1.title, book1.author, book1.subject);

    //如果交给指针后。则必须用 -> 来使用
    struct Books *p_book1 = &book1;
    MLog("这本:%s的,作者是:%s,书的基本描述:%s", p_book1->title, p_book1->author, p_book1->subject);

    // typedef 给 数据类型 1、重新定义一个新名字,2、简化声明(隐藏指针什么的)
    //    typedef long int *pint32;
    //    pint32 x, y, z;  // x, y 和 z 都是指向长整型 long int 的指针。
    typedef Books B;
    B b;

    NumberBookClass numberBookClass = NumberBookClass("c++", "小马");
    numberBookClass.passNumber = 60;//60分及格
    //因为父类是被保护的。只有子类才能获取。。我本来没办法修改,但是我通过子类获取到父类的成员变量指针,我就可以修改了
    int *protectedBookId = numberBookClass.getSuperBookId();
    MLog("通过子类派生类 的方法获取了指针:%d", *protectedBookId);
    *protectedBookId = 1314520;
    MLog("获取指针,非子类赋值:%d", *protectedBookId);
    MLog("这是子类拼接的\n%s", numberBookClass.toString());
}


extern "C"
JNIEXPORT void JNICALL
Java_com_xm_j_XmJni_fstream(JNIEnv *env, jclass clazz) {

}

猜你喜欢

转载自blog.csdn.net/masai158/article/details/125426912
今日推荐