Как разобрать AndroidManifest.xml-файл внутри an.пакет apk
этот файл представляется в двоичном формате XML. Что это за формат и как он может быть проанализирован программно (в отличие от использования инструмента дампа aapt в SDK)?
этот двоичный формат не обсуждается в документации здесь.
Примечание: Я хочу получить доступ к этой информации из-за пределов среды Android, предпочтительно из Java.
14 ответов:
используйте android-apktool
есть приложение, которое читает apk файлы и декодирует XMLs почти в оригинальной форме.
использование:
apktool d Gmail.apk && cat Gmail/AndroidManifest.xmlРегистрация android-apktool для получения дополнительной информации
этот метод Java, который работает на Android, документирует (что я смог интерпретировать) двоичный формат AndroidManifest.xml-файл в поле .пакет apk. Во втором поле кода показано, как вызвать метод decompressXML и как загрузить байт[] из файла пакета приложения на устройстве. (Есть поля, назначение которых я не понимаю, если вы знаете, что они означают, скажите мне, я обновлю информацию.)
// decompressXML -- Parse the 'compressed' binary form of Android XML docs // such as for AndroidManifest.xml in .apk files public static int endDocTag = 0x00100101; public static int startTag = 0x00100102; public static int endTag = 0x00100103; public void decompressXML(byte[] xml) { // Compressed XML file/bytes starts with 24x bytes of data, // 9 32 bit words in little endian order (LSB first): // 0th word is 03 00 08 00 // 3rd word SEEMS TO BE: Offset at then of StringTable // 4th word is: Number of strings in string table // WARNING: Sometime I indiscriminently display or refer to word in // little endian storage format, or in integer format (ie MSB first). int numbStrings = LEW(xml, 4*4); // StringIndexTable starts at offset 24x, an array of 32 bit LE offsets // of the length/string data in the StringTable. int sitOff = 0x24; // Offset of start of StringIndexTable // StringTable, each string is represented with a 16 bit little endian // character count, followed by that number of 16 bit (LE) (Unicode) chars. int stOff = sitOff + numbStrings*4; // StringTable follows StrIndexTable // XMLTags, The XML tag tree starts after some unknown content after the // StringTable. There is some unknown data after the StringTable, scan // forward from this point to the flag for the start of an XML start tag. int xmlTagOff = LEW(xml, 3*4); // Start from the offset in the 3rd word. // Scan forward until we find the bytes: 0x02011000(x00100102 in normal int) for (int ii=xmlTagOff; ii<xml.length-4; ii+=4) { if (LEW(xml, ii) == startTag) { xmlTagOff = ii; break; } } // end of hack, scanning for start of first start tag // XML tags and attributes: // Every XML start and end tag consists of 6 32 bit words: // 0th word: 02011000 for startTag and 03011000 for endTag // 1st word: a flag?, like 38000000 // 2nd word: Line of where this tag appeared in the original source file // 3rd word: FFFFFFFF ?? // 4th word: StringIndex of NameSpace name, or FFFFFFFF for default NS // 5th word: StringIndex of Element Name // (Note: 01011000 in 0th word means end of XML document, endDocTag) // Start tags (not end tags) contain 3 more words: // 6th word: 14001400 meaning?? // 7th word: Number of Attributes that follow this tag(follow word 8th) // 8th word: 00000000 meaning?? // Attributes consist of 5 words: // 0th word: StringIndex of Attribute Name's Namespace, or FFFFFFFF // 1st word: StringIndex of Attribute Name // 2nd word: StringIndex of Attribute Value, or FFFFFFF if ResourceId used // 3rd word: Flags? // 4th word: str ind of attr value again, or ResourceId of value // TMP, dump string table to tr for debugging //tr.addSelect("strings", null); //for (int ii=0; ii<numbStrings; ii++) { // // Length of string starts at StringTable plus offset in StrIndTable // String str = compXmlString(xml, sitOff, stOff, ii); // tr.add(String.valueOf(ii), str); //} //tr.parent(); // Step through the XML tree element tags and attributes int off = xmlTagOff; int indent = 0; int startTagLineNo = -2; while (off < xml.length) { int tag0 = LEW(xml, off); //int tag1 = LEW(xml, off+1*4); int lineNo = LEW(xml, off+2*4); //int tag3 = LEW(xml, off+3*4); int nameNsSi = LEW(xml, off+4*4); int nameSi = LEW(xml, off+5*4); if (tag0 == startTag) { // XML START TAG int tag6 = LEW(xml, off+6*4); // Expected to be 14001400 int numbAttrs = LEW(xml, off+7*4); // Number of Attributes to follow //int tag8 = LEW(xml, off+8*4); // Expected to be 00000000 off += 9*4; // Skip over 6+3 words of startTag data String name = compXmlString(xml, sitOff, stOff, nameSi); //tr.addSelect(name, null); startTagLineNo = lineNo; // Look for the Attributes StringBuffer sb = new StringBuffer(); for (int ii=0; ii<numbAttrs; ii++) { int attrNameNsSi = LEW(xml, off); // AttrName Namespace Str Ind, or FFFFFFFF int attrNameSi = LEW(xml, off+1*4); // AttrName String Index int attrValueSi = LEW(xml, off+2*4); // AttrValue Str Ind, or FFFFFFFF int attrFlags = LEW(xml, off+3*4); int attrResId = LEW(xml, off+4*4); // AttrValue ResourceId or dup AttrValue StrInd off += 5*4; // Skip over the 5 words of an attribute String attrName = compXmlString(xml, sitOff, stOff, attrNameSi); String attrValue = attrValueSi!=-1 ? compXmlString(xml, sitOff, stOff, attrValueSi) : "resourceID 0x"+Integer.toHexString(attrResId); sb.append(" "+attrName+"=\""+attrValue+"\""); //tr.add(attrName, attrValue); } prtIndent(indent, "<"+name+sb+">"); indent++; } else if (tag0 == endTag) { // XML END TAG indent--; off += 6*4; // Skip over 6 words of endTag data String name = compXmlString(xml, sitOff, stOff, nameSi); prtIndent(indent, "</"+name+"> (line "+startTagLineNo+"-"+lineNo+")"); //tr.parent(); // Step back up the NobTree } else if (tag0 == endDocTag) { // END OF XML DOC TAG break; } else { prt(" Unrecognized tag code '"+Integer.toHexString(tag0) +"' at offset "+off); break; } } // end of while loop scanning tags and attributes of XML tree prt(" end at offset "+off); } // end of decompressXML public String compXmlString(byte[] xml, int sitOff, int stOff, int strInd) { if (strInd < 0) return null; int strOff = stOff + LEW(xml, sitOff+strInd*4); return compXmlStringAt(xml, strOff); } public static String spaces = " "; public void prtIndent(int indent, String str) { prt(spaces.substring(0, Math.min(indent*2, spaces.length()))+str); } // compXmlStringAt -- Return the string stored in StringTable format at // offset strOff. This offset points to the 16 bit string length, which // is followed by that number of 16 bit (Unicode) chars. public String compXmlStringAt(byte[] arr, int strOff) { int strLen = arr[strOff+1]<<8&0xff00 | arr[strOff]&0xff; byte[] chars = new byte[strLen]; for (int ii=0; ii<strLen; ii++) { chars[ii] = arr[strOff+2+ii*2]; } return new String(chars); // Hack, just use 8 byte chars } // end of compXmlStringAt // LEW -- Return value of a Little Endian 32 bit word from the byte array // at offset off. public int LEW(byte[] arr, int off) { return arr[off+3]<<24&0xff000000 | arr[off+2]<<16&0xff0000 | arr[off+1]<<8&0xff00 | arr[off]&0xFF; } // end of LEWэтот метод считывает AndroidManifest в байт[] для обработка:
public void getIntents(String path) { try { JarFile jf = new JarFile(path); InputStream is = jf.getInputStream(jf.getEntry("AndroidManifest.xml")); byte[] xml = new byte[is.available()]; int br = is.read(xml); //Tree tr = TrunkFactory.newTree(); decompressXML(xml); //prt("XML\n"+tr.list()); } catch (Exception ex) { console.log("getIntents, ex: "+ex); ex.printStackTrace(); } } // end of getIntentsбольшинство приложений хранятся в /system /app, который читается без root my Evo, другие приложения находятся в/data / app, который мне нужен root, чтобы увидеть. Аргумент " путь "выше будет выглядеть примерно так:" /system/app/Weather.apk"
как насчет использования Android Asset Packaging Tool (aapt), из Android SDK, в скрипт Python (или что-то еще)?
через aapt (http://elinux.org/Android_aapt), действительно, Вы можете получить информацию о .apk пакета и о его AndroidManifest.xml. В частности, вы можете извлечь значения отдельных элементов.apk пакета через 'dump' суб-команды. Например, вы можете извлечь user-permissions на AndroidManifest.xml внутри .apk пакета таким образом:
$ aapt dump permissions package.apkздесь пакета.apk ваш .apk пакета.
кроме того, вы можете использовать команду Unix pipe для очистки выходных данных. Например:
$ aapt dump permissions package.apk | sed 1d | awk '{ print $NF }'вот скрипт на Питоне что к тому программно:
import os import subprocess #Current directory and file name: curpath = os.path.dirname( os.path.realpath(__file__) ) filepath = os.path.join(curpath, "package.apk") #Extract the AndroidManifest.xml permissions: command = "aapt dump permissions " + filepath + " | sed 1d | awk '{ print $NF }'" process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=None, shell=True) permissions = process.communicate()[0] print permissionsаналогичным образом вы можете извлечь другую информацию (например,пакета,название и т. д...) из AndroidManifest.xml:
#Extract the APK package info: shellcommand = "aapt dump badging " + filepath process = subprocess.Popen(shellcommand, stdout=subprocess.PIPE, stderr=None, shell=True) apkInfo = process.communicate()[0].splitlines() for info in apkInfo: #Package info: if string.find(info, "package:", 0) != -1: print "App Package: " + findBetween(info, "name='", "'") print "App Version: " + findBetween(info, "versionName='", "'") continue #App name: if string.find(info, "application:", 0) != -1: print "App Name: " + findBetween(info, "label='", "'") continue def findBetween(s, prefix, suffix): try: start = s.index(prefix) + len(prefix) end = s.index(suffix, start) return s[start:end] except ValueError: return ""если вместо этого вы хотите разобрать все XML-дерево AndroidManifest, вы можете сделать это аналогичным образом с помощью xmltree:
aapt dump xmltree package.apk AndroidManifest.xmlиспользование Python как и раньше:
#Extract the AndroidManifest XML tree: shellcommand = "aapt dump xmltree " + filepath + " AndroidManifest.xml" process = subprocess.Popen(shellcommand, stdout=subprocess.PIPE, stderr=None, shell=True) xmlTree = process.communicate()[0] print "Number of Activities: " + str(xmlTree.count("activity")) print "Number of Services: " + str(xmlTree.count("service")) print "Number of BroadcastReceivers: " + str(xmlTree.count("receiver"))
вы можете использовать axml2xml.pl инструмент разработан некоторое время назад в рамках android-random. Он будет генерировать текстовый файл манифеста (AndroidManifest.xml)из двоичного.
Я говорю, что "текстовое", а не "оригинал " потому что, как и многие инструменты обратной инженерии, этот не идеален и результат не будет быть полным. Я предполагаю, что либо это никогда не было особенностью полная или просто не совместимая с прямой передачей (с более новой схемой двоичного кодирования). Какова бы ни была причина,axml2xml.pl инструмент не сможет правильно извлечь все значения атрибутов. Такими атрибутами являются minSdkVersion, targetSdkVersion и в основном все атрибуты, которые ссылаются на ресурсы (например, строки, значки и т. д.), т. е. только имена классов (видов работ, услуг и т. д.) извлекаются правильно.
тем не менее, вы все еще можете найти эти недостающие сведения, запустив aapt инструмент в исходном файле приложения для Android (.apk):
aapt l-a
проверьте следующее проект WPF который правильно декодирует свойства.
АПК-парсер, https://github.com/caoqianli/apk-parser, Легкий осущ для Java без зависимостей для aapt или других binarys, хороша для разбора двоичных файлов XML, и других полная информация.
ApkParser apkParser = new ApkParser(new File(filePath)); // set a locale to translate resource tag into specific strings in language the locale specified, you set locale to Locale.ENGLISH then get apk title 'WeChat' instead of '@string/app_name' for example apkParser.setPreferredLocale(locale); String xml = apkParser.getManifestXml(); System.out.println(xml); String xml2 = apkParser.transBinaryXml(xmlPathInApk); System.out.println(xml2); ApkMeta apkMeta = apkParser.getApkMeta(); System.out.println(apkMeta); Set<Locale> locales = apkParser.getLocales(); for (Locale l : locales) { System.out.println(l); } apkParser.close();
если это полезно, вот версия C++ фрагмента Java, опубликованного Ribo:
struct decompressXML { // decompressXML -- Parse the 'compressed' binary form of Android XML docs // such as for AndroidManifest.xml in .apk files enum { endDocTag = 0x00100101, startTag = 0x00100102, endTag = 0x00100103 }; decompressXML(const BYTE* xml, int cb) { // Compressed XML file/bytes starts with 24x bytes of data, // 9 32 bit words in little endian order (LSB first): // 0th word is 03 00 08 00 // 3rd word SEEMS TO BE: Offset at then of StringTable // 4th word is: Number of strings in string table // WARNING: Sometime I indiscriminently display or refer to word in // little endian storage format, or in integer format (ie MSB first). int numbStrings = LEW(xml, cb, 4*4); // StringIndexTable starts at offset 24x, an array of 32 bit LE offsets // of the length/string data in the StringTable. int sitOff = 0x24; // Offset of start of StringIndexTable // StringTable, each string is represented with a 16 bit little endian // character count, followed by that number of 16 bit (LE) (Unicode) chars. int stOff = sitOff + numbStrings*4; // StringTable follows StrIndexTable // XMLTags, The XML tag tree starts after some unknown content after the // StringTable. There is some unknown data after the StringTable, scan // forward from this point to the flag for the start of an XML start tag. int xmlTagOff = LEW(xml, cb, 3*4); // Start from the offset in the 3rd word. // Scan forward until we find the bytes: 0x02011000(x00100102 in normal int) for (int ii=xmlTagOff; ii<cb-4; ii+=4) { if (LEW(xml, cb, ii) == startTag) { xmlTagOff = ii; break; } } // end of hack, scanning for start of first start tag // XML tags and attributes: // Every XML start and end tag consists of 6 32 bit words: // 0th word: 02011000 for startTag and 03011000 for endTag // 1st word: a flag?, like 38000000 // 2nd word: Line of where this tag appeared in the original source file // 3rd word: FFFFFFFF ?? // 4th word: StringIndex of NameSpace name, or FFFFFFFF for default NS // 5th word: StringIndex of Element Name // (Note: 01011000 in 0th word means end of XML document, endDocTag) // Start tags (not end tags) contain 3 more words: // 6th word: 14001400 meaning?? // 7th word: Number of Attributes that follow this tag(follow word 8th) // 8th word: 00000000 meaning?? // Attributes consist of 5 words: // 0th word: StringIndex of Attribute Name's Namespace, or FFFFFFFF // 1st word: StringIndex of Attribute Name // 2nd word: StringIndex of Attribute Value, or FFFFFFF if ResourceId used // 3rd word: Flags? // 4th word: str ind of attr value again, or ResourceId of value // TMP, dump string table to tr for debugging //tr.addSelect("strings", null); //for (int ii=0; ii<numbStrings; ii++) { // // Length of string starts at StringTable plus offset in StrIndTable // String str = compXmlString(xml, sitOff, stOff, ii); // tr.add(String.valueOf(ii), str); //} //tr.parent(); // Step through the XML tree element tags and attributes int off = xmlTagOff; int indent = 0; int startTagLineNo = -2; while (off < cb) { int tag0 = LEW(xml, cb, off); //int tag1 = LEW(xml, off+1*4); int lineNo = LEW(xml, cb, off+2*4); //int tag3 = LEW(xml, off+3*4); int nameNsSi = LEW(xml, cb, off+4*4); int nameSi = LEW(xml, cb, off+5*4); if (tag0 == startTag) { // XML START TAG int tag6 = LEW(xml, cb, off+6*4); // Expected to be 14001400 int numbAttrs = LEW(xml, cb, off+7*4); // Number of Attributes to follow //int tag8 = LEW(xml, off+8*4); // Expected to be 00000000 off += 9*4; // Skip over 6+3 words of startTag data std::string name = compXmlString(xml, cb, sitOff, stOff, nameSi); //tr.addSelect(name, null); startTagLineNo = lineNo; // Look for the Attributes std::string sb; for (int ii=0; ii<numbAttrs; ii++) { int attrNameNsSi = LEW(xml, cb, off); // AttrName Namespace Str Ind, or FFFFFFFF int attrNameSi = LEW(xml, cb, off+1*4); // AttrName String Index int attrValueSi = LEW(xml, cb, off+2*4); // AttrValue Str Ind, or FFFFFFFF int attrFlags = LEW(xml, cb, off+3*4); int attrResId = LEW(xml, cb, off+4*4); // AttrValue ResourceId or dup AttrValue StrInd off += 5*4; // Skip over the 5 words of an attribute std::string attrName = compXmlString(xml, cb, sitOff, stOff, attrNameSi); std::string attrValue = attrValueSi!=-1 ? compXmlString(xml, cb, sitOff, stOff, attrValueSi) : "resourceID 0x"+toHexString(attrResId); sb.append(" "+attrName+"=\""+attrValue+"\""); //tr.add(attrName, attrValue); } prtIndent(indent, "<"+name+sb+">"); indent++; } else if (tag0 == endTag) { // XML END TAG indent--; off += 6*4; // Skip over 6 words of endTag data std::string name = compXmlString(xml, cb, sitOff, stOff, nameSi); prtIndent(indent, "</"+name+"> (line "+toIntString(startTagLineNo)+"-"+toIntString(lineNo)+")"); //tr.parent(); // Step back up the NobTree } else if (tag0 == endDocTag) { // END OF XML DOC TAG break; } else { prt(" Unrecognized tag code '"+toHexString(tag0) +"' at offset "+toIntString(off)); break; } } // end of while loop scanning tags and attributes of XML tree prt(" end at offset "+off); } // end of decompressXML std::string compXmlString(const BYTE* xml, int cb, int sitOff, int stOff, int strInd) { if (strInd < 0) return std::string(""); int strOff = stOff + LEW(xml, cb, sitOff+strInd*4); return compXmlStringAt(xml, cb, strOff); } void prt(std::string str) { printf("%s", str.c_str()); } void prtIndent(int indent, std::string str) { char spaces[46]; memset(spaces, ' ', sizeof(spaces)); spaces[min(indent*2, sizeof(spaces) - 1)] = 0; prt(spaces); prt(str); prt("\n"); } // compXmlStringAt -- Return the string stored in StringTable format at // offset strOff. This offset points to the 16 bit string length, which // is followed by that number of 16 bit (Unicode) chars. std::string compXmlStringAt(const BYTE* arr, int cb, int strOff) { if (cb < strOff + 2) return std::string(""); int strLen = arr[strOff+1]<<8&0xff00 | arr[strOff]&0xff; char* chars = new char[strLen + 1]; chars[strLen] = 0; for (int ii=0; ii<strLen; ii++) { if (cb < strOff + 2 + ii * 2) { chars[ii] = 0; break; } chars[ii] = arr[strOff+2+ii*2]; } std::string str(chars); free(chars); return str; } // end of compXmlStringAt // LEW -- Return value of a Little Endian 32 bit word from the byte array // at offset off. int LEW(const BYTE* arr, int cb, int off) { return (cb > off + 3) ? ( arr[off+3]<<24&0xff000000 | arr[off+2]<<16&0xff0000 | arr[off+1]<<8&0xff00 | arr[off]&0xFF ) : 0; } // end of LEW std::string toHexString(DWORD attrResId) { char ch[20]; sprintf_s(ch, 20, "%lx", attrResId); return std::string(ch); } std::string toIntString(int i) { char ch[20]; sprintf_s(ch, 20, "%ld", i); return std::string(ch); } };
Если ваш в Python или использовать Androguard, в Androguard характеристика Androaxml будет сделать это преобразование для вас. Эта функция подробно описана в этот блог дополнительные документации и источник.
использование:
$ ./androaxml.py -h Usage: androaxml.py [options] Options: -h, --help show this help message and exit -i INPUT, --input=INPUT filename input (APK or android's binary xml) -o OUTPUT, --output=OUTPUT filename output of the xml -v, --version version of the API $ ./androaxml.py -i yourfile.apk -o output.xml $ ./androaxml.py -i AndroidManifest.xml -o output.xml
для справки вот моя версия кода Рибо. Основное различие заключается в том, что decompressXML() напрямую возвращает строку, которая для моих целей была более подходящим использованием.
Примечание: моей единственной целью в использовании решения Рибо было получить an .Опубликованная версия APK-файла из XML-файла манифеста, и я подтверждаю, что для этой цели он прекрасно работает.
EDIT [2013-03-16]: он прекрасно работает если версия устанавливается как обычный текст, а если он настроен на ссылку на XML-ресурс, например, он будет отображаться как "ресурс 0x1". В этом конкретном случае вам, вероятно, придется связать это решение с другим решением, которое будет получать соответствующую ссылку на строковый ресурс.
/** * Binary XML doc ending Tag */ public static int endDocTag = 0x00100101; /** * Binary XML start Tag */ public static int startTag = 0x00100102; /** * Binary XML end Tag */ public static int endTag = 0x00100103; /** * Reference var for spacing * Used in prtIndent() */ public static String spaces = " "; /** * Parse the 'compressed' binary form of Android XML docs * such as for AndroidManifest.xml in .apk files * Source: http://stackoverflow.com/questions/2097813/how-to-parse-the-androidmanifest-xml-file-inside-an-apk-package/4761689#4761689 * * @param xml Encoded XML content to decompress */ public static String decompressXML(byte[] xml) { StringBuilder resultXml = new StringBuilder(); // Compressed XML file/bytes starts with 24x bytes of data, // 9 32 bit words in little endian order (LSB first): // 0th word is 03 00 08 00 // 3rd word SEEMS TO BE: Offset at then of StringTable // 4th word is: Number of strings in string table // WARNING: Sometime I indiscriminently display or refer to word in // little endian storage format, or in integer format (ie MSB first). int numbStrings = LEW(xml, 4*4); // StringIndexTable starts at offset 24x, an array of 32 bit LE offsets // of the length/string data in the StringTable. int sitOff = 0x24; // Offset of start of StringIndexTable // StringTable, each string is represented with a 16 bit little endian // character count, followed by that number of 16 bit (LE) (Unicode) chars. int stOff = sitOff + numbStrings*4; // StringTable follows StrIndexTable // XMLTags, The XML tag tree starts after some unknown content after the // StringTable. There is some unknown data after the StringTable, scan // forward from this point to the flag for the start of an XML start tag. int xmlTagOff = LEW(xml, 3*4); // Start from the offset in the 3rd word. // Scan forward until we find the bytes: 0x02011000(x00100102 in normal int) for (int ii=xmlTagOff; ii<xml.length-4; ii+=4) { if (LEW(xml, ii) == startTag) { xmlTagOff = ii; break; } } // end of hack, scanning for start of first start tag // XML tags and attributes: // Every XML start and end tag consists of 6 32 bit words: // 0th word: 02011000 for startTag and 03011000 for endTag // 1st word: a flag?, like 38000000 // 2nd word: Line of where this tag appeared in the original source file // 3rd word: FFFFFFFF ?? // 4th word: StringIndex of NameSpace name, or FFFFFFFF for default NS // 5th word: StringIndex of Element Name // (Note: 01011000 in 0th word means end of XML document, endDocTag) // Start tags (not end tags) contain 3 more words: // 6th word: 14001400 meaning?? // 7th word: Number of Attributes that follow this tag(follow word 8th) // 8th word: 00000000 meaning?? // Attributes consist of 5 words: // 0th word: StringIndex of Attribute Name's Namespace, or FFFFFFFF // 1st word: StringIndex of Attribute Name // 2nd word: StringIndex of Attribute Value, or FFFFFFF if ResourceId used // 3rd word: Flags? // 4th word: str ind of attr value again, or ResourceId of value // TMP, dump string table to tr for debugging //tr.addSelect("strings", null); //for (int ii=0; ii<numbStrings; ii++) { // // Length of string starts at StringTable plus offset in StrIndTable // String str = compXmlString(xml, sitOff, stOff, ii); // tr.add(String.valueOf(ii), str); //} //tr.parent(); // Step through the XML tree element tags and attributes int off = xmlTagOff; int indent = 0; int startTagLineNo = -2; while (off < xml.length) { int tag0 = LEW(xml, off); //int tag1 = LEW(xml, off+1*4); int lineNo = LEW(xml, off+2*4); //int tag3 = LEW(xml, off+3*4); int nameNsSi = LEW(xml, off+4*4); int nameSi = LEW(xml, off+5*4); if (tag0 == startTag) { // XML START TAG int tag6 = LEW(xml, off+6*4); // Expected to be 14001400 int numbAttrs = LEW(xml, off+7*4); // Number of Attributes to follow //int tag8 = LEW(xml, off+8*4); // Expected to be 00000000 off += 9*4; // Skip over 6+3 words of startTag data String name = compXmlString(xml, sitOff, stOff, nameSi); //tr.addSelect(name, null); startTagLineNo = lineNo; // Look for the Attributes StringBuffer sb = new StringBuffer(); for (int ii=0; ii<numbAttrs; ii++) { int attrNameNsSi = LEW(xml, off); // AttrName Namespace Str Ind, or FFFFFFFF int attrNameSi = LEW(xml, off+1*4); // AttrName String Index int attrValueSi = LEW(xml, off+2*4); // AttrValue Str Ind, or FFFFFFFF int attrFlags = LEW(xml, off+3*4); int attrResId = LEW(xml, off+4*4); // AttrValue ResourceId or dup AttrValue StrInd off += 5*4; // Skip over the 5 words of an attribute String attrName = compXmlString(xml, sitOff, stOff, attrNameSi); String attrValue = attrValueSi!=-1 ? compXmlString(xml, sitOff, stOff, attrValueSi) : "resourceID 0x"+Integer.toHexString(attrResId); sb.append(" "+attrName+"=\""+attrValue+"\""); //tr.add(attrName, attrValue); } resultXml.append(prtIndent(indent, "<"+name+sb+">")); indent++; } else if (tag0 == endTag) { // XML END TAG indent--; off += 6*4; // Skip over 6 words of endTag data String name = compXmlString(xml, sitOff, stOff, nameSi); resultXml.append(prtIndent(indent, "</"+name+"> (line "+startTagLineNo+"-"+lineNo+")")); //tr.parent(); // Step back up the NobTree } else if (tag0 == endDocTag) { // END OF XML DOC TAG break; } else { Log.e(TAG, " Unrecognized tag code '"+Integer.toHexString(tag0) +"' at offset "+off); break; } } // end of while loop scanning tags and attributes of XML tree Log.i(TAG, " end at offset "+off); return resultXml.toString(); } // end of decompressXML /** * Tool Method for decompressXML(); * Compute binary XML to its string format * Source: Source: http://stackoverflow.com/questions/2097813/how-to-parse-the-androidmanifest-xml-file-inside-an-apk-package/4761689#4761689 * * @param xml Binary-formatted XML * @param sitOff * @param stOff * @param strInd * @return String-formatted XML */ public static String compXmlString(byte[] xml, int sitOff, int stOff, int strInd) { if (strInd < 0) return null; int strOff = stOff + LEW(xml, sitOff+strInd*4); return compXmlStringAt(xml, strOff); } /** * Tool Method for decompressXML(); * Apply indentation * * @param indent Indentation level * @param str String to indent * @return Indented string */ public static String prtIndent(int indent, String str) { return (spaces.substring(0, Math.min(indent*2, spaces.length()))+str); } /** * Tool method for decompressXML() * Return the string stored in StringTable format at * offset strOff. This offset points to the 16 bit string length, which * is followed by that number of 16 bit (Unicode) chars. * * @param arr StringTable array * @param strOff Offset to get string from * @return String from StringTable at offset strOff * */ public static String compXmlStringAt(byte[] arr, int strOff) { int strLen = arr[strOff+1]<<8&0xff00 | arr[strOff]&0xff; byte[] chars = new byte[strLen]; for (int ii=0; ii<strLen; ii++) { chars[ii] = arr[strOff+2+ii*2]; } return new String(chars); // Hack, just use 8 byte chars } // end of compXmlStringAt /** * Return value of a Little Endian 32 bit word from the byte array * at offset off. * * @param arr Byte array with 32 bit word * @param off Offset to get word from * @return Value of Little Endian 32 bit word specified */ public static int LEW(byte[] arr, int off) { return arr[off+3]<<24&0xff000000 | arr[off+2]<<16&0xff0000 | arr[off+1]<<8&0xff00 | arr[off]&0xFF; } // end of LEWнадеюсь, что это может помочь и другим людям.
С последними SDK-Tools, теперь вы можете использовать инструмент под названием apkanalyzer, чтобы распечатать AndroidManifest.xml АПК (а также другие части, такие как ресурсы).
[android sdk]/tools/bin/apkanalyzer manifest print [app.apk]
В Android studio 2.2 вы можете непосредственно анализировать apk. Goto build-анализ apk. Выберите apk, перейдите к androidmanifest.XML. Вы можете увидеть детали androidmanifest.
Я нашел AXMLPrinter2, приложение Java в проекте Android4Me, чтобы нормально работать на AndroidManifest.xml, который у меня был (и печатает XML в хорошо отформатированном виде). http://code.google.com/p/android4me/downloads/detail?name=AXMLPrinter2.jar
Примечание.. он (и код этого ответа от Ribo), похоже, не обрабатывает каждый скомпилированный XML-файл, с которым я столкнулся. Я нашел тот, где строки хранились с одним байтом на символ, а не двухбайтовый формат, который он предполагает.
это может быть полезным
public static int vCodeApk(String path) { PackageManager pm = G.context.getPackageManager(); PackageInfo info = pm.getPackageArchiveInfo(path, 0); return info.versionCode; // Toast.makeText(this, "VersionCode : " + info.versionCode + ", VersionName : " + info.versionName, Toast.LENGTH_LONG).show(); }G-это мой класс применение :
public class G extends Application {
Я уже больше года работаю с кодом Ribo, опубликованным выше, и он хорошо нам послужил. С последними обновлениями (Gradle 3.x) хотя, я больше не был в состоянии разобрать AndroidManifest.xml, я получал index out of bounds ошибки, и в целом он больше не был в состоянии разобрать файл.
обновление: теперь я считаю, что наши проблемы были с обновлением до Gradle 3.x. в этой статье описывается, как AirWatch были проблемы и могут быть исправлены с помощью настройки Gradle, чтобы используйте aapt вместо aapt2 AirWatch кажется несовместимым с Android плагином для Gradle 3.0.0-beta1
в поиске я наткнулся на этот проект с открытым исходным кодом, и он поддерживается, и я смог добраться до точки и прочитать как мои старые APKs, которые я мог ранее разобрать, так и новые APKs, которые логика от Ribo бросила исключения
https://github.com/xgouchet/AXML
из его примера это то, что я делать
zf = new ZipFile(apkFile); //Getting the manifest ZipEntry entry = zf.getEntry("AndroidManifest.xml"); InputStream is = zf.getInputStream(entry); // Read our manifest Document Document manifestDoc = new CompressedXmlParser().parseDOM(is); // Make sure we got a doc, and that it has children if (null != manifestDoc && manifestDoc.getChildNodes().getLength() > 0) { // Node firstNode = manifestDoc.getFirstChild(); // Now get the attributes out of the node NamedNodeMap nodeMap = firstNode.getAttributes(); // Finally to a point where we can read out our values versionName = nodeMap.getNamedItem("android:versionName").getNodeValue(); versionCode = nodeMap.getNamedItem("android:versionCode").getNodeValue(); }
Comments