该博客基于>【http://blog.csdn.net/jiangwei0910410003/article/details/48415225/】
大神的文章分析完善的,若没了解过基础请先移步大神博客看看。
完善点一:
android ActivityThread源码【android-sdk\sources\android-xx\android\app\ActivityThread.java】文件中,版本不一致源码稍有差异,这里不体现其他差异,为了解决apk解壳时的兼容性必须注意以下差异点:
android -Build.VERSION.SDK_INT<=18 :
final HashMap<String, WeakReference> mPackages
= new HashMap<String, WeakReference>();
final HashMap<String, WeakReference> mResourcePackages
= new HashMap<String, WeakReference>();
final HashMap mDefaultDisplayMetrics
= new HashMap();
final HashMap > mActiveResources
= new HashMap >();
可以看到其包以及装载LoadApk都是使用HashMap。
而在api>18中则是如下:
final ArrayMap<String, WeakReference> mPackages
= new ArrayMap<String, WeakReference>();
final ArrayMap<String, WeakReference> mResourcePackages
= new ArrayMap<String, WeakReference>();
final ArrayList mRelaunchingActivities
= new ArrayList();
很明显可以看到大于18的api使用的是ArrayMap来装载。
这个差异性在解壳的apk中需要区分:
public static void hookDexLoader(String packageName, String apkFileName,
String odexPath, String libPath) {
Object currentActivityThread = RefInvoke.invokeStaticMethod(
"android.app.ActivityThread", "currentActivityThread",
new Class[]{}, new Object[]{});
WeakReference wr = null;
if (Build.VERSION.SDK_INT < 19) {
HashMap mPackages = (HashMap) RefInvoke.getFieldOjbect(
"android.app.ActivityThread", currentActivityThread,
"mPackages");
wr = (WeakReference) mPackages.get(packageName);
} else {
ArrayMap mPackages = (ArrayMap) RefInvoke.getFieldOjbect(
"android.app.ActivityThread", currentActivityThread,
"mPackages");
wr = (WeakReference) mPackages.get(packageName);
}
DexClassLoader dLoader = new DexClassLoader(apkFileName, odexPath,
libPath, (ClassLoader) RefInvoke.getFieldOjbect(
"android.app.LoadedApk", wr.get(), "mClassLoader"));
RefInvoke.setFieldOjbect("android.app.LoadedApk", "mClassLoader",
wr.get(), dLoader);
}
完善点二:
加密APK的xxxx.so库的存放加载路径。
出错主要是:api18以下的机子找不到so库。
经过测试API<18 时,需要在data/data/xxx/下新建so存放文件夹,因为18以下加载so库时不能加载原来的data/data/xxx/lib/下的库。因此需要在解壳源码时把so写入新文件夹,同时指定DexClassLoader 的lib环境。
复制so文件:
private void copyLib(File newlib) {
File old = new File(PlatformInfo.getAppLibDir(this.getPackageName()));
if (!old.exists()) {
Log.i("result", "lib not find");
}
for (File file : old.listFiles()) {
String libname = file.getName();
File storeFile = new File(newlib.getAbsolutePath() + "/" + libname);
try {
storeFile.createNewFile();
nioTransferCopy(file, storeFile);
} catch (Exception e) {
}
}
for (File file : old.listFiles()) {
file.delete();
}
}
在API>=18,不需要拷贝so文件到新文件夹,直接引用apk原来lib下面的库环境即可。
这个差别主要源于so linker 加载器,这个加载器我也是云里雾里,解决该问题主要靠错误日志,因此so加载器不做过多说明。
完善点三:
加密工具完善:
1 使用apktool.jar解开apk包。
......
String cmdUnpack = "tools\\apktool.jar d -s "+ file.getAbsolutePath()+" -o "+outputfile.getAbsolutePath();
System.out.println("正在执行解包命令....");
CMDUtils.runCMD(cmdUnpack);
......
2 修改AndroidManifest.xml文件【添加指定解壳dex的Application】
//解析原apk项目清单文件
Document doc=XMLPares1.parse("force\\"+file.getName().substring(0, file.getName().lastIndexOf("."))+"\\AndroidManifest.xml");
//获取到Application节点
Element elt_root=doc.getDocumentElement();
Element elt_application=XMLPares1.getChildElement(elt_root, "application");
//添加格式:String add_child_elt="+elt_application.getAttribute("android:name")+"\"/>";
//新建一个节点
Element child_elt_tag=doc.createElement("meta-data");
child_elt_tag.setAttribute("android:name", "APPLICATION_CLASS_NAME");
String tag_application_value=elt_application.getAttribute("android:name");
if(tag_application_value!=null&&tag_application_value.length()>0){
child_elt_tag.setAttribute("android:value", tag_application_value);
}else{
child_elt_tag.setAttribute("android:value", "");
}
elt_application.appendChild(child_elt_tag);
elt_application.setAttribute("android:name",rename_application);
//把当前更改的文档变成xml
String update_xml =XMLPares1.doc2String(doc);
//写入xml
writeString(mainfest_path,update_xml);
3 加壳解压的原apk -dex【这里的加密自己定义】
4 重新打包,并且自动签名,输出到指定路径。
System.out.println("------------->开始重新打包");
String unsignApk = file.getName().substring(0, file.getName().lastIndexOf("."))+ "_jiagu_unsing.apk";
String cmdpack="tools\\apktool.jar b force\\"+file.getName().substring(0, file.getName().lastIndexOf("."))+" -o force\\"+unsignApk;
CMDUtils.runCMD(cmdpack);
Thread.sleep(PACKAGE_TIME);
CMDUtils.CMD("exit");
//签名
System.out.println("------------->开始重新签名");
String outputsingapkname=file.getName().substring(0, file.getName().lastIndexOf("."))+"_jiagu_sing.apk";
String cmdsingapk="jarsigner -digestalg SHA1 -sigalg MD5withRSA -verbose -keystore "+key_store_path+" -storepass "+key_store_pwd+" -signedjar "+apkoutputdir+"\\"+outputsingapkname+" force\\"+unsignApk+" "+key_aleas;
CMDUtils.runCMD(cmdsingapk);
完善点四:
在art环境下快速加载dex文件,主要用到的技术是so 的hook技术,art模式下dex会有一个转换oat格式优化文件。使用的是git上开源的hook库libturbo-dex【https://github.com/asLody/TurboDex】
简单使用:
在解壳appliactoin中:
@Override
protected void attachBaseContext(Context base) {
//调用hook
boolean flag=DexOperation.enableTurboDex();
super.attachBaseContext(base);
……..
}
以上是项目的完善点。
该加密方式是第二代dex隐藏加壳加密,使破解者使用源码查看软件不能直接查看源码。
这中方式加密的dex确实无法直接查看源码,反编译后只能看到解壳代码,但是缺点也很明显:只要一部root过的手机就可以拿到源码,因此在进一步优化应该是直接在内存中加载dex字节码,无需释放保存到本地,这样可以提高破解难度。但是仍然也可以获取到源码,通过dump内存。攻与防 都是相对的,新的加密方式始终会有破解方法计算目前最主流加密方式VMP也无法挡住广大技术爱好者的爆破。
android本开源,何须太加密。加密越深,第一次打开应用速度就越慢。
最后附上部分源码(加壳工具类代码写的有点乱):
https://github.com/lambertlei/Dexencryption.git