0 概述
很多apk在zip格式上做手脚,防止逆向工具解析,不过都很好解决,最近分析时突然发现一个apk逆向时报一大堆错,main_activity也解析不出来,后来发现是AXML格式有问题,由于没有官方的AXML格式说明,这里花了挺多时间学习。
通过010 editor的模板可以大致了解格式,如下图:
AXML格式的深度分析,有个大佬讲的很完整了,可参考:blog.csdn.net/beyond702/a… 观看此文还需了解普通的xml格式,尤其是xml命名空间相关。
1 问题描述
注:下面用到的010editor模板都是AndroidResource.bt模板,比AndroidManifest.bt更详细,两者本质上是同样的结构
python自动化分析apk时发现一个异常的apk,他的manifest里面没有xmlns:android="http://schemas.android.com/apk/res/android",而官方文档有说明,manifest里面必须要有这个命名空间。用jeb打开后观察到的manifest示例如下(太长了截图截不全):
<?xml version="1.0" encoding="UTF-8"?>
<manifest android:compileSdkVersion="28" android:compileSdkVersionCodename="9"
android:versionName="4.3.0"
package="aaa.bbb.ccc"
platformBuildVersionCode="28"
platformBuildVersionName="9" versionCode="1"
xmlns:activity="http://schemas.android.com/apk/distribution"
xmlns:uses-permissionandroid="http://schemas.android.com/apk/distribution">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
...
...
他定义了两个命名空间,但是都是错误的,如果这样写,使用android studio编译时会报错。不过这里还有个问题,就是在jeb里面,依然可以看到android命名空间(manifest的第三个属性android:versionName),而且正常解析出结果了
之后使用python的androguard包对其解析,结果报错,如下:
Confused: name contains a unknown namespace prefix: 'android:name'. This is either a broken AXML file or some attempt to break stuff.
Name 'android:name' contains invalid characters!
Confused: name contains a unknown namespace prefix: 'android:exported'. This is either a broken AXML file or some attempt to break stuff.
Name 'android:exported' contains invalid characters!
经过代码跟踪定位后,发现确实就是因为缺少android命名空间导致报错。
最后吧androidmanifest.xml文件放到010 editor里面用模板解析看一下,解析正确,而且字符串块当中包含了http://schemas.android.com/apk/res/android,这个就是android命名空间的URI,如下图
之后在第一个Element里面随便找一些attribute,可以看到versionCode是有正确的命名空间URI的,但是在jeb里面versionCode没有;而且jeb里面versionName是有命名空间的,但是axml文件里面却没有,并且versionName字符串都是空的(如下图,attrib[1]就是versionName,但是struct ResStringPool_ref name是空的)
前期信息收集的差不多了,发现了这么多矛盾点,就想了解其中的原理,于是问题从最开始的“如何用python正常解析这个apk”,转化为“如何自己写一个类似的apk”。
2 分析
我猜测jeb自己会给本该有命名空间但是实际上缺失了的attribute加上android命名空间,所以jeb的解析结果不能参考,一切以010模板解析的结果为准。
仔细分析Element,发现很多attribute都有正确的命名空间,而二进制文件中的定义命名空间的块确实没有定义http://schemas.android.com/apk/res/android。猜测是这里被修改了,于是自己写了个apk,包含了正确的android命名空间(只有这样才能编译通过),并同时写入一个错误的命名空间,之后用010editor编辑,吧正确的URI也指向错误的,做出与问题apk类似的效果:
(吧android改成activity,uri改成错误的)
之后用jeb解析,发现所有的命名空间都消失了,说明肯定不止这一个地方有修改。
(图没了,这里就是所有的android:前缀都消失了)
之后有点走错方向,搞了半天没高出结果,之后突然想到android studio也能反编译apk,于是问题apk拖进去看了看,终于发现了问题所在
android studio解析的结果可看出,很多attribute都没有name,只有value,那答案只有一个了:问题apk的axml吧部分attribute的name值给抹掉了。
这里用到了一个axml的特性:android系统在解析axml的attribute的时候,是用资源id定位name的具体名称,而name保存的id同时充当资源id和字符串id,所以这里字符串id所指向的字符串可以置空。此时开始修改自己写的apk。
从下图可看出,资源id为8的资源,就是versionName
字符串id为8的字符串原本也是versionName,图中已经被我置空了(注意,不能直接用0替换所有的字符串,需要吧字符串长度改成0并删除多余的字符,然后修复文件总长度、修复字符串块的总长度、字符串偏移,比较复杂)
之后再用jeb打开自己写的apk,经过修改的attribute串果然被自动加上了命名空间
用android studio打开也跟问题apk一样,被修改的attribute name为空,这下跟问题apk基本一致了。
3 总结
总的来说就是先写一个正常apk,然后吧编译好的apk的androidmanifest.xml文件拿出来,改掉命名空间块(实际上xml的各个属性还是保存了原始的命名空间uri的,所以手机能正常解析);之后在字符串块中,将部分字符串替换为空字符串;最后修复axml文件头、字符串块的头。
最后用某个一键签名工具尝试签名结果失败了,应该是软件太辣鸡,之后用android sdk自带的apksigner成功签名。
至于最开始的问题,怎么用python正确的解析,可以选择自己写解析脚本,或者修改androguard源码,了解了原理之后应该都不难 就是费时间