android ndk的简单应用

做android开发,或多或少应该对ndk有些了解。大家都知道,开发android应用很多部分是使用java完成的,但是java语言使用起来虽然简单,但是也比较容易进行反编译,尽管现在网络上有很多的加密工具。那怎么保护应用的一些隐私逻辑模块(加解密)的,ndk是一个很好的选择。

ndk使用c或者cpp完成代码的编写,使用c或者cpp可以将一些模块编译为链接库(so文件),这些文件反编译起来则非常的困难,同时使用c和cpp写出的代码在执行效率上会有所提升。本文将展示使用ndk技术将字符串的简单加解密方法写进so文件中。

考虑到编码等的原因,本文中的加密解密算法方式为:java中将字符串转为为byte数组,然后通过jni调用c语言加解密函数,同时将byte数据传递给c(中间有一部类型转化,将byte数组转化为char数组),c语言对char数组进行加解密后返回。有关ndk的一些简单使用,大家可以看一些麦子学院的教程教程,本文中只对使用的一些例子进行解释。对于字符串的加解密,按照本文的方式加解密应该是一种不错的方式,使用中您可能需要修改一下c语言中的加解密函数即可。

本文中c语言对char数组的加解密很简单,对每一个char进行拆分,char占8位,高4位与低4位拆成2个数值,然后根据数值从一个长度为16的钥匙串中拿出对应字符,这些字符对应的数组记为加密后的字符串。反向解密原理相似,只需要将字符数组中每2个字符抽取,计算出加密前的数值即可。下面看一下整体ndk的使用。

使用ndk前,需要从android官网上下载ndk组件,解压后大概3G左右,建议一个新项目(AndroidNDK),将新项目目录指定在ndk解压后的根目录(这样比较方便,不这样做也可以),我们需要在新项目中额外添加的只有一个jni目录,jni目录与src,res等是同一层的。目录内需要含有的文件如下所示:


1

2

3

4

5


mac:AndroidNDK YD$ tree  jni

jni

├── Android.mk

├── Application.mk

└── encrypt.c

其中encrypt.c即是我们的加解密函数所在的位置,Android.mk与Application.mk为配置文件,内容很固定。可以看下各个文件的内容:

Application.mk


1


APP_ABI := armeabi,armeabi-v7a

Application.mk中还有其他的一些配置,大家可以去官网或者google一下,常用的配置是App_ABI,指定生成对应cpu架构的库文件。

Android.mk


1

2

3

4

5

6


LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := codeboy_encrypt

LOCAL_SRC_FILES := encrypt.c

LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog

include $(BUILD_SHARED_LIBRARY)

Android.mk中需要修改的内容只有LOCAL_MODULE和LOCAL_SRC_FILES,前者指定生成的连接库名称,后者指定要编译的c语言或者cpp的文件名字,其他的保持不变即可。

encrypt.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


#include<string.h>

#include<jni.h>

#include<android/log.h>

//宏定义打印函数,使用方法 LOGI("hello") 或者 LOGI("money %d",15)

#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native", __VA_ARGS__))

#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native", __VA_ARGS__))

const char key[] = "abcdefghijklmnop"; //16个字符

int len = 0;

//计算字符对应的byte值

unsigned char getByteNumber(unsigned char first, unsigned char end) {

int firstPosition = 0, endPosition = 0;

int position = 0;

for (; position < 16; position++) {

if (key[position] == first) {

firstPosition = position;

}

if (key[position] == end) {

endPosition = position;

}

}

return (firstPosition << 4) | (endPosition);

}

//加密函数

void encrypt(unsigned char p[], unsigned char res[]) {

int i = 0;

for (; i < len; i++) {

res[2 * i] = key[p[i] / 16];

res[2 * i + 1] = key[p[i] % 16];

}

}

//解密函数

void decrypt(unsigned char p[], char res[]) {

int i;

for (i = 0; i < len; i++) {

res[i] = getByteNumber(p[i * 2], p[i * 2 + 1]);

}

}

//java中生命的native函数,函数名称格式Java_包名(点换下划线)_类名_函数名

//前两个参数JNIEnv *env, jclass this比较固定,其中第二个参数jclass代表方法是静态的,仅仅是个表示,如果方法不是静态的话,jclass换成jobject

//后续的参数是函数要传进来的参数

//java中的byte数组对应jni中的jbyteArray,jni中的jbyteArray可以通过jni中的函数转换为char数组

jstring Java_me_codeboy_encrypt_EncryptUtil_encrypt(JNIEnv *env, jclass
this,

jbyteArray src) {

unsigned char *buff = (char*) (*env)->GetByteArrayElements(env, src, NULL);

len = (*env)->GetArrayLength(env, src);

//加密后长度变为原先的2倍

unsigned char res[len * 2];

encrypt(buff, res);

//此步骤很重要,标志结束

res[len * 2] = ‘\0‘;

//使用完毕释放src数组,因为src数组的存在jvm中

(*env)->ReleaseByteArrayElements(env, src, buff, 0);

//jni中函数将char数组转变为字符串,jni中字符串为jstring,对应java中的String

jstring resStr = (*env)->NewStringUTF(env, res);

return resStr;

}

//和加密类似

jstring Java_me_codeboy_encrypt_EncryptUtil_decrypt(JNIEnv *env, jclass
this,

jbyteArray src) {

unsigned char *buff = (char*) (*env)->GetByteArrayElements(env, src, NULL);

len = (*env)->GetArrayLength(env, src);

//解密后长度变为原先的1/2

len = len / 2;

signed char res[len];

decrypt(buff, res);

//此步骤很重要,标志结束

res[len] = ‘\0‘;

//使用完毕释放src数组,因为src数组的存在jvm中

(*env)->ReleaseByteArrayElements(env, src, buff, 0);

jstring resStr = (*env)->NewStringUTF(env, res);

return resStr;

}

这样我们的ndk相关的文件就写好了,下面在终端下切换到AndroidNDK目录下,运行命令即可:


1


../ndk-build

运行结果如下:


1

2

3

4

5

6

7

8


mac:AndroidNDK YD$ ../ndk-build

Android NDK: WARNING: APP_PLATFORM android-21 is larger than android:minSdkVersion 14 in ./AndroidManifest.xml

[armeabi] Compile thumb  : codeboy_encrypt <= encrypt.c

[armeabi] SharedLibrary  : libcodeboy_encrypt.so

[armeabi] Install        : libcodeboy_encrypt.so => libs/armeabi/libcodeboy_encrypt.so

[armeabi-v7a] Compile thumb  : codeboy_encrypt <= encrypt.c

[armeabi-v7a] SharedLibrary  : libcodeboy_encrypt.so

[armeabi-v7a] Install        : libcodeboy_encrypt.so => libs/armeabi-v7a/libcodeboy_encrypt.so

执行完成后,我们可以看一下libs文件夹,多了一些so文件,如下:


1

2

3

4

5

6

7

8

9


mac:AndroidNDK YD$ tree libs

libs

├── android-support-v4.jar

├── armeabi

│   └── libcodeboy_encrypt.so

└── armeabi-v7a

└── libcodeboy_encrypt.so

2 directories, 3 files

下面我们就开始写对应的java代码了,将调用c函数的加解密函数抽象到一个类中即可

EncryptUtil.java


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


package me.codeboy.encrypt;

public class EncryptUtil {

public native static String encrypt(byte[] src); //
加密函数

public native static String decrypt(byte[] src); //
解密函数

static {

System.loadLibrary("codeboy_encrypt");

}

/**

* 加密函数

*

* @param src

* @return

*/

public static String encrypt(String src) {

return encrypt(src.getBytes());

}

/**

* 解密函数

*

* @param src

* @return

*/

public static String decrypt(String src) {

return decrypt(src.getBytes());

}

}

注意C语言中的函数名称中的包名类名函数名要与该类统一,还有对应的链接库名称。

做好了这些以后,我们就可以使用了。在我们的Activity中简单的定义一个按钮,点击后对一个字符串进行加密打印,之后进行解密打印,Activity中的代码如下:


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


package me.codeboy.ndk.ui;

import me.codeboy.encrypt.EncryptUtil;

import me.codeboy.ndk.R;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

public class MainActivity extends Activity {

@Override

protected void onCreate(Bundle
savedInstanceState) {

setContentView(R.layout.main_ui);

super.onCreate(savedInstanceState);

Button btn = (Button) findViewById(R.id.btn);

btn.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

String src = "我是玄恒,欢迎访问我的网站codeboy.me";

String res = EncryptUtil.encrypt(src);

System.out.println("------>" + res);

res = EncryptUtil.decrypt(res);

System.out.println("--->" + res);

}

});

}

}

点击按钮后可以看到点击button打印的结果:


1

2


--->ogiijbogjikpohioieogibjcoplmimogkmkcoilpiooikolpojjhkoogiijbohjkieohlnjbohkljjgdgpgegfgcgphjcogngf

--->我是玄恒,欢迎访问我的网站codeboy.me

这样下来我们就完成了简单的加解密操作,也对ndk有了一个初步的了解。

更多文章请访问小胖轩.

时间: 2024-09-30 09:02:56

android ndk的简单应用的相关文章

Android NDK 编译 简单示例 之 HelloWord

前面,在win7用cygwin上进行NDK的编译,确实发现有点麻烦,要配置很多环节. 还有另一种更简单的方法.就是直接在ADT上面配置即可,非常方便实用. 一.首先到官方下载最新的NDK解压到指定地方即可. 二.打开ADT, 1.Project 2.Properties 3.Builders---New... 4.Program 5.写一下项目名把 6.Main下面Location 选择Browser FIle System..选择NDK的路径下面的ndk-build.cmd 7.Working

Android NDK环境搭建与简单实例

一.NDK与JNI简介 NDK全称为native development kit本地语言(C&C++)开发包.而对应的是经常接触的Android-SDK,(software development kit)软件开发包(只支持java语言开发). 简单来说利用NDK,可以开发纯C&C++的代码,然后编译成库,让利用Android-SDK开发的Java程序调用.NDK开发的可以称之为底层开发或者jni(java  native interface)层开发,SDK开发可以称为上层开发. Andr

Android NDK 简单介绍、工具安装、环境配置

NDK全称:Native Development Kit. 1.NDK是一系列工具的集合. * NDK提供了一系列的工具,帮助开发人员高速开发C(或C++)的动态库,并能自己主动将so和java应用一起打包成apk.这些工具对开发人员的帮助是巨大的. * NDK集成了交叉编译器,并提供了对应的mk文件隔离平台.CPU.API等差异,开发者仅仅须要简单改动mk文件(指出"哪些文件须要编译"."编译特性要求"等),就能够创建出so. * NDK能够自己主动地将so和Ja

Android NDK学习笔记(一) 为什么要用NDK?

NDK是什么 NDK是Native Development Kit的简称,即本地开发工具包.通过NDK,Android允许开发人员使用本地代码语言(例如C/C++)来完成应用的部分(甚至全部)功能.注意:由于翻译原因,有些地方也把Native翻译为"原生". NDK是SDK的一个补充,可以帮助你做这些事情: 生成可以在ARM CPU,Android 1.5(及以上)平台运行的JNI兼容的共享库. 将生成的共享库放置在应用程序项目路径的合适位置,使其能自动地添加进你最终的(和经过签名的)

深入理解Android NDK日志符号化

为了进行代码及产品保护,几乎所有的非开源App都会进行代码混淆,这样当收集到崩溃信息后,就需 要进行符号化来还原代码信息,以便开发者可以定位Bug.基于使用SDK和NDK的不同,Android的崩溃分为两类:Java崩溃和C/C++崩溃.Java崩溃通过mapping.txt文件进行符号化,比较简单直观,而C/C++崩溃的符号化则需要使用Google自带的一些NDK工具,比如ndk-stack.addr2line.objdump等.本文不去讨论如何使用这些工具,有兴趣的朋友可以参考同事写的另一篇

Android NDK开发指南---Application.mk文件和android.mk文件

https://android.googlesource.com/platform/development/+/donut-release/ndk/docs/OVERVIEW.TXT https://android.googlesource.com/platform/ndk/+/4e159d95ebf23b5f72bb707b0cb1518ef96b3d03/docs/ANDROID-MK.TXT https://android.googlesource.com/platform/ndk/+/4

Android NDK编写一个HelloWorld

在上一篇博文中,我们搭建好了Android ndk 的开发环境,作为码农,我们是不是先来写一个HelloWorld来表示一下自己的到来. (1)首先在你的工作空间建一个Android项目,这里我建立的为NDKHelloWorld.项目结构如图(1) . 图(1) (2)在MainActivity中声明你所需要的本地方法,MainActivity的代码如下所示: package com.gc.ndkhelloworld; import android.app.Activity; import an

【转】Android NDK学习(2)Windows下NDK开发环境配置

一.配置好Android开发环境 现在android SDK的版本已经到了4.4了,几乎不用配置,下载下来解压,SDK跟Android开发专用Eclipse都有了.喜欢. 二.下载安装安卓NDK 下载最新版,解压即可. 2013年6月7日: 目前版本已经到了r8e. 三.下载安装cygwin   由于NDK编译代码时必须要用到make和gcc,所以你必须先搭建一个linux环境, cygwin是一个在windows平台上运行的unix模拟环境,它对于学习unix/linux操作环境,或者从uni

Android NDK R9环境配置,开发教程

最近,在学习android ndk开发,配置环境的时候遇到了些问题,总算不负有心人--在这里记录哈过程,与筒子们分享哈--想学NDK的筒子们有福啦-- 教程本人亲测,非copy的-- 如有什么不明白的地方,可以留言 大神也可以进来瞧瞧有什么不对的地方,请指教两招 ----------------------------------------------------------------------------------------------------------------------