package parser.apk; import com.googlecode.dex2jar.reader.DexFileReader; import org.apache.commons.io.IOUtils; import parser.axml.ManifestInfo; import parser.axml.Parser; import parser.dex.DexClass; import parser.dex.DexFileAdapter; import parser.utils.CertTool; import parser.utils.FileTypesDetector; import parser.utils.HashTool; import java.io.*; import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; /** * apk package information, classes, methods, various.... * <p/> * <p/> * ex: * <p> * APK apk = new APK("test.apk"); * apk.getCertificateInfos(); * </p> */ public class APK { HashMap<String, String> certificateInfos; private List<DexClass> dexClasses = new ArrayList<>(); @SuppressWarnings("UnusedDeclaration") private String absolutePath; private String dexMd5 = null; private String fileName = ""; private ManifestInfo manifestInfo; private DexFileReader dexFileReader; private String dexSHA256; private ArrayList<String> subFileHash256List; private HashMap<String, ElfData> elfDataHashMap; private HashMap<String, APK> subApkDataMap; private HashMap<String, String> subFileHash256Map; private HashMap<String, String> subAPKHash256Map; private HashMap<String, String> metaDatas; private HashMap<String, String> subFilesMap; public APK(InputStream inputStream) throws IOException { BufferedInputStream bis = new BufferedInputStream(inputStream); bis.mark(bis.available()); certificateInfos = CertTool.getCertificateInfos(bis); bis.reset(); int size; byte[] buffer = new byte[2048]; ZipInputStream zipInputStream = new ZipInputStream(bis); ZipEntry entry; ByteArrayOutputStream byteArrayOutputStream; InputStream amInputStream = null; byte[] arscBytes = null; byte[] axmlBytes = null; while ((entry = zipInputStream.getNextEntry()) != null) { if (entry.getName().equals("classes.dex")) { byteArrayOutputStream = new ByteArrayOutputStream(); while ((size = zipInputStream.read(buffer, 0, buffer.length)) != -1) { byteArrayOutputStream.write(buffer, 0, size); } byte[] bytes = byteArrayOutputStream.toByteArray(); byteArrayOutputStream.close(); dexFileReader = new DexFileReader(byteArrayOutputStream.toByteArray()); dexMd5 = HashTool.getSHA256(bytes); continue; } if (entry.getName().equals("AndroidManifest.xml")) { byteArrayOutputStream = new ByteArrayOutputStream(); while ((size = zipInputStream.read(buffer, 0, buffer.length)) != -1) { byteArrayOutputStream.write(buffer, 0, size); } axmlBytes = byteArrayOutputStream.toByteArray(); byteArrayOutputStream.close(); // amInputStream = new ByteArrayInputStream(bytes); continue; } if (entry.getName().contains(".arsc")) { byteArrayOutputStream = new ByteArrayOutputStream(); while ((size = zipInputStream.read(buffer, 0, buffer.length)) != -1) { byteArrayOutputStream.write(buffer, 0, size); } arscBytes = byteArrayOutputStream.toByteArray(); byteArrayOutputStream.close(); } } if (axmlBytes != null) { System.out.println(axmlBytes.toString()); Parser parser = new Parser(axmlBytes, arscBytes); manifestInfo = parser.getManifestInfo(); } // if (amInputStream != null) { // final ManifestParser mp = new ManifestParser(); // manifestInfo = mp.parse(amInputStream, arscBytes); // } zipInputStream.close(); bis.close(); inputStream.close(); } /** * @param filePath 文件路径 * @param parseAXML 是否解析 AndroidManifest.xml * @param isParseCode 是否解析 Dex 中的代码,解析代码会比较耗时 * @param parseCert 是否解析证书 * @throws IOException */ public APK(String filePath, boolean parseAXML, boolean isParseCode, boolean parseCert) throws IOException { File file = new File(filePath); absolutePath = file.getAbsolutePath(); fileName = file.getName(); if (!FileTypesDetector.isAPK(file)) { throw new IOException("IT IS NOT A APK FILE."); } ZipFile zipFile = new ZipFile(file); ZipEntry zipEntry = zipFile.getEntry("classes.dex"); if (zipEntry != null) { dexMd5 = HashTool.getSHA256(IOUtils.toByteArray(zipFile.getInputStream(zipEntry))); } zipFile.close(); if (parseAXML) { // 解析清单信息 Parser parser = new Parser(file); manifestInfo = parser.getManifestInfo(); if (manifestInfo == null) { throw new FileNotFoundException("No AndroidManifest.xml"); } } initDexFileReader(file, isParseCode); if (parseCert) { certificateInfos = CertTool.getCertificateInfos(file); } } /** * @param file 文件 * @param parseAXML 是否解析 AndroidManifest.xml * @param isParseCode 是否解析 Dex * @param parseCert 是否解析证书 * @throws IOException */ public APK(File file, boolean parseAXML, boolean isParseCode, boolean parseCert) throws IOException { absolutePath = file.getAbsolutePath(); fileName = file.getName(); if (!FileTypesDetector.isAPK(file)) { throw new IOException("IT IS NOT A APK FILE."); } ZipFile zipFile = new ZipFile(file); ZipEntry zipEntry = zipFile.getEntry("classes.dex"); if (zipEntry != null) { dexMd5 = HashTool.getSHA256(IOUtils.toByteArray(zipFile.getInputStream(zipEntry))); } zipFile.close(); if (parseAXML) { Parser parser = new Parser(file); manifestInfo = parser.getManifestInfo(); if (manifestInfo == null) { throw new FileNotFoundException("No AndroidManifest.xml"); } } initDexFileReader(file, isParseCode); if (parseCert) { certificateInfos = CertTool.getCertificateInfos(file); } } /** * 构造函数 * * @param filePath 文件路径 * @throws IOException */ public APK(String filePath) throws Exception { File file = new File(filePath); absolutePath = file.getAbsolutePath(); fileName = file.getName(); if (!FileTypesDetector.isAPK(file)) { throw new Exception("IT IS NOT A APK FILE."); } ZipFile zipFile = new ZipFile(file); ZipEntry zipEntry = zipFile.getEntry("classes.dex"); if (zipEntry != null) { dexMd5 = HashTool.getMD5(IOUtils.toByteArray(zipFile.getInputStream(zipEntry))); dexSHA256 = HashTool.getSHA256(IOUtils.toByteArray(zipFile.getInputStream(zipEntry))); } initSubFileList(zipFile); zipFile.close(); initDexFileReader(file); // 解析清单信息 Parser parser = new Parser(file); manifestInfo = parser.getManifestInfo(); if (manifestInfo == null) { throw new Exception("No AndroidManifest.xml"); } certificateInfos = CertTool.getCertificateInfos(file); } /** * 构造函数 * * @param file 文件 * @throws IOException */ public APK(File file) throws IOException { if (!FileTypesDetector.isAPK(file)) { throw new IOException("IT IS NOT A APK FILE."); } absolutePath = file.getAbsolutePath(); fileName = file.getName(); ZipFile zipFile = new ZipFile(file); ZipEntry zipEntry = zipFile.getEntry("classes.dex"); if (zipEntry != null) { dexMd5 = HashTool.getMD5(IOUtils.toByteArray(zipFile.getInputStream(zipEntry))); dexSHA256 = HashTool.getSHA256(IOUtils.toByteArray(zipFile.getInputStream(zipEntry))); } initSubFileList(zipFile); zipFile.close(); Parser parser = new Parser(file); manifestInfo = parser.getManifestInfo(); initDexFileReader(file); certificateInfos = CertTool.getCertificateInfos(file); } public HashMap<String, String> getSubFilesMap() { return subFilesMap; } public HashMap<String, APK> getSubApkDataMap() { return subApkDataMap; } public HashMap<String, String> getSubAPKHash256Map() { return subAPKHash256Map; } public HashMap<String, ElfData> getElfDataHashMap() { return elfDataHashMap; } private void initSubFileList(ZipFile zipFile) { subFileHash256List = new ArrayList<>(); subFileHash256Map = new HashMap<>(); subAPKHash256Map = new HashMap<>(); elfDataHashMap = new HashMap<>(); subApkDataMap = new HashMap<>(); subFilesMap = new HashMap<>(); ZipEntry zipEntry; Enumeration enumeration = zipFile.entries(); while (enumeration.hasMoreElements()) { zipEntry = (ZipEntry) enumeration.nextElement(); String name = zipEntry.getName(); try { String fileType = FileTypesDetector.getType(zipFile.getInputStream(zipEntry)); subFilesMap.put(name, fileType); // FIXME elf got some bug. // if (fileType.contains("ELF")) { // String hash = HashTool.getSHA256(IOUtils.toByteArray(zipFile.getInputStream(zipEntry))); // Elf elf = new Elf(IOUtils.toByteArray(zipFile.getInputStream(zipEntry))); // List<String> strings = elf.loadStrings(); // // elfDataHashMap.put(name, new ElfData(hash, strings)); // continue; // } String hash = HashTool.getSHA256(IOUtils.toByteArray(zipFile.getInputStream(zipEntry))); if (fileType.contains("APK") || fileType.contains("ZIP") && FileTypesDetector.isAPK(zipFile.getInputStream(zipEntry))) { APK apk = new APK(zipFile.getInputStream(zipEntry)); subAPKHash256Map.put(name, hash); subApkDataMap.put(name, apk); continue; } subFileHash256Map.put(name, hash); } catch (IOException e) { e.printStackTrace(); } } } /** * 初始化DexFileReader,默认解析Code(解析比较耗时) * * @param file 文件 * @throws IOException */ private void initDexFileReader(File file) throws IOException { initDexFileReader(file, true); } /** * @param file 文件 * @param isParseCode 是否解析代码 * @throws IOException */ private void initDexFileReader(File file, Boolean isParseCode) throws IOException { dexFileReader = new DexFileReader(file); if (isParseCode) { dexFileReader.accept(new DexFileAdapter(dexClasses), DexFileReader.SKIP_DEBUG | DexFileReader.SKIP_ANNOTATION); } } /** * 获取文件名 * * @return 文件名 */ public String getFileName() { return fileName; } /** * 获取包名 * * @return 包名 */ public String getPackageName() { return manifestInfo.packageName; } /** * 获取版本号 * * @return 版本号 */ public String getVersionCode() { return manifestInfo.versionCode; } /** * 获取dexMD5 * * @return dex md5 */ @SuppressWarnings("UnusedDeclaration") public String getDexMD5() { return dexMd5; } /** * 获取dexMD5 * * @return dex md5 */ public String getDexSHA256() { return dexSHA256; } /** * 获取版本名 * * @return 版本名 */ public String getVersionName() { return manifestInfo.versionName; } /** * 获取应用名 * * @return 应用名 */ public String getLabel() { return manifestInfo.label; } /** * 获取权限列表 * * @return ArrayList<String> 权限列表 */ public ArrayList<String> getPermissions() { return manifestInfo.requestedPermissions; } /** * 获取证书信息 * * @return 格式 MD5:证书信息 */ public HashMap<String, String> getCertificateInfos() { return certificateInfos; } /** * 获取接收器信息,包含了对应的 INTENT * * @return 接收器信息 */ public HashMap<String, ArrayList<String>> getReceivers() { return manifestInfo.receivers; } /** * 获取服务列表 * * @return 服务列表 */ public ArrayList<String> getServices() { return manifestInfo.services; } /** * 获取 DexFileReader. * * @return dexFileReader, null 则表示没有dex文件 */ public DexFileReader getDexFileReader() { return dexFileReader; } /** * 获取 Activity 列表,包含了对应的 INTENT * * @return Activity 列表 */ public HashMap<String, ArrayList<String>> getActivities() { return manifestInfo.activities; } public List<DexClass> getDexClasses() { return dexClasses; } /** * 获取所有类/方法/内容 * La/b/c;->mtd; * * @return 方法Map <method : method body> */ public HashMap<String, String> getMethods() { HashMap<String, String> methods = new HashMap<>(); for (DexClass dexClass : dexClasses) { if (dexClass.methodMap.size() > 0) { for (String key : dexClass.methodMap.keySet()) { methods.put(key, dexClass.methodMap.get(key)); } } } return methods; } /** * 获取子包? */ @SuppressWarnings("UnusedDeclaration") public void getSubApks() { } /** * @return @return <class : set(String)> */ public HashMap<String, HashSet<String>> getStringsMap() { HashMap<String, HashSet<String>> stringMap = new HashMap<>(); final List<DexClass> dexClasses = new ArrayList<>(); try { for (DexClass dexClass : dexClasses) { stringMap.put(dexClass.className, new HashSet<>(dexClass.stringData)); } } catch (java.lang.OutOfMemoryError error) { error.printStackTrace(); } return stringMap; } /** * 获得 DEX 中存在的字符 * * @return 获取字符串 */ public List<String> getStrings() { return dexFileReader.loadStrings(); } @Override public String toString() { return "APK{" + "certificateInfos=" + certificateInfos + ", dexMd5='" + dexMd5 + '\'' + ", fileName='" + fileName + '\'' + ", manifestInfo=" + manifestInfo + '}'; } public HashMap<String, String> getSubFileHash256Map() { return subFileHash256Map; } @SuppressWarnings("UnusedDeclaration") public ArrayList<String> getSubFileHash256List() { return subFileHash256List; } public HashMap<String, String> getMetaDatas() { if (metaDatas == null) { metaDatas = manifestInfo.metaData; } return metaDatas; } public class ElfData { String hash; List<String> stringList; private ElfData(String hash, List<String> stringList) { this.hash = hash; this.stringList = stringList; } public String getHash() { return hash; } public List<String> getStringList() { return stringList; } } }