Java环境解析apk文件信息
Posted ✧*꧁一品堂.技术学习笔记꧂*✧.
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java环境解析apk文件信息相关的知识,希望对你有一定的参考价值。
概述:Java解析apk文件,获取apk文件里的包名,版本号,图标文件等;
功能:可以提供给windows和linux平台使用;
原理:利用aapt.exe或者aapt这些anroid平台解析apk文件的工具,借用终端shell调用命令解析输出信息;
代码:
这里贴出一些关键代码,并给出代码注释,如下
1 package com.apkutils; 2 3 import java.io.BufferedReader; 4 import java.io.Closeable; 5 import java.io.File; 6 import java.io.FileInputStream; 7 import java.io.IOException; 8 import java.io.InputStream; 9 import java.io.InputStreamReader; 10 import java.util.HashMap; 11 import java.util.Map; 12 import java.util.Properties; 16 13 14 /** 15 * apk工具类。封装了获取Apk信息的方法。 16 * 17 * @author @author tony 18 * 19 * <p> 20 * <b>version description</b><br /> 21 * V0.2.1 修改程序名字为从路径中获得。 22 * </p> 23 */ 24 public class ApkUtil { 25 public static final String VERSION_CODE = "versionCode"; 26 public static final String VERSION_NAME = "versionName"; 27 public static final String SDK_VERSION = "sdkVersion"; 28 public static final String TARGET_SDK_VERSION = "targetSdkVersion"; 29 public static final String USES_PERMISSION = "uses-permission"; 30 public static final String APPLICATION_LABEL = "application-label"; 31 public static final String APPLICATION_ICON = "application-icon"; 32 public static final String USES_FEATURE = "uses-feature"; 33 public static final String USES_IMPLIED_FEATURE = "uses-implied-feature"; 34 public static final String SUPPORTS_SCREENS = "supports-screens"; 35 public static final String SUPPORTS_ANY_DENSITY = "supports-any-density"; 36 public static final String DENSITIES = "densities"; 37 public static final String PACKAGE = "package"; 38 public static final String APPLICATION = "application:"; 39 public static final String LAUNCHABLE_ACTIVITY = "launchable-activity"; 40 41 // api ---- os 42 static Map<String, String> OSVersion = new HashMap<String, String>(); 43 44 static { 45 OSVersion.put("3", "1.5"); 46 OSVersion.put("4", "1.6"); 47 OSVersion.put("5", "2.0"); 48 OSVersion.put("6", "2.0.1"); 49 OSVersion.put("7", "2.1"); 50 OSVersion.put("8", "2.2"); 51 OSVersion.put("9", "2.3"); 52 OSVersion.put("10", "2.3.3"); 53 OSVersion.put("11", "3.0"); 54 OSVersion.put("12", "3.1"); 55 OSVersion.put("13", "3.2"); 56 OSVersion.put("14", "4.0"); 57 OSVersion.put("15", "4.0.3"); 58 OSVersion.put("16", "4.1.1"); 59 OSVersion.put("17", "4.2"); 60 OSVersion.put("18", "4.3"); 61 OSVersion.put("19", "4.4"); 62 } 63 64 private static final String SPLIT_REGEX = "(: )|(=\')|(\' )|\'"; 65 private static final String FEATURE_SPLIT_REGEX = "(:\')|(\',\')|\'"; 66 /** 67 * aapt所在的目录。 68 */ 69 private String mAaptPath = "D:\\\\App\\\\";//winOS 70 //private String mAaptPath = ApkUtil.class.getClassLoader().getResource("").getPath();//linux 71 72 static String[] shellCommand; 73 static String softName = ""; 74 static { 75 shellCommand = new String[2]; 76 final String anOSName = System.getProperty("os.name"); 77 if (anOSName.toLowerCase().startsWith("windows")) { 78 // Windows XP, Vista ... 79 shellCommand[0] = "cmd"; 80 shellCommand[1] = "/C"; 81 softName = "aapt.exe"; 82 } else { 83 // Unix, Linux ... 84 shellCommand[0] = "/bin/sh"; 85 shellCommand[1] = "-c"; 86 softName = "aapt"; 87 } 88 } 89 90 /*** 91 * apkPath 92 */ 93 static String apkPath = "C:/Users/win7/Desktop/android/baiduyinyue_49.apk"; 94 95 /** 96 * 返回一个apk程序的信息。 97 * 98 * @param apkPath 99 * apk的路径。 100 * @return apkInfo 一个Apk的信息。 101 */ 102 public ApkInfo getApkInfo(String apkPath) throws Exception { 103 String command = mAaptPath + softName + " d badging \\"" + apkPath 104 + "\\""; 105 Process process; 106 try { 107 process = Runtime.getRuntime().exec( 108 new String[] {shellCommand[0], shellCommand[1], command}); 109 } catch (IOException e) { 110 process = null; 111 throw e; 112 } 113 114 ApkInfo apkInfo = null; 115 if(process != null){ 116 InputStream is = process.getInputStream(); 117 BufferedReader br = new BufferedReader( 118 new InputStreamReader(is, "utf8")); 119 String tmp = br.readLine(); 120 try { 121 if (tmp == null || !tmp.startsWith("package")) { 122 throw new Exception("参数不正确,无法正常解析APK包。输出结果为:\\n" + tmp + "..."); 123 } 124 apkInfo = new ApkInfo(); 125 do { 126 setApkInfoProperty(apkInfo, tmp); 127 } while ((tmp = br.readLine()) != null); 128 } catch (Exception e) { 129 throw e; 130 } finally { 131 process.destroy(); 132 closeIO(is); 133 closeIO(br); 134 } 135 } 136 return apkInfo; 137 } 138 139 /** 140 * 设置APK的属性信息。 141 * 142 * @param apkInfo 143 * @param source 144 */ 145 private void setApkInfoProperty(ApkInfo apkInfo, String source) { 146 if (source.startsWith(PACKAGE)) { 147 splitPackageInfo(apkInfo, source); 148 } else if (source.startsWith(LAUNCHABLE_ACTIVITY)) { 149 apkInfo.setLaunchableActivity(getPropertyInQuote(source)); 150 } else if (source.startsWith(SDK_VERSION)) { 151 apkInfo.setSdkVersion(getPropertyInQuote(source)); 152 apkInfo.setMinOSVersion(OSVersion.get(getPropertyInQuote(source))); 153 } else if (source.startsWith(TARGET_SDK_VERSION)) { 154 apkInfo.setTargetSdkVersion(getPropertyInQuote(source)); 155 } else if (source.startsWith(USES_PERMISSION)) { 156 apkInfo.addToUsesPermissions(getPropertyInQuote(source)); 157 } else if (source.startsWith(APPLICATION_LABEL)) { 158 apkInfo.setApplicationLable(getPropertyInQuote(source)); 159 } else if (source.startsWith(APPLICATION_ICON)) { 160 apkInfo.addToApplicationIcons(getKeyBeforeColon(source), 161 getPropertyInQuote(source)); 162 } else if (source.startsWith(APPLICATION)) { 163 String[] rs = source.split("( icon=\')|\'"); 164 apkInfo.setApplicationIcon(rs[rs.length - 1]); 165 } else if (source.startsWith(USES_FEATURE)) { 166 apkInfo.addToFeatures(getPropertyInQuote(source)); 167 } else if (source.startsWith(USES_IMPLIED_FEATURE)) { 168 apkInfo.addToImpliedFeatures(getFeature(source)); 169 } else { 170 171 } 172 try { 173 apkInfo.setApkFileSize(getFileSizes(new File(apkPath))); 174 } catch (Exception e) { 175 e.printStackTrace(); 176 } 177 } 178 179 private ImpliedFeature getFeature(String source) { 180 String[] result = source.split(FEATURE_SPLIT_REGEX); 181 ImpliedFeature impliedFeature = new ImpliedFeature(result[1], result[2]); 182 return impliedFeature; 183 } 184 185 /** 186 * 返回出格式为name: \'value\'中的value内容。 187 * 188 * @param source 189 * @return 190 */ 191 private String getPropertyInQuote(String source) { 192 int index = source.indexOf("\'") + 1; 193 return source.substring(index, source.indexOf(\'\\\'\', index)); 194 } 195 196 /** 197 * 返回冒号前的属性名称 198 * 199 * @param source 200 * @return 201 */ 202 private String getKeyBeforeColon(String source) { 203 return source.substring(0, source.indexOf(\':\')); 204 } 205 206 /** 207 * 分离出包名、版本等信息。 208 * 209 * @param apkInfo 210 * @param packageSource 211 */ 212 private void splitPackageInfo(ApkInfo apkInfo, String packageSource) { 213 String[] packageInfo = packageSource.split(SPLIT_REGEX); 214 apkInfo.setPackageName(packageInfo[2]); 215 apkInfo.setVersionCode(packageInfo[4]); 216 apkInfo.setVersionName(packageInfo[6]); 217 } 218 219 /** 220 * 释放资源。 221 * 222 * @param c 223 * 将关闭的资源 224 */ 225 private final void closeIO(Closeable c) { 226 if (c != null) { 227 try { 228 c.close(); 229 } catch (IOException e) { 230 e.printStackTrace(); 231 } 232 } 233 } 234 235 public static void main(String[] args) { 236 try { 237 ApkInfo apkInfo = new ApkUtil().getApkInfo(apkPath); 238 System.out.println(apkInfo); 239 IconUtil.extractFileFromApk(apkPath, apkInfo.getApplicationIcon(), 240 "D:\\\\icon.png"); 241 } catch (Exception e) { 242 e.printStackTrace(); 243 } 244 } 245 246 public String getmAaptPath() { 247 return mAaptPath; 248 } 249 250 public void setmAaptPath(String mAaptPath) { 251 this.mAaptPath = mAaptPath; 252 } 253 254 // 取得文件大小 255 public static long getFileSizes(File f) throws Exception { 256 long s = 0; 257 if (f.exists()) { 258 FileInputStream fis = null; 259 fis = new FileInputStream(f); 260 s = fis.available(); 261 } else { 262 System.out.println("文件不存在"); 263 } 264 return s; 265 } 266 }
上面加上阴影的部分代码,我想基本都是很好理解的,获取当前运行的操作系统类型,适配上相应的指令和软件,这样可以跨平台操作,而不需要修改代码;
获取到相应软件后,对apk文件一行一行的进行解析,将相应的属性存到javaBean中,并将apk文件图片通过流的方式写出到文件系统中,
1 package com.apkutils; 2 3 import java.io.BufferedInputStream; 4 import java.io.BufferedOutputStream; 5 import java.io.File; 6 import java.io.FileOutputStream; 7 import java.io.IOException; 8 import java.io.InputStream; 9 import java.util.zip.ZipEntry; 10 import java.util.zip.ZipFile; 11 12 13 /** 14 * 通过ApkInfo 里的applicationIcon从APK里解压出icon图片并存放到磁盘上 15 * @author @author tony 16 */ 17 public class IconUtil { 18 19 /** 20 * 从指定的apk文件里获取指定file的流 21 * @param apkpath 22 * @param fileName 23 * @return 24 */ 25 public static InputStream extractFileFromApk(String apkpath, String fileName) { 26 try { 27 ZipFile zFile = new ZipFile(apkpath); 28 ZipEntry entry = zFile.getEntry(fileName); 29 entry.getComment(); 30 entry.getCompressedSize(); 31 entry.getCrc(); 32 entry.isDirectory(); 33 entry.getSize(); 34 entry.getMethod(); 35 InputStream stream = zFile.getInputStream(entry); 36 return stream; 37 } catch (IOException e) { 38 e.printStackTrace(); 39 } 40 return null; 41 } 42 43 public static void extractFileFromApk(String apkpath, String fileName, String outputPath) throws Exception { 44 InputStream is = extractFileFromApk(apkpath, fileName); 45 46 File file = new File(outputPath); 47 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file), 1024); 48 byte[] b = new byte[1024]; 49 BufferedInputStream bis = new BufferedInputStream(is, 1024); 50 while(bis.read(b) != -1){ 51 bos.write(b); 52 } 53 bos.flush(); 54 is.close(); 55 bis.close(); 56 bos.close(); 57 } 58 }
这里没什么好说的,就是用JavaIO中zip的方式解析文件,并拿出图标写出到指定文件目录
参考文献 :
纯java从apk文件里获取包名、版本号、icon
以上是关于Java环境解析apk文件信息的主要内容,如果未能解决你的问题,请参考以下文章
如何解析 .apk 包中的 AndroidManifest.xml 文件
Android 逆向Android 逆向方法 ( 静态逆向解析 | 函数调用分析 | 动态运行跟踪 | 运行日志分析 | 文件格式解析 | 敏感信息分析 | 网络信息监控 | 环境伪装模拟 )