.apk 패키지 내에서 AndroidManifest.xml 파일을 구문 분석하는 방법


173

이 파일은 이진 XML 형식 인 것 같습니다. 이 형식은 무엇이며 프로그래밍 방식으로 구문 분석 할 수있는 방법은 무엇입니까 (SDK의 aapt dump 도구를 사용하는 것과 대조적으로)?

이 바이너리 형식은 여기 문서 에서 다루지 않습니다 .

참고 : Android 환경 외부에서, 바람직하게는 Java 에서이 정보에 액세스하고 싶습니다.


2
특정 사용 사례는 무엇입니까? android.content.pm.PackageManager.queryXX메서드를 사용하여 자신의 앱에있는 많은 매니페스트 정보를 쿼리 할 수 ​​있습니다 (docs : developer.android.com/reference/android/content/pm/… ).
Roman Nurik

2
Android 환경이 아닙니다. .apk 파일을 읽고 AndroidManifest.xml을 추출하여 XML로 구문 분석하고 싶습니다.
jnorris

2
AAPT에 의존하지 않는 APK 추출기를 개발했습니다. 그것은 어떤 안드로이드 바이너리 XML 컨텐츠 - 구문 분석 할 수 파서 포함 code.google.com/p/apk-extractor
user542954

답변:


174

android-apktool 사용

apk 파일을 읽고 XML을 거의 원래 형식으로 디코딩하는 응용 프로그램이 있습니다.

용법:

apktool d Gmail.apk && cat Gmail/AndroidManifest.xml

확인 안드로이드 - apktool을 자세한 내용은


11
apktool d Gmail.apk && cat Gmail/AndroidManifest.xml
훌륭함

minSdkVersion다른 버전 매개 변수도 있습니다 :Gmail/apktool.yml
daserge

Android 앱에서 어떻게 정확하게 사용할 수 있습니까? 그리고 InputStream에서 매니페스트 데이터를 얻는 데 사용할 수 있습니까 (예 : zip 파일 안에 APK 파일이 있습니다)?
안드로이드 개발자

71

Android에서 실행되는이 Java 메소드는 .apk 패키지에있는 AndroidManifest.xml 파일의 이진 형식을 문서화합니다. 두 번째 코드 상자는 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에 저장되며 루트없이 Evo를 읽을 수 있으며 다른 앱은 / data / app에 있으며 루트를 확인해야합니다. 위의 'path'인수는 "/system/app/Weather.apk"와 같습니다.


12
Android 외부에서 사용할 수있는 도구의 경우 +1 작업 명령 줄 Java 도구로 래핑했습니다. pastebin.com/c53DuqMt를 참조하십시오 .
noamtm

1
안녕하세요 Ribo, 위의 코드를 사용하여 xml 파일을 읽습니다. 이제 내가하고 싶은 것은 내 XML 파일에 있으며, 값이 "@ string / abc"로 지정된 속성 이름을 가지고 있으며 문자열로 하드 코딩하고 싶습니다. 즉; 문자열 참조를 제거하십시오. 그러나 문제는 attrValueSi의 값을 -1로 얻는 것입니다. 맵에 키를 추가하고 맵에 키 항목이 있으며 attrValueSi에 값을 넣고 싶습니다. 어떻게 진행합니까? PLZ 도움.
AndroidGuy

1
@ corey-ogburn, compXmlStringAt의 구현 변경 :`char [] chars = new char [strLen]; for (int ii = 0; ii <strLen; ii ++) {chars [ii] = (char) (((arr [strOff + 2 + ii * 2 + 1] & 0x00FF) << 8) + (arr [strOff + 2 + ii * 2] & 0x00FF)); }`
Anton-M

1
최근에 이것을 시도한 사람이 있습니까? 우리는 Android Studio 3.0.1을 사용하고 최근에 cmake로 전환했는데 더 이상 작동하지 않습니다. AS인지 또는 빌드 프로세스 변경인지 확인해야합니다.
GR Envoy

1
@GREnvoy 우리는 여기서도 문제에 직면하고 있습니다. 'java.lang.ArrayIndexOutOfBoundsException'예외가 발생 함
Mad

32

무엇 사용에 대한 안드로이드 자산 패키징 도구 파이썬 (또는 무엇이든) 스크립트에, 안드로이드 SDK에서, (aapt에를)?

aapt ( http://elinux.org/Android_aapt )를 통해 실제로 .apk 패키지 및 AndroidManifest.xml 파일 에 대한 정보를 검색 할 수 있습니다 . 특히 'dump' 하위 명령을 통해 .apk 패키지 의 개별 요소 값을 추출 할 수 있습니다 . 예를 들어 .apk 패키지 내의 AndroidManifest.xml 파일 에서 사용자 권한을 다음 과 같이 추출 할 수 있습니다 .

$ aapt dump permissions package.apk

여기서 package.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의 다른 정보 (예 : package , app name 등)를 추출 할 수 있습니다 .

#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 ""

대신 전체 AndroidManifest XML 트리를 구문 분석하려면 xmltree 명령을 사용하여 비슷한 방식으로 수행 할 수 있습니다 .

aapt dump xmltree package.apk AndroidManifest.xml

이전과 같이 파이썬 사용하기 :

#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"))

이 도구는 항상 Android Roms에 있습니까? 항상 내장되어 있습니까?
안드로이드 개발자

당신이 나에게 묻는다면 더 나은. :) apktool 및 AXMLPrinter2에 문제가 있습니다. 때때로 예외가 발생합니다. 공식 도구 라는 것은 말할 것도 없습니다 .
Albus Dumbledore

16

android-random 프로젝트 내에서 얼마 전에 개발 된 axml2xml.pl 도구 를 사용할 수 있습니다 . 바이너리에서 텍스트 매니페스트 파일 (AndroidManifest.xml)을 생성합니다.

많은 리버스 엔지니어링 도구와 같이 완벽 하지 않고 결과 완전하지 않기 때문에 " 원본 "이 아닌 " 텍스트 " 라고 말하고 있습니다 . 나는 그것이 완전한 기능을 갖지 못했거나 단순히 순방향 호환되지 않는 것으로 생각합니다 (새로운 바이너리 인코딩 체계 사용). 이유가 무엇이든, axml2xml.pl 도구는 모든 속성 값을 올바르게 추출 할 수 없습니다. 이러한 속성은 minSdkVersion, targetSdkVersion이며 기본적으로 문자열, 아이콘 등과 같은 리소스를 참조하는 모든 속성, 즉 클래스 이름 (활동, 서비스 등) 만 올바르게 추출됩니다.

그러나 원본 Android 앱 파일 ( .apk ) 에서 aapt 도구를 실행하면 이러한 누락 된 정보를 계속 찾을 수 있습니다 .

aapt l -a <someapp.apk>


1
@Shonzilla에게 감사합니다. 패키지 이름과 버전 코드 정보가 필요합니다. LAMP로 작업하면서 PHP에서 aapt 명령을 실행하고 PHP로 출력을 처리합니다.
hongster

Android에서 작동하는 Java / Kotlin 솔루션이 있습니까?
안드로이드 개발자

12

최신 SDK-Tools를 사용하면 apkanalyzer라는 도구를 사용하여 APK의 AndroidManifest.xml (및 리소스와 같은 다른 부분)을 인쇄 할 수 있습니다.

[android sdk]/tools/bin/apkanalyzer manifest print [app.apk]

apkanalyzer


정말 고맙습니다! 나는 이것을 며칠 동안 인터넷 검색을 해 왔으며 파이썬이나 펄 또는 Java JAR 또는 의존하는 타사 솔루션을 원하지 않았습니다.
Jeremy

이것은 현재 도구 환경에서 가장 좋은 대답입니다.
greg7gkb

11

속성을 올바르게 디코딩하는 다음 WPF 프로젝트 를 확인하십시오 .


1
+1 감사합니다 !!! C # 개발자에게는 꼭 추천합니다. 많은 시간을 절약했습니다. =) 버전 번호와 패키지 이름을 검색하기 위해 "aapt"를 실행해야했기 때문에 잠시 동안 시간이 절약되었습니다. 패키지 이름 및 버전 번호).
Jonathan Liono

실제로 PresentationCore 종속성을 쉽게 제거 할 수 있으며 Color 클래스에만 사용됩니다. 직접 만들거나 System.Drawing을 사용할 수 있습니다.
Alexis

이와 같은 솔루션이 있지만 Android 앱에서 작동하는 솔루션이 있습니까?
안드로이드 개발자

11

apk-parser, https://github.com/caoqianli/apk-parser 는 aapt 또는 기타 이진에 대한 종속성이없는 java 용 경량 impl이며 이진 XML 파일 및 기타 apk 정보를 구문 분석하는 데 적합합니다.

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();

검증되지 않은. 작동해야하지만 누군가 안드로이드 L에 문제를보고합니다.
Liu Dong

내가 참조. 인 텐트 필터를 이런 식으로 얻을 수 있습니까?
안드로이드 개발자

인 텐트 필터는 매니페스트 XML 파일을 구문 분석하여 얻을 수 있으며 직접적인 방법은 없습니다.
Liu Dong

"Pure java", 매우 불행한 문구
Glenn Maynard

7

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

6

유용한 경우 Ribo가 게시 한 Java 스 니펫의 C ++ 버전은 다음과 같습니다.

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);
    }
};

두 가지 버그 : compXMLStringAt에서 : chars새로운 char []에 의해 할당되지만 올바른 대신에 해제 delete[] chars;됩니다. decompressXML ctor의 끝에서이어야한다 prt(" end at offset "+toIntString(off));. 그렇지 않으면 포인터 산술이 사용된다.
smilingthax

3

여기에 참조 용으로 Ribo 코드가 있습니다. 가장 큰 차이점은 decompressXML ()이 String을 직접 반환한다는 것입니다.

참고 : Ribo 솔루션을 사용하는 유일한 목적은 Manifest XML 파일에서 .APK 파일의 게시 된 버전을 가져 오는 것이 었으며이 목적을 위해 아름답게 작동한다는 것을 확인했습니다.

편집은 [2013년 3월 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

다른 사람들도 도울 수 있기를 바랍니다.


APK의 매니페스트 파일이 Version의 String resource xml을 참조하면이 코드가 실패합니다. 제 경우에는 github.com/stephanenicolas/RoboDemo/robodemo-sample-1.0.1.apk/… 에서 APK를 다운로드 하여 코드를 실행했습니다. 버전을 인쇄하는 대신 자원 ID를 인쇄합니다. 즉, "resourceID 0x1"은 쓸모가 없으며 해당 자원 ID를 찾으려면 해당 자원 파일을 찾아서 디 컴파일 할 수있는 다른 프로그램이 필요합니다.
Yahya Arshad

이것은 완전히 이해됩니다. 솔직히 말해서, 버전이 일반 텍스트 대신 Resource XML에서 참조 될 수 있다는 것은 나에게 발생하지 않았습니다. 해당 내용을 다루기 위해 게시물을 편집하겠습니다.
Mathieu

String.xml을 디코딩하고 특정 리소스 ID를 찾는 방법을 알려주십시오. 대학 프로젝트에서 이것을 사용하고 싶습니다. 빌드 관리 시스템 구축
Yahya Arshad

@Cheeta 정직하게 당신보다 더 몰라요. Ribo의 코드를 가져 와서 특정 요구 사항에 맞게 수정 한 다음 다른 사람이 혜택을 볼 수 있도록 공유했습니다. .APK에서 String Resources를 검색하는 데 적합한 솔루션을 찾고 여기에 게시 한 솔루션과 결합하는 것이 좋습니다. 행운을 빕니다!
Mathieu

3

Android Studio 2.2에서는 APK를 직접 분석 할 수 있습니다. 고토 빌드-APK 분석. APK를 선택하고 androidmanifest.xml로 이동하십시오. androidmanifest의 세부 사항을 볼 수 있습니다.


3

@Mathieu Kotlin 버전은 다음과 같습니다.

fun main(args : Array<String>) {
    val fileName = "app.apk"
    ZipFile(fileName).use { zip ->
        zip.entries().asSequence().forEach { entry ->
            if(entry.name == "AndroidManifest.xml") {
                zip.getInputStream(entry).use { input ->
                    val xml = decompressXML(input.readBytes())
                    //TODO: parse the XML
                    println(xml)

                }
            }
        }
    }
}

    /**
     * Binary XML doc ending Tag
     */
    var endDocTag = 0x00100101

    /**
     * Binary XML start Tag
     */
    var startTag = 0x00100102

    /**
     * Binary XML end Tag
     */
    var endTag = 0x00100103


    /**
     * Reference var for spacing
     * Used in prtIndent()
     */
    var 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
     */
    fun decompressXML(xml: ByteArray): String {

        val resultXml = 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).
        val 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.
        val 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.
        val 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.
        var 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)
        run {
            var ii = xmlTagOff
            while (ii < xml.size - 4) {
                if (LEW(xml, ii) == startTag) {
                    xmlTagOff = ii
                    break
                }
                ii += 4
            }
        } // 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
        var off = xmlTagOff
        var indent = 0
        var startTagLineNo = -2
        while (off < xml.size) {
            val tag0 = LEW(xml, off)
            //int tag1 = LEW(xml, off+1*4);
            val lineNo = LEW(xml, off + 2 * 4)
            //int tag3 = LEW(xml, off+3*4);
            val nameNsSi = LEW(xml, off + 4 * 4)
            val nameSi = LEW(xml, off + 5 * 4)

            if (tag0 == startTag) { // XML START TAG
                val tag6 = LEW(xml, off + 6 * 4)  // Expected to be 14001400
                val 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
                val name = compXmlString(xml, sitOff, stOff, nameSi)
                //tr.addSelect(name, null);
                startTagLineNo = lineNo

                // Look for the Attributes
                val sb = StringBuffer()
                for (ii in 0 until numbAttrs) {
                    val attrNameNsSi = LEW(xml, off)  // AttrName Namespace Str Ind, or FFFFFFFF
                    val attrNameSi = LEW(xml, off + 1 * 4)  // AttrName String Index
                    val attrValueSi = LEW(xml, off + 2 * 4) // AttrValue Str Ind, or FFFFFFFF
                    val attrFlags = LEW(xml, off + 3 * 4)
                    val attrResId = LEW(xml, off + 4 * 4)  // AttrValue ResourceId or dup AttrValue StrInd
                    off += 5 * 4  // Skip over the 5 words of an attribute

                    val attrName = compXmlString(xml, sitOff, stOff, attrNameSi)
                    val attrValue = if (attrValueSi != -1)
                        compXmlString(xml, sitOff, stOff, attrValueSi)
                    else
                        "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
                val 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 {
                        println("  Unrecognized tag code '" + Integer.toHexString(tag0)
                            + "' at offset " + off
                )
                break
            }
        } // end of while loop scanning tags and attributes of XML tree
        println("    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
     */
    fun compXmlString(xml: ByteArray, sitOff: Int, stOff: Int, strInd: Int): String? {
        if (strInd < 0) return null
        val 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
     */
    fun prtIndent(indent: Int, str: String): String {

        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
     */
    fun compXmlStringAt(arr: ByteArray, strOff: Int): String {
        val strLen = (arr[strOff + 1] shl (8 and 0xff00)) or (arr[strOff].toInt() and 0xff)
        val chars = ByteArray(strLen)
        for (ii in 0 until strLen) {
            chars[ii] = arr[strOff + 2 + ii * 2]
        }
        return 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
     */
    fun LEW(arr: ByteArray, off: Int): Int {
        return (arr[off + 3] shl 24 and -0x1000000 or ((arr[off + 2] shl 16) and 0xff0000)
                or (arr[off + 1] shl 8 and 0xff00) or (arr[off].toInt() and 0xFF))
    } // end of LEW

    private infix fun Byte.shl(i: Int): Int = (this.toInt() shl i)
    private infix fun Int.shl(i: Int): Int = (this shl i)

이것은 위의 답변의 kotlin 버전입니다.


슬프게도 이것은 드문 경우에 문제가있는 것으로 보입니다. 여기를보십시오 : stackoverflow.com/q/60565299/878126
안드로이드 개발자

0

Android4Me 프로젝트의 Java 응용 프로그램 인 AXMLPrinter2는 내가 가지고있는 AndroidManifest.xml에서 잘 작동하고 XML을 형식이 좋은 방식으로 인쇄합니다. http://code.google.com/p/android4me/downloads/detail?name=AXMLPrinter2.jar

참고 사항 .. (그리고 Ribo 의이 답변의 코드)는 내가 접한 모든 컴파일 된 XML 파일을 처리하는 것으로 보이지 않습니다. 문자열이 가정하는 2 바이트 형식이 아닌 문자 당 1 바이트로 저장된 곳을 찾았습니다.


이 링크에 접근 할 수 없습니다. 대안이 있습니까?
안드로이드 개발자

0

도움이 될 수 있습니다

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 {

0

나는 위에 게시 된 Ribo 코드를 1 년 넘게 실행 해 왔으며 우리에게 잘 제공되었습니다. 최근 업데이트 (Gradle 3.x)를 사용하면 더 이상 AndroidManifest.xml을 구문 분석 할 수 없었고 색인에서 범위를 벗어난 오류가 발생했으며 일반적으로 더 이상 파일을 구문 분석 할 수 없었습니다.

업데이트 : 이제 우리의 문제는 Gradle 3.x로 업그레이드하는 데 문제가 있다고 생각합니다. 이 문서에서는 AirWatch에 문제가 있었으며 Gradle 설정을 사용하여 aapt2 대신 aapt를 사용하여 해결할 수있는 방법에 대해 설명합니다. Gradle 3.0.0-beta1 용 Android 플러그인과 호환되지 않는 것 같습니다

주변에서 검색 할 때이 오픈 소스 프로젝트를 발견했으며 유지 관리 중이며 이전에 구문 분석 할 수있는 이전 APK와 Ribo의 논리가 예외를 던진 새로운 APK를 모두 읽을 수있었습니다.

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();
     }

0

apkanalyzer가 도움이 될 것입니다

@echo off

::##############################################################################
::##
::##  apkanalyzer start up script for Windows
::##
::##  converted by ewwink
::##
::##############################################################################

::Attempt to set APP_HOME

SET SAVED=%cd%
SET APP_HOME=C:\android\sdk\tools
SET APP_NAME="apkanalyzer"

::Add default JVM options here. You can also use JAVA_OPTS and APKANALYZER_OPTS to pass JVM options to this script.
SET DEFAULT_JVM_OPTS=-Dcom.android.sdklib.toolsdir=%APP_HOME%

SET CLASSPATH=%APP_HOME%\lib\dvlib-26.0.0-dev.jar;%APP_HOME%\lib\util-2.2.1.jar;%APP_HOME%\lib\jimfs-1.1.jar;%APP_HOME%\lib\annotations-13.0.jar;%APP_HOME%\lib\ddmlib-26.0.0-dev.jar;%APP_HOME%\lib\repository-26.0.0-dev.jar;%APP_HOME%\lib\sdk-common-26.0.0-dev.jar;%APP_HOME%\lib\kotlin-stdlib-1.1.3-2.jar;%APP_HOME%\lib\protobuf-java-3.0.0.jar;%APP_HOME%\lib\apkanalyzer-cli.jar;%APP_HOME%\lib\gson-2.3.jar;%APP_HOME%\lib\httpcore-4.2.5.jar;%APP_HOME%\lib\dexlib2-2.2.1.jar;%APP_HOME%\lib\commons-compress-1.12.jar;%APP_HOME%\lib\generator.jar;%APP_HOME%\lib\error_prone_annotations-2.0.18.jar;%APP_HOME%\lib\commons-codec-1.6.jar;%APP_HOME%\lib\kxml2-2.3.0.jar;%APP_HOME%\lib\httpmime-4.1.jar;%APP_HOME%\lib\annotations-12.0.jar;%APP_HOME%\lib\bcpkix-jdk15on-1.56.jar;%APP_HOME%\lib\jsr305-3.0.0.jar;%APP_HOME%\lib\explainer.jar;%APP_HOME%\lib\builder-model-3.0.0-dev.jar;%APP_HOME%\lib\baksmali-2.2.1.jar;%APP_HOME%\lib\j2objc-annotations-1.1.jar;%APP_HOME%\lib\layoutlib-api-26.0.0-dev.jar;%APP_HOME%\lib\jcommander-1.64.jar;%APP_HOME%\lib\commons-logging-1.1.1.jar;%APP_HOME%\lib\annotations-26.0.0-dev.jar;%APP_HOME%\lib\builder-test-api-3.0.0-dev.jar;%APP_HOME%\lib\animal-sniffer-annotations-1.14.jar;%APP_HOME%\lib\bcprov-jdk15on-1.56.jar;%APP_HOME%\lib\httpclient-4.2.6.jar;%APP_HOME%\lib\common-26.0.0-dev.jar;%APP_HOME%\lib\jopt-simple-4.9.jar;%APP_HOME%\lib\sdklib-26.0.0-dev.jar;%APP_HOME%\lib\apkanalyzer.jar;%APP_HOME%\lib\shared.jar;%APP_HOME%\lib\binary-resources.jar;%APP_HOME%\lib\guava-22.0.jar

SET APP_ARGS=%*
::Collect all arguments for the java command, following the shell quoting and substitution rules
SET APKANALYZER_OPTS=%DEFAULT_JVM_OPTS% -classpath %CLASSPATH% com.android.tools.apk.analyzer.ApkAnalyzerCli %APP_ARGS%

::Determine the Java command to use to start the JVM.
SET JAVACMD="java"
where %JAVACMD% >nul 2>nul
if %errorlevel%==1 (
  echo ERROR: 'java' command could be found in your PATH.
  echo Please set the 'java' variable in your environment to match the
  echo location of your Java installation.
  echo.
  exit /b 0
)

:: execute apkanalyzer

%JAVACMD% %APKANALYZER_OPTS%

원래 게시물 https://stackoverflow.com/a/51905063/1383521


1
@ 다른 답변 참조
Seven
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.