一种基于随机数组so库的Android数据资源硬加密方法
作者:AniO软件咨询服务,天涯来客-Leemboy
摘要:本文提供一种简洁的Android数据资源硬加密方案,利用随机数组so静态库,对Android应用的Res资源,Assets资源文件数据按顺序进行随机加密,对Android应用开发者的数据资源进行保护,增加数据资源的破解难度。
1、 算法背景
对于数据资源毫无加密的Android应用来说,要从其app下载包中获取Res和Assets资源数据相当简单。对与数据资源没有加密的APK包,直接把文件扩展名apk改成zip,让后用解压软件解压后,进入解压文件夹下,就容易轻松获取到Assets和Res目录下的文件。
2、 随机数组生成方案
随机数组元素数量自定义。我们直接给出java代码如下:
public class randomGenerator {
//定义数组的最大元素数目
public final static int MAX_NUM=128;
public static void main(String[] args) throws IOException
{
int max=65535;//随机数最大值
int min=1024;//随机数最小值
int[] metrixInt=newint[128];
Random random =new Random();
String content="";
for(int i=0;i<MAX_NUM;i++){
//生成随机数
metrixInt[i]=random.nextInt(max)%(max-min+1) + min;
System.out.println("\""+metrixInt[i]+"\",");
content=content+Integer.toString(metrixInt[i])+"\n";
}
}
3、批量数据资源加密方案
3.1加密步骤:
一、 把待加密批量源文件拷贝到源文件目录,具体路径见代码分析;
二、 执行加密程序
三、 加密后的文件自动存入目标目录。
3.2加密算法代码及分析:
注:以下代码在Eclipse中调试运行通过。
package net.lingw.leemboy.encription;
import java.io.*;
public class imageInc {
/**
* @param args
* @throws IOException
*/
//随机数组最大数量
public final static int MAX_RAND=128;
//定义目录初始路径深度,从根目录开始遍历
private static in depth =1;
//代码主函数
public static void main(String[] args) throws IOException
{
//定义待加密的文件路径
Strings fileDir=System.getProperty ("user.home") +"/eclipse/workspace/EncImage/image/";
//定义加密后的文件存放路径
String dfileDir=System.getProperty ("user.home") +"/eclipse/workspace/EncImage/enc_image/";
//执行加密函数
find(sfileDir,dfileDir,depth);
}
//这是个递归函数,遍历源文件路径,找出所有文件,执行加密算法
//参数1:源文件目录路径
//参数2:目标文件目录路径
//参数3:路径深度
public static void find(String pathName,String destPathName,int depth)throwsIOException
{
//设置初始文件数目
int filecount=0;
//获取pathName路径的File对象
File dirFile =new File(pathName);
//判断该文件或目录是否存在,不存在时在控制台输出提醒
if (!dirFile.exists()) {
System.out.println("do not exit");
return ;
}
//判断如果不是一个目录,就判断是不是一个文件,是文件则输出文件路径
if (!dirFile.isDirectory()){
if (dirFile.isFile()) {
//如果是文件,就输出文件及路径
System.out.println(dirFile.getCanonicalFile());
}
return ;
}
//显示文件树结构
for (int j = 0; j < depth; j++) {
System.out.print(" ");
}
System.out.print("|--");
System.out.println(dirFile.getName());
//获取此目录下的所有文件名与目录名
String[] fileList =dirFile.list();
//当前深度更新
int currentDepth=depth+1;
//遍历文件目录
for (int i = 0; i < fileList.length;i++) {
String string =fileList[i];
//File("documentName","fileName")是File的另一个构造器
File file =new File(dirFile.getPath(),string);
String name =file.getName();
//如果是一个目录,搜索深度depth++,输出目录名后,进行递归
if (file.isDirectory()){
//如果是目录,继续执行递归函数
find(file.getCanonicalPath(),destPathName,currentDepth);
}else{
//如果是文件,则以树形结构输出文件名,并对文件执行加密函数
for (int j = 0; j < currentDepth; j++) {
System.out.print(" ");
}
System.out.print("|--"); //树形线
System.out.println(name); //加密文件名
//执行加密函数
encriptionImage(name,pathName,destPathName);
}
}
}
/***********************************************/
//加密函数
//参数1:待加密文件名;
//参数2:待加密文件路径名;
//参数3:加密后文件存储路径。
/***********************************************/
public static void encriptionImage(String fileName,String fromDir,String toDir)
{
try{
String source=fromDir+fileName;
String dest=toDir+fileName;
//源文件对象,即指向待加密文件
File inFile =new File(source);
//目标文件对象,即指向加密后的文件
File outFile = new File(dest);
//源文件输入流
FileInputStream input =new FileInputStream(inFile);
//目标文件输入流
FileOutputStream output =new FileOutputStream(outFile);
int content = 0;//该数据是用来存储读取到的数据,以整数形式存储
//定义加密随机数组索引初始值
int index=0;
while((content =input.read())!= -1)
{//如果没有到文件的末尾,那么继续读取数据
//按模余原则顺序从随机数组中获取当前加密随机数,
int rand=getRandnum(index%MAX_RAND);
//对文件中当前取到的内容执行异或运算,当然也可以执行其他运算。
//执行其他运算时,要在解密时执行对应的逆运算,
//加密后的数据写入输出文件。
output.write(content^rand);
index++;
if(index==MAX_RAND){
index=0;//随机数组已达上限,复位索引值。
}
}
//执行完加密后,关闭资源
output.close();
input.close();
}catch (FileNotFoundExceptione) {
e.printStackTrace();
return;
}catch (IOExceptione) {
e.printStackTrace();
return;
}
}
//取随机数函数,参数:随机数索引
public static int getRandnum(int i){
int rand=0;
String[] randArray ={"6659",
"39643",
"29006",
"61162",
//…… … … …
};
if(i<0 || i> MAX_RAND){
return 0;
}
rand=Integer.valueOf(randArray[i]);
return rand;
}
3.3 加密算法执行效果
左边列是加密随机数组部分内容,右边列是加密过程中加密文件显示过程
4、加密矩阵存储方案
为了实现简单,这里把加密数组存储在Android工程的so静态库中,由于So静态库是采用C语言编程模式,在Android项目编译中,C语言静态库被编译成二进制文件,对于一般的编程人员来说,二进制文件破解具有一定复杂性,笔者认为,对于普通应用,这种加密方案已经能够较有效的保护数据。So静态库中加密数组存储形式如下。
Java_net_lingw_leemboy_babylearnenglish2_DetailMainActivity_rnum(
JNIEnv* env,
jobject /* this */) {
std::string str =
"6659\n"
"39643\n"
"29006\n"
"61162\n"
"44305\n"
"10893\n"
"53718\n"
//。。。。。。。。。。。。。。。。。
54363";
return env->NewStringUTF(str.c_str());
}
5、资源文件读取及解密方案
Android应用文件如下:
package net.lingw.leemboy.babylearnenglish2;
//。。。 。。。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
//。。。 。。。
public class DetailMainActivity extends AppCompatActivity
{
//解密后的图片资源在ImageButton 中显示
ImageButton pictureButton;
//定义随机数组大小
public final static int MAX_RAND_NUM=128;
//存放随机数组
public int[] randArray=new int[MAX_RAND_NUM];
static {
//加载静态库
System.loadLibrary("native-lib");
}
/*******************************************************/
//申明静态库中的函数
//静态库里有很多函数,这里只列出取随机数组函数
//随机数组以字符串形式存储
public native String rnum();
/*******************************************************/
//取随机数函数
private void createRandArray()
{
String str=rnum();
String[] strArray=str.trim().split(separatorEnter);
//随机数组字符串è整数格式
for(int i=0;i<MAX_RAND_NUM;i++)
{
randArray[i]=Integer.valueOf(strArray[i].trim());
}
}
/*******************************************************/
//读取Assets图像资源,并用随机数组解密和显示
//参数:Assets图像资源名称
void showAssetsImage(String image){
String tempFile="temp.jpg";//存放临时文件
try
{
// 获取资源文件内容到输入流
InputStreamims = getAssets().open(image);
//定义输出数据流,并与输出文件关联
OutputStreamoms=new ObjectOutputStream(newFileOutputStream(new File(getFilesDir(),"")+File.separator+tempFile));
//输出文件的全路径格式为:/文件路径字符串/ tempFile
//以整数格式存放读取到的加密数据内容
int content =0;
int index=0;
while((content = ims.read())!= -1)
{ //如果没有到文件的末尾,那么继续读取数据
//加密数据与随机数组异或运算解密,并写到输出流
oms.write(content^randArray[index%MAX_RAND_NUM]);
index++;
if(index==MAX_RAND_NUM){
//如果随机数组已取完,索引值复位到初始位置
index=0;
}
}
//解密完数据,关闭输出流
oms.close();
//创建显示图像的输入流
Input Streaminput=new ObjectInputStream(new FileInputStream(new File(getFilesDir(),"")+File.separator+tempFile));
//图像文件完整名称为:/文件路径/ tempFile
//输入流转换为Drawable资源,这里的Drawable变量名为d
Drawable d =Drawable.createFromStream(input,null);
// 在ImageView中显示Drawable资源
pictureButton.setImageDrawable(d);
//文件输出后关闭所有的流变量
input.close();
ims.close();
}
catch(IOException ex)
{
return;
}
}
}
/**********************************************/
//加密后的图像资源存放在下列位置
6、加密效果和运行效果展示
加密后文件数据格式已经打乱,无法通过官方工具显示,说明已达到加密目的。
通过随机数组解密后的显示结果如下。
7、总结
本算法对普通应用开发者的资源能进行有效的保护,并且算法易实现,操作简单。对非专业人员来说,破解有一定困难。另外该算法除了可以保护图像数据,也可以保护各种文档、媒体等文件。
此外随机数组粗放在so库中,需要在工程编译时对java代码部分做简单混淆,起到对java代码保护并防止获取so库内容。