android 解析未安装apk中的AndroidManifest.xml以及系统源码分析
2017-07-11 10:42
966 查看
前言:
场景:在不安装apk的前提下,获取apk中的包名,LAUNCHER Activity等,这就需要解析我们的androidManifest.xml文件了,而我们的apk又是一个zip的压缩文件,通过压缩文件的方式--好压,rar等打开。可以看到我们以上的目录,而我们的AndroidManifest.xml就在其中,有了这个就好办了,那我们就只需要获取这个zip包中的androidManifest.xml文件,拿出来解析就行了。不过在这之前值得一说的是,当我们把项目打包成apk的时候,androidManifest.xml文件已经被加密了,所以直接拿出来通过解析xml的三种api解析是不行的。
获取xml文件的方式有两种:
1.通过反编译获取
2.通过ZipFile来获取。这个要通过google提供的api APKParser.jar来解析这个加密的xml文件。
APKParser下载:http://code.google.com/p/xml-apk-parser 其中还有个demo,可以参考!
分析如何解析apk中的androidManifest.xml之前,先从源码的角度了解下系统是如何解析我们的apk中的androidManifest.xml文件的。
源码角度分析-如何解析androidManifest.xml
解析apk是由系统的PackageManagerService来执行的,而PackageManagerService的解析工作是在它的初始化中的。PackageManagerService的初始化:在我们的SystemServer的initAndLoop方法中通过PackageManagerService.main()来初始化的。有些版本的源码可能是main()方法,SystemServer初始化了framework层的许多服务,这里仅仅只分析PackageManager。
深入到PackageManagerService.main()方法中
可以看到这里对PackageManagerService进行初始化操作。继续追踪下去,看下new PackageManagerService()到底进行了什么工作。
在它的初始化方法中,有这么段代码,分别是先获取到系统目录下的app等文件夹,然后通过scanDirLI()这个方法来扫描其中的apk文件。那接下来又追踪到我们的scanDirLI()方法。
可以看到,这个方法遍历整个文件夹,然后再通过scanPackageLI()方法来扫描其中的apk文件。
在scanPackageLI()方法中又通过一个parsePackage()的方法来扫描具体的apk文件。
它返回的package对象就包含了我们apk中的许多信息。那我们就继续来追踪下parsePackage()整个方法,他到底是如何解析我们的apk的。
这方法的代码有点长,分开来截图,这里有几个重点:
1.第一个红框是判断是否为apk文件,不是则返回null;
2.openXmlResourceParser方法返回的是一个Xml资源解析对象,这里解析的就是我们的androidManifest.xml文件。
3.调用他的同名函数parsePackage()方法进行具体的解析操作。如下图。
同名函数parsePackage的解析操作:
从上面的代码可以清楚的了解到,分别对xml文件中的application,permission等节点进行解析,而我们的activity,service等四大组件和一些其他的节点信息就在parseApplication()方法中进行解析得到的,同时把解析到的到四大组件以及xml文件中的其他信息封装到返回的package对象中。以上就是源码如何解析apk中androidManifest.xml文件的具体流程,当然packageManagerService的初始化工作并不仅仅是解析apk文件,他还有对dex文件进行优化以及一些其他的操作,想了解的可以深入看看源码。
了解了源码是如何解析androidManifest.xml文件之后,对于解析未安装的apk中的xml文件就更不在话下了。
解析未安装apk文件中的androidManifest.xml文件
在贴解析代码之前,来个解析结果图:由于数量过多,所以这里仅仅截取一小部分,以上结果输出了3个字段。
分别是tag-name 代表节点信息;name代表当前节点属性的key值;value代表当前节点属性的value值。
不过解析到这并不行,还要能获取特定的信息,就比如刚开始所说的,需要包名/入口activity/这个apk的渠道信息等,这里的demo仅仅演示入口activity的获取。
package com.ljx.test; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; import android.content.res.AXmlResourceParser; import android.util.TypedValue; public class AnalysisApk { public static void main(String[] args) { File file = null; file = new File("F:\\XXX.apk"); ArrayList<String> mActivities = new ArrayList<String>(); try { ZipFile zipFile = new ZipFile(file); Enumeration enumeration = zipFile.entries(); // 获取到apk中的AndroidManifest.xml文件 ZipEntry zipEntry = zipFile.getEntry(("AndroidManifest.xml")); AXmlResourceParser parser = new AXmlResourceParser(); parser.open(zipFile.getInputStream(zipEntry)); boolean flag = true; while (flag) { int event = parser.next(); if (event == XmlPullParser.START_TAG) { int count = parser.getAttributeCount(); //// 解析整个AndroidManifest.xml文件并输出 // for (int i = 0; i != parser.getAttributeCount(); ++i) { // System.out.printf("%s%s%s=\"%s\"", // new StringBuilder(10), // getNamespacePrefix(parser.getAttributePrefix(i)), // parser.getAttributeName(i), // getAttributeValue(parser, i)); // System.out.println(); // } for (int i = 0, size = parser.getAttributeCount(); i != size; ++i) { // 解析整个AndroidManifest.xml文件并输出 // System.out.println("tag-name " + parser.getName()); // System.out.println("name " + // parser.getAttributeName(i)); // System.out.println("value " + // getAttributeValue(parser,i)); // System.out.println(""); // 获取应用入口activity if (parser.getName().endsWith("activity") && parser.getAttributeName(i).equals("name")) { mActivities.add(getAttributeValue(parser, i)); } if (parser.getAttributeValue(i).contains("MAIN")) { System.out.println(mActivities.get(mActivities.size() - 1)); return; } } } } } catch (ZipException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (XmlPullParserException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private static String getNamespacePrefix(String prefix) { if (prefix == null || prefix.length() == 0) { return ""; } return prefix + ":"; } private static String getAttributeValue(AXmlResourceParser parser, int index) { int type = parser.getAttributeValueType(index); int data = parser.getAttributeValueData(index); if (type == TypedValue.TYPE_STRING) { return parser.getAttributeValue(index); } if (type == TypedValue.TYPE_ATTRIBUTE) { return String.format("?%s%08X", getPackage(data), data); } if (type == TypedValue.TYPE_REFERENCE) { return String.format("@%s%08X", getPackage(data), data); } if (type == TypedValue.TYPE_FLOAT) { return String.valueOf(Float.intBitsToFloat(data)); } if (type == TypedValue.TYPE_INT_HEX) { return String.format("0x%08X", data); } if (type == TypedValue.TYPE_INT_BOOLEAN) { return data != 0 ? "true" : "false"; } if (type == TypedValue.TYPE_DIMENSION) { return Float.toString(complexToFloat(data)) + DIMENSION_UNITS[data & TypedValue.COMPLEX_UNIT_MASK]; } if (type == TypedValue.TYPE_FRACTION) { return Float.toString(complexToFloat(data)) + FRACTION_UNITS[data & TypedValue.COMPLEX_UNIT_MASK]; } if (type >= TypedValue.TYPE_FIRST_COLOR_INT && type <= TypedValue.TYPE_LAST_COLOR_INT) { return String.format("#%08X", data); } if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { return String.valueOf(data); } return String.format("<0x%X, type 0x%02X>", data, type); } private static String getPackage(int id) { if (id >>> 24 == 1) { return "android:"; } return ""; } private static void log(StringBuilder xmlSb, String format, Object... arguments) { log(true, xmlSb, format, arguments); } private static void log(boolean newLine, StringBuilder xmlSb, String format, Object... arguments) { // System.out.printf(format,arguments); // if(newLine) System.out.println(); xmlSb.append(String.format(format, arguments)); if (newLine) xmlSb.append("\n"); } /////////////////////////////////// ILLEGAL STUFF, DONT LOOK :) public static float complexToFloat(int complex) { return (float) (complex & 0xFFFFFF00) * RADIX_MULTS[(complex >> 4) & 3]; } private static final float RADIX_MULTS[] = { 0.00390625F, 3.051758E-005F, 1.192093E-007F, 4.656613E-010F }; private static final String DIMENSION_UNITS[] = { "px", "dip", "sp", "pt", "in", "mm", "", "" }; private static final String FRACTION_UNITS[] = { "%", "%p", "", "", "", "", "", "" }; }
上面main方法中注释的两段代码,都是对androidManifest.xml文件进行解析,只是输出样式不同,有兴趣的可以自己试着写下,然后看下输出结果是否是我们应用的入口activity。
上面代码的思路是,解析androidManifest.xml文件中的所有节点,然后找到其中的activity节点,紧接着对这个activity中的属性进行解析,看是否包含"MAIN",因为只有"MAIN"以及"LAUNCHER"同时存在的时候,这个activity就是我们应用的入口activity,不过上面的代码有一点问题的是,当androidManifest.xml中如果存在多个activity的action是"android.intent.action.MAIN"的时候,上面的判断逻辑就不正确了,严谨点的话是把MAIN替换成LAUNCHER,不过我这是事先知道自己应用只有一个activity中的action是...MAIN的,所以这里这样处理了。可以看到通过上面的demo代码,找到了SplashActivity,然后可以打开我们项目的AndroidManifest.xml文件看下,是否是这个activity。
通过上图可以发现,我们解析是完全正确的,
如何解析androidManifest.xml文件以及系统层面的源码分析,基本上就到这了,仔细看看代码,然后自己动手写一写。如果以后有这方面的需求,相信也难道不到你了!
相关文章推荐
- Android动态部署二:APK安装及AndroidManifest.xml解析流程分析
- Android APK应用安装原理解析之AndroidManifest使用PackageParser.parserPackage原理分析
- 【安卓系统源码学习之permission】 系统源代码AndroidManifest.xml分析之permission解读
- Android 软件安装程序(*.apk)的结构分析、反编译以及汉化
- android smack源码分析——接收消息以及如何解析消息
- dos 安装android apk 以及导出系统文件(手机需root)到本地磁盘
- android关于AndroidManifest.xml详细分析 清单文件解析
- Android开发——Android系统启动以及APK安装、启动过程
- Android APK应用安装原理(1)-解析AndroidManifest原理-PackageParser.parserPackage
- android smack源码分析——接收消息以及如何解析消息
- android源码解析之(十三)-->apk安装流程
- Android系统应用程序安装过程源码分析
- 关于androidManifest.xml的概叙以及intent-filter的详细分析
- 安装后新建Android出现“AndroidManifest.xml 系统找不到指定的文件”解决方案
- 安装后新建Android出现“AndroidManifest.xml 系统找不到指定的文件”解决方案
- 使用AXMLParser解析apk中的AndroidManifest.xml
- 批量解析apk的AndroidManifest.xml获得Pagename(Python)
- android smack源码分析——接收消息以及如何解析消息
- android smack源码分析——接收消息以及如何解析消息
- android源码解析之(十二)-->系统启动并解析Manifest的流程