Android 应用程序加固技术分析篇二

更新:11-07 名人轶事 我要投稿 纠错 投诉

大家好,Android 应用程序加固技术分析篇二相信很多的网友都不是很明白,包括也是一样,不过没有关系,接下来就来为大家分享关于Android 应用程序加固技术分析篇二和的一些知识点,大家可以关注收藏,免得下次来找不到哦,下面我们开始吧!

Android APK加固技术研究(二)

Android APK加固技术研究(三)

为了保证Android应用程序的源代码安全,我们一般会对线上应用程序的代码进行混淆。然而,代码混淆还不够。我们还需要加强我们的应用程序,防止他人通过反编译获取我们的源代码。目前apk加固技术已经比较成熟和完善,市场上比较流行的就是“360加固”。本文对apk加固技术进行技术探索。希望读者读完后能够理解加固原理,并自行实现加固方案。

在Android apk加固技术研究(一)中,大致介绍了反编译过程以及我们能够获取源代码的原因。下面对加固的基本过程进行说明。

源码地址:https://gitee.com/openjk/apk-steady

加固流程

新建一个Android项目,在其中创建shell模块,生成加固的shell arr文件。 shell模块包含Application的子类SteadyApplication,其中包含解密dex文件的逻辑。编译shell并生成shell.aar文件并解压。将待加固的apk放入apkUnzip目录下,获取其中所有dex文件,并将apkUnzip下AndroidManifest.xml文件中application根节点下的name属性值修改为2中创建的SteadyApplication。将原apk的Application路径保存到meta-data节点,准备在SteadyApplication中解析生成。使用加密算法对上一步得到的dex文件进行加密,并删除原dex文件解压3中生成的aar文件得到里面的jar文件,然后通过dx将jar文件转换为dex文件SDK中提供的工具。将生成的dex文件放入apkUnzip文件中,并压缩apkUnzip文件夹,生成新的apk。重新签名这篇文章主要讲解上述1、2、3步骤,如何生成一个 Shell.arr(壳)文件。Shell 最终会打入到原 apk 的class.dex 中,用来解密已经加密的原 apk 中的dex和加载原来的 dex 文件

一、生成 Shell.aar(dex 解密和类加载)

1、解密加固的 dex 文件的流程

在应用程序中,您可以通过getApplicationInfo().sourceDir 获取基础APK。这个apk包含我们应用程序的所有代码。通过Application的getDir()方法,我们在应用程序的私有目录中创建一个私有文件夹SteadyDir。在2创建的目录中,我们解压bask.apk。解压后,我们得到apk的所有文件,然后过滤掉所有以dex为后缀的文件。文件。其中,classes.dex文件我们不需要,因为它已经加载到系统中,所以我们只需要处理加密的dex文件即可。将解密后的dex文件加载到程序中并运行apk真实应用程序。启动应用程序

2、如何解压 apk 文件

zip解压main 使用java中的ZipFile类。具体实现直接在代码中。代码中有注释,没有解释。

公共静态无效unZip(文件zip,文件目录){

尝试{

//清空解压文件存放目录

删除文件(目录);

ZipFile zipFile=new ZipFile(zip);

//zip 文件中的每个条目

枚举?扩展ZipEntryentries=zipFile.entries();

//遍历

while (entries.hasMoreElements()) {

ZipEntry zipEntry=items.nextElement();

//zip文件/目录名

字符串名称=zipEntry.getName();

//不再需要原来的签名文件

if (name.equals("META-INF/CERT.RSA") || name.equals("META-INF/CERT.SF") || 名称

.equals("META-INF/MANIFEST.MF")) {

继续;

}

//忽略空目录

if (!zipEntry.isDirectory()) {

文件file=new File(目录, 名称);

//创建目录

if (!file.getParentFile().exists()) {

file.getParentFile().mkdirs();

}

//写入文件

FileOutputStream fos=new FileOutputStream(文件);

输入流=zipFile.getInputStream(zipEntry);

字节[]缓冲区=新字节[2048];

int 长度;

while ((len=is.read(buffer)) !=-1) {

fos.write(缓冲区, 0, len);

}

is.close();

fos.close();

}

}

zipFile.close();

} catch (异常e) {

e.printStackTrace();

}

}

私有静态无效deleteFile(文件文件){

if (file.isDirectory()){

File[] files=file.listFiles();

对于(文件f:文件){

删除文件(f);

}

}别的{

文件.删除();

}

}

3、如何解密 dex 文件

通过第二步的解压方法,我们可以轻松将base.apk解压到私有目录。然后我们通过文件扩展名.dex过滤掉所有dex文件(不包括classes.dex),然后将每个dex读入一个字节数组,然后对字节数组进行解密。

这里的加密和解密采用的是AES方法。为了增加安全性,解密方法使用jni方法完成。解密方法如下:

jbyteArray 解密(JNIEnv *env,jbyteArray srcData) {

jstring 类型=(*env).NewStringUTF("AES");

jstring cipher_mode=(*env).NewStringUTF("AES/ECB/PKCS5Padding");

jbyteArray pwd=(*env).NewByteArray(16);

char *master_key=(char *) "huangdh"l,AMWK;";

(*env).SetByteArrayRegion(pwd,0,16,reinterpret_cast(master_key));

jclass SecretKeySpecClass=(*env).FindClass("javax/crypto/spec/SecretKeySpec");

jmethodID SecretKeySpecMethodId=(*env).GetMethodID(secretKeySpecClass,"", "([BLjava/lang/String;)V");

jobject SecretKeySpecObj=(*env).NewObject(secretKeySpecClass,secretKeySpecMethodId,pwd,type);

jclass cipherClass=(*env).FindClass("javax/crypto/Cipher");

jmethodID cipherInitMethodId=(*env).GetMethodID(cipherClass,"init", "(ILjava/security/Key;)V");

jmethodID cipherInstanceMethodId=(*env).GetStaticMethodID(cipherClass,"getInstance", "(Ljava/lang/String;)Ljavax/crypto/Cipher;");

jobject cipherObj=(*env).CallStaticObjectMethod(cipherClass,cipherInstanceMethodId,cipher_mode);

jfieldID cryptoModeFieldId=(*env).GetStaticFieldID(cipherClass,"DECRYPT_MODE", "I");

jint 模式=(*env).GetStaticIntField(cipherClass,decryptModeFieldId);

(*env).CallVoidMethod(cipherObj,cipherInitMethodId,模式,secretKeySpecObj);

jmethodID doFinalMethodId=(*env).GetMethodID(cipherClass,"doFinal", "([B)[B");

jbyteArray text=(jbyteArray)(*env).CallObjectMethod(cipherObj,doFinalMethodId,srcData);

返回文本;

}

4、加载 dex 文件

通过上面的解压解密操作,我们得到了原始的dex文件。我们将这些dex文件放入一个集合中,然后使用类加载机制来加载解密后的dex文件。类加载机制将在后续文章中进行讲解。

公共静态无效loadDex(应用程序应用程序,ListdexFiles,文件版本Dir)抛出异常{

//1.首先从ClassLoader中获取pathList变量

字段pathListField=ProxyUtils.findField(application.getClassLoader(), "pathList");

//1.1 获取DexPathList类

对象pathList=pathListField.get(application.getClassLoader());

//1.2 从DexPathList类中获取dexElements变量

字段dexElementsField=ProxyUtils.findField(pathList,"dexElements");

//1.3 获取加载的dex数组

Object[] dexElements=(Object[])dexElementsField.get(pathList);

//2.反射到初始化dexElements的方法,即获取加载dex到系统中的方法

方法makeDexElements=ProxyUtils.findMethod(pathList,"makePathElements",List.class,File.class,List.class);

//2.1 需要实例化一个集合makePathElements

ArrayListsuppressedExceptions=new ArrayList();

//2.2 反射执行makePathElements函数,将解码后的dex加载到系统中。否则会导致dex无法打开并导致崩溃。

Object[] addElements=(Object[])makeDexElements.invoke(pathList,dexFiles,versionDir,suppressedExceptions);

//3.实例化一个新数组,将当前加载的和已加载的dex合并到一个新数组中

Object[] newElements=(Object[]) Array.newInstance(dexElements.getClass().getComponentType(),dexElements.length+addElements.length);

//3.1 将系统中加载的dex放入newElements中

System.arraycopy(dexElements,0,newElements,0,dexElements.length);

//3.2 将解密后加载的dex放入新数组中

System.arraycopy(addElements,0,newElements,dexElements.length,addElements.length);

//4.将合并后的新数组重置为DexPathList的dexElements

dexElementsField.set(pathList,newElements);

}

5、加载真实的 application 类,运行 app

1. 首先从AndroidManifest.xml文件中获取原应用的类名。 (下一篇我们会讲解如何将apk原来的应用类名放在AndroidManifest.xml的meta-data标签下)

/**

* 解析项目中原有的Application名称

*/

私有无效getMateData(){

尝试{

ApplicationInfo applicationInfo=getPackageManager().getApplicationInfo(getPackageName(),

PackageManager.GET_META_DATA);//获取包信息

Bundle metaData=applicationInfo.metaData;//获取Meta-data的键值对信息

if(null !=元数据){

if(metaData.containsKey("app_name")){

app_name=metaData.getString("app_name");//获取原始包名

}

}

}catch(异常e){

e.printStackTrace();

}

}2.获取原应用的类名后,通过反射获取应用的实例。

私有无效bindRealApplication()抛出异常{

如果(isBindReal){

返回;

}

if(TextUtils.isEmpty(app_name)){

返回;

}

//1.获取attachBaseContext(context)传入的上下文ContextImpl

上下文baseContext=getBaseContext();

//2.获取真实APK应用程序的类

Class?delegateClass=Class.forName(app_name);

//反射实例化,

delegate=(Application) delegateClass.newInstance();

//获取Application的attach()方法,该方法最先被初始化

方法Attach=Application.class.getDeclaredMethod("attach",Context.class);

Attach.setAccessible(true);

//执行应用程序#attach(Context)

Attach.invoke(委托,baseContext);

//ContextImpl----mOuterContext(app)是通过Application的attachBaseContext回调参数获取的

//4.获取Context的实现类

Class?contextImplClass=Class.forName("android.app.ContextImpl");

//4.1 获取mOuterContext上下文属性

字段mOuterContextField=contextImplClass.getDeclaredField("mOuterContext");

mOuterContextField.setAccessible(true);

//4.2 将真正的应用程序交给上下文。这是按照源码执行的。 setOuterContext函数是在实例化Application后调用的,所以需要绑定Context。

//应用程序=mActivityThread.mInstrumentation.newApplication(

//cl、appClass、appContext);

//appContext.setOuterContext(app);

mOuterContextField.set(baseContext, delegate);

//ActivityThread---mAllApplications(ArrayList) ContextImpl 的mMainThread 属性

//5.获取ActivityThread变量

字段mMainThreadField=contextImplClass.getDeclaredField("mMainThread");

mMainThreadField.setAccessible(true);

//5.1 获取ActivityThread对象

对象mMainThread=mMainThreadField.get(baseContext);

//ActivityThread---mInitialApplication

//6.反射获取ActivityThread类

类?activityThreadClass=Class.forName("android.app.ActivityThread");

//6.1 获取当前加载的Application类

字段mInitialApplicationField=ActivityThreadClass.getDeclaredField("mInitialApplication");

mInitialApplicationField.setAccessible(true);

//6.2 将ActivityThread中的Applicaiton替换为真正的Application,可以用来接收相应的生命周期和一些调用等

mInitialApplicationField.set(mMainThread,委托);

//ActivityThread---mAllApplications(ArrayList) ContextImpl 的mMainThread 属性

//7.获取ActivityThread中所有Application集合对象。这是一个多进程场景。

字段mAllApplicationsField=ActivityThreadClass.getDeclaredField("mAllApplications");

mAllApplicationsField.setAccessible(true);

ArrayListmAllApplications=(ArrayList) mAllApplicationsField.get(mMainThread);

//7.1 删除ProxyApplication

mAllApplications.remove(this);

//7.2 添加真实应用

mAllApplications.add(委托);

//LoadedApk--------mApplication ContextImpl的mPackageInfo属性

//8.从ContextImpl 获取mPackageInfo 变量

字段mPackageInfoField=contextImplClass.getDeclaredField("mPackageInfo");

mPackageInfoField.setAccessible(true);

//8.1 获取LoadedApk对象

对象mPackageInfo=mPackageInfoField.get(baseContext);

//9 反射获取LoadedApk对象

//@覆盖

//公共上下文getApplicationContext() {

//返回(mPackageInfo !=null) ?

//mPackageInfo.getApplication() : mMainThread.getApplication();

//}

Class?loadedApkClass=Class.forName("android.app.LoadedApk");

字段mApplicationField=loadApkClass.getDeclaredField("mApplication");

mApplicationField.setAccessible(true);

//9.1 将LoadedApk中的Application替换为真实的Application

mApplicationField.set(mPackageInfo,委托);

//修改ApplicationInfo className LoadedApk

//10.获取LoadApk中的mApplicationInfo变量

字段mApplicationInfoField=returnedApkClass.getDeclaredField("mApplicationInfo");

mApplicationInfoField.setAccessible(true);

//10.1 根据变量反射获取ApplicationInfo对象

ApplicationInfo mApplicationInfo=(ApplicationInfo)mApplicationInfoField.get(mPackageInfo);

//10.2 为其分配我们真正的APPplication ClassName名称

mApplicationInfo.className=app_name;

//11.执行代理Application onCreate生命周期

delegate.onCreate();

//解码完成

isBindReal=true;

用户评论

十言i

终于看到了加固系列的第二篇,希望能详细讲解一些常用的加固方法。

    有13位网友表示赞同!

红玫瑰。

我一直很想知道APK加固技术是怎么运作的,期待学习更多新的知识。

    有13位网友表示赞同!

不相忘

这个话题对我很有帮助,我正在尝试保护自己的应用不被反编译。

    有12位网友表示赞同!

余笙南吟

Android开发中加固的重要性不容忽视,希望这篇文章能深入讲解一些实战经验 。

    有9位网友表示赞同!

太难

想要了解如何有效地对抗apk恶意行为, 加固技术是必不可少的。

    有8位网友表示赞同!

单身i

期待作者能够分享一些最新有效的加固技术以及它们的优缺点。

    有17位网友表示赞同!

有你,很幸福

学习完第一篇后对Android APK加固有了初步的了解,这篇第二篇肯定更精彩!

    有14位网友表示赞同!

关于道别

想了解更多关于APK反编译和防御机制的内容, 这篇文章应该是很好的参考资料。

    有11位网友表示赞同!

無極卍盜

最近在接触安全领域,希望这篇文章能给我一些新的启发。

    有12位网友表示赞同!

孤单*无名指

作为一名Android开发者,了解加固技术非常必要,希望能从这篇文章中学到实战技巧。

    有5位网友表示赞同!

断秋风

每次看到有关Android的深入解析文章,我的热情都会被激发起来!

    有18位网友表示赞同!

放肆丶小侽人

相信这篇文章能帮我更好地理解APK加固的过程和原理。

    有12位网友表示赞同!

爱情的过失

很期待作者能够分享一些实用的案例和工具,以便我们更好地学习和应用加固技术。

    有7位网友表示赞同!

高冷低能儿

希望文章能涵盖不同类型的加固方法,并对比分析它们的优劣势。

    有16位网友表示赞同!

隔壁阿不都

在保护APP安全的道路上,不断学习新的加固技术非常重要。

    有16位网友表示赞同!

三年约

通过阅读这篇文章,我相信我能更全面地了解APK加固的技术现状和发展趋势。

    有7位网友表示赞同!

轨迹!

我是一个喜欢探索新鲜技术的开发者,这次一定要仔细阅读这篇文章!

    有17位网友表示赞同!

一尾流莺

感谢作者分享如此宝贵的知识,期待后续的文章继续深入探讨Android安全领域。

    有7位网友表示赞同!

【Android 应用程序加固技术分析篇二】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活

上一篇:《巴里公主》2024韩影惊悚大片完整版高清免费在线观看,1080p中文字幕,网盘资源分享 下一篇:全面解析:跨领域融合创新趋势与策略