大家好,感谢邀请,今天来为大家分享一下深入解析:Android应用加固技术原理与应用的问题,以及和的一些困惑,大家要是还不太明白的话,也没有关系,因为接下来将为大家分享,希望可以帮助到大家,解决大家的问题,下面就开始吧!
加固原理
1. 在主项目中添加一个模块。该模块用于打包apk。
2. 在主工程中添加java lib库。这用于加密主apk的dex并生成加固的apk。
项目目录.png
当一个apk需要启动时,系统会解压该apk并使用dex文件解析这些dex文件来启动修改后的apk。因为主apk的dex必须要进行加密,防止被反编译,所以这些dex文件已经被加密了,那么系统在加载这些dex文件时肯定会报错。为了让apk正常启动,你应该启动这个shell的应用程序。因此,你必须在应用程序的AndroidManifest中注册shell的Application;这样App才能正常启动。
Apk加固流程图如下:
加密.png
Decryption.png
加密的流程
首先,在Module中生成aar库。生成后,解压该库的apk shell dex包,加密到proxy_tools的java库中。
生成apk的壳文件(jar转dex)
文件aarFile=new File("proxy_core/build/outputs/aar/proxy_core-debug.aar");
文件aarTemp=new File("proxy_tools/temp");
Zip.unZip(aarFile, aarTemp);
文件classesJar=new File(aarTemp, "classes.jar");
文件classesDex=new File(aarTemp, "classes.dex");
进程进程=null;
尝试{
process=Runtime.getRuntime().exec("cmd /c dx --dex --output " +classesDex.getAbsolutePath()
+ " " +classesJar.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
尝试{
process.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (process.exitValue() !=0) {
抛出新的RuntimeException("dex 错误");
}通过这个操作可以将aar中的jar转为dex包,这样就可以得到apk的shell dex。
加密apk中的dex文件
//下面密码加密的APK中的所有dex文件
文件apkFile=new File("app/build/outputs/apk/debug/app-debug.apk");
文件apkTemp=new File("app/build/outputs/apk/debug/temp");
//提取apk
Zip.unZip(apkFile, apkTemp);
//只要把dex文件取出来加密即可
文件dexFiles[]=apkTemp.listFiles(new FilenameFilter() {
@覆盖
公共布尔接受(文件文件,字符串s){
return s.endsWith(".dex");
}
});
//执行AES加密
AES.init(AES.DEFAULT_PWD);
for (文件dexFile : dexFiles) {
尝试{
byte[] 字节=Utils.getBytes(dexFile);
byte[] 加密=AES.加密(字节);
FileOutputStream fileOutputStream=new FileOutputStream(new File(apkTemp,
"秘密-" + dexFile.getName()));
fileOutputStream.write(加密);
fileOutputStream.flush();
文件输出流.close();
dexFile.delete();//删除未加密的dex
} catch (异常e) {
e.printStackTrace();
}
}这样,apk中的dex文件就被加密了。加密完成后,需要将shell dex文件放到apk目录下,生成新的apk文件。
重新生成apk文件
//将dex放入apk解压路径并重新生成apk文件
classDex.renameTo(new File(apkTemp, "classes.dex"));
文件unSignedApk=new File("app/build/outputs/apk/debug/app-unsigned.apk");
尝试{
//生成未签名的apk
Zip.zip(apkTemp, unSignedApk);
} catch (异常e) {
e.printStackTrace();
}新的apk已经生成,下面需要对齐签名。
新apk的签名对齐
//4.签名对齐
文件alignedApk=new File("app/build/outputs/apk/debug/app-unsigned-aligned.apk");
尝试{
process=Runtime.getRuntime().exec("cmd /c zipalign -v -p 4 " + unSignedApk.getAbsolutePath()
+""+alignedApk.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
尝试{
process.waitFor();
} catch (InterruptedException e) {
System.out.println("异常" + e.toString());
e.printStackTrace();
}签名对齐后,就可以签名并生成可运行的apk了。
签名生成可以运行的apk文件
文件jskFile=new File("proxy_tools/proxy.jks");
System.out.println("开始签名");
尝试{
process=Runtime.getRuntime().exec("cmd /c apksigner sign --ks " + jskFile.getAbsolutePath()
+ " --ks-key-alias qwert --ks-pass pass:123456 --key-pass pass:123456 --out "
+signedApk.getAbsolutePath()+""+alignedApk.getAbsolutePath());
System.out.println("签名完成");
} catch (IOException e) {
e.printStackTrace();
}
尝试{
process.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (process.exitValue() !=0) {
throw new RuntimeException("签名的apk 错误");
}
System.out.println("Apk生成成功");
}到这里加密部分就完成了。加密完成后,即可进行解密操作。
解密流程
解密之前先说一下,apk安装后,会在/data/app/package name-xxx/下生成一个名为base.apk的文件。这个文件是apk中所有dex文件的封装,所以需要拿到这个base.apk中的dex进行解密。
文件apkFile=new File(getApplicationInfo().sourceDir);
Log.d(TAG,"apk路径:"+apkFile.getAbsolutePath());
文件versionDir=getDir(app_name+"_"+ app_version,MODE_PRIVATE);
文件appDir=new File(versionDir,"app");
文件dexFile=new File(appDir,"dexDir");
//我们必须使用需要加载的dex文件
ListdexFiles=new ArrayList();
if(MD5.验证(dexFile)){
//提取apk
Zip.unZip(apkFile,appDir);
//获取目录下所有文件
文件[] 文件=appDir.listFiles();
for(文件文件:个文件){
字符串名称=file.getName();
Log.d(ProxyApplication.class.getName(),"dexName:"+名称);
if(name.endsWith(".dex") !TextUtils.equals(name,"classes.dex")){
AES.init(AES.DEFAULT_PWD);
//读取文件内容
尝试{
byte[] bytes=Utils.getBytes(file);
//解密
byte[] 解密=AES.decrypt(bytes);
FileOutputStream fileOutputStream=new FileOutputStream(文件);
fileOutputStream.write(解密);
fileOutputStream.flush();
文件输出流.close();
dexFiles.add(文件);
} catch (异常e) {
e.printStackTrace();
}
}
}
}别的{
for(文件文件: dexFile.listFiles()){
dexFiles.add(文件);
}
}
//将解密后的dex文件加载到系统中
尝试{
loadDex(dexFiles,versionDir);
} catch (异常e) {
e.printStackTrace();
}到这里已经完成dex文件的解密了。接下来,要把解密后的dex文件交给系统处理,让其运行apk。
把dex交给系统处理
要将解密后的dex文件交给系统,首先要知道系统是如何加载dex文件的。只有了解了系统如何加载dex文件,才能将解密后的dex文件交给系统。相信大家都熟悉getClassLoader方法。通过查看系统原始代码,我们发现getClassLoader返回的ClassLoader是一个名为PathClassLoader的类。在系统原始代码中,我们可以看到这个类只有一个构造方法,没有其他方法。然后查看其父类。它的父类是一个名为BaseDexClassLoader的类。通过查看里面的方法,可以看到有一个findClass方法来加载这些类。查找类.png
findClass类调用pathList中的findClass方法。那么这个pathList是什么呢?这个pathList是一个叫做DexPathList的类。然后去DexPathList看看findClass方法做了什么?以下是DexPathList中的findClass方法
DexPathList 中的findClass.png
从上面的代码我们可以看到这个DexPathList中的findClass方法是遍历一个叫做dexElements的数组来生成一个Class对象;那么我们需要找到dexElements数组是在哪里初始化的?通过查看DexPathList类的构造方法,我们发现dexElements是通过一个名为makePathElements的方法来初始化的。
dexElements.png 的初始化
至此,我们已经知道系统是如何加载dex文件的了。那么解密后的dex文件必须放到系统的dexElements中才可以被系统加载。系统调用makePathElements方法生成dexElements数组。那么我们也可以通过Reflectively 调用这个方法来生成自己的dexElements 数组,然后将自己的dexElement 数组和系统的dexElements 数组合并成一个新的数组并设置到这个DexPathList 中。
私人无效loadDex(ListdexFiles,文件版本Dir)抛出异常{
//通过查看源码可以知道,系统将dex存储在一个数组:dexElements中,dexElements是DexPathList的成员。
字段pathListField=Utils.findField(getClassLoader(),"pathList");
对象pathList=pathListField.get(getClassLoader());
字段dexElementsFieId=Utils.findField(pathList,"dexElements");
//这是DexPathList中的原始数据,也就是系统中的dexElements数组
对象dexElements[]=(Object[]) dexElementsFieId.get(pathList);
//构建你自己的dexElement
方法makeDexElements=Utils.findMethod(pathList,"makePathElements",List.class,File.class,List.class);
ArrayListsuppressedExceptions=new ArrayList();
Object[] mCustomDexElements=(Object[]) makeDexElements.invoke(pathList,dexFiles,versionDir,suppressedExceptions);
//合并数组
Object newElements=Array.newInstance(dexElements.getClass().getComponentType(),dexElements.length+mCustomDexElements.length);
System.arraycopy(dexElements,0,newElements,0,dexElements.length);
System.arraycopy(mCustomDexElements,0,newElements,dexElements.length,mCustomDexElements.length);
//替换类加载器中的elements数组
dexElementsFieId.set(pathList,newElements);
深入解析:Android应用加固技术原理与应用的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于、深入解析:Android应用加固技术原理与应用的信息别忘了在本站进行查找哦。
【深入解析:Android应用加固技术原理与应用】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
我一直想学习关于apk加固的知识,希望能找到一些实际可用的教程。
有10位网友表示赞同!
听说现在很多手游都会进行加固处理,这样就可以防止破解了?
有5位网友表示赞同!
我有点不明白,Android apk 加固到底是怎么实现的?是用什么技术?
有6位网友表示赞同!
想了解一下不同平台的apk加固方法有什么区别?
有18位网友表示赞同!
现在很多安全厂商都会提供apk加固服务,看起来很可靠的样子。
有20位网友表示赞同!
学习了这个知识之后,自己开发的app是不是也能进行加固保护呢?
有6位网友表示赞同!
我想知道apk加固的效果怎么样,能真的起到保护作用吗?
有12位网友表示赞同!
如果对apk加固有深入了解,那我们在使用app的时候会不会更容易识别出是否进行了加固?
有10位网友表示赞同!
这个方面好像很难入门,建议提供一些详细的案例讲解。
有12位网友表示赞同!
应该会有很多开发工具可以辅助进行apk加固操作吧?
有20位网友表示赞同!
加固之后的apk文件会比未加固的文件体积大吗?
有5位网友表示赞同!
学习一下这个知识,也许能够帮助我在安全领域多一些了解。
有7位网友表示赞同!
想知道apk加固对用户体验的影响有多大?
有16位网友表示赞同!
如果apk加固做的太厉害,会不会导致app运行过慢或者崩溃?
有11位网友表示赞同!
在某些特殊情况下,是可以直接解开apk加固的措施吗?
有9位网友表示赞同!
这个领域的知识更新很快吗?需要不断学习新技巧吗?
有11位网友表示赞同!
我想知道apk加固的攻击和防御之间的博弈是怎样的?
有19位网友表示赞同!
这个领域的研究前景如何?未来发展趋势是什么?
有6位网友表示赞同!
apk加固是一个比较门槛较高的技术吗?需要具备哪些基础知识才能学习理解?
有7位网友表示赞同!
想了解一下apk加固的商业模式,现在有哪些公司在提供相关服务?
有17位网友表示赞同!