วิธีแยกวิเคราะห์ไฟล์ AndroidManifest.xml ภายในแพ็คเกจ. apk


173

ไฟล์นี้ดูเหมือนจะอยู่ในรูปแบบ XML ไบนารี รูปแบบนี้คืออะไรและสามารถแยกวิเคราะห์แบบเป็นโปรแกรมได้อย่างไร (ตรงข้ามกับการใช้เครื่องมือการถ่ายโอนข้อมูล aapt ใน SDK)

รูปแบบนี้ไบนารีไม่ได้กล่าวถึงในเอกสารที่นี่

หมายเหตุ : ฉันต้องการเข้าถึงข้อมูลนี้จากนอกสภาพแวดล้อม 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
ฉันได้พัฒนาเครื่องมือแยก APK ซึ่งไม่ขึ้นอยู่กับ AAPT มันมีตัวแยกวิเคราะห์ซึ่งสามารถแยกวิเคราะห์ Android Binary XML ใด ๆ content- code.google.com/p/apk-extractor
user542954

คำตอบ:


174

ใช้ android-apktool

มีแอปพลิเคชันที่อ่านไฟล์ apk และถอดรหัส XML เป็นรูปแบบดั้งเดิมเกือบเป็น

การใช้งาน:

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

ตรวจสอบandroid-apktoolสำหรับข้อมูลเพิ่มเติม


11
ตัวอย่างข้อมูลที่จะแสดงความกล้าหาญของมันอาจจะดี:apktool d Gmail.apk && cat Gmail/AndroidManifest.xml
Ehtesh Choudhury

minSdkVersion และพารามิเตอร์รุ่นอื่น ๆ สามารถพบได้ใน Gmail/apktool.yml
daserge

วิธีนี้สามารถใช้ภายในแอพ Android ได้อย่างไร? และสามารถใช้เพื่อรับข้อมูลรายการจาก InputStream (ตัวอย่าง: มีไฟล์ APK อยู่ในไฟล์ zip) หรือไม่
นักพัฒนา android

71

เมธอด 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 / แอพที่สามารถอ่านได้โดยไม่ต้องรูต Evo ของฉันแอพอื่น ๆ อยู่ใน / data / แอพที่ฉันต้องการให้รูทเห็น อาร์กิวเมนต์ 'path' ด้านบนจะเป็นดังนี้: "/system/app/Weather.apk"


12
+1 สำหรับเครื่องมือที่สามารถใช้นอก Android ฉันห่อมันเป็นเครื่องมือ Java บรรทัดคำสั่งที่ใช้งานได้; เห็นpastebin.com/c53DuqMt
noamtm

1
สวัสดี Ribo ฉันใช้โค้ดด้านบนเพื่ออ่านไฟล์ xml ตอนนี้สิ่งที่ฉันต้องการจะทำคือในไฟล์ xml ของฉันฉันมีชื่อคุณลักษณะที่มีการระบุค่าโดย "@ string / abc" & ฉันต้องการรหัสยากกับสตริง เช่น; ลบการอ้างอิงสตริง แต่ปัญหาคือฉันได้รับค่าของ attrValueSi เป็น -1 ฉันกำลังเพิ่มคีย์ในแผนที่ & ฉันมีรายการคีย์ในแผนที่ฉันต้องการใส่ค่าใน attrValueSi ฉันจะดำเนินการอย่างไร กรุณาช่วยด้วย
AndroidGuy

1
@ corey-ogburn เปลี่ยนการใช้งาน compXmlStringAt: `char [] chars = new char [strLen]; สำหรับ (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

1
@GREnvoy เรากำลังเผชิญปัญหาที่นี่ด้วย เรากำลังได้รับข้อยกเว้น 'java.lang.ArrayIndexOutOfBoundsException'
Mad

32

สิ่งที่เกี่ยวกับการใช้เครื่องมือบรรจุสินทรัพย์ Android (aapt) จาก Android SDK เป็นสคริปต์ Python (หรืออะไรก็ตาม)

ผ่าน aapt ( http://elinux.org/Android_aapt ) คุณสามารถดึงข้อมูลเกี่ยวกับแพคเกจ. apkและข้อมูลเกี่ยวกับไฟล์AndroidManifest.xml โดยเฉพาะอย่างยิ่งคุณสามารถแยกค่าของแต่ละองค์ประกอบของแพ็คเกจ. apkผ่านคำสั่งย่อย'dump' ตัวอย่างเช่นคุณสามารถแยกสิทธิ์ผู้ใช้ในไฟล์AndroidManifest.xmlภายในแพ็คเกจ. apkด้วยวิธีนี้:

$ aapt dump permissions package.apk

ที่ไหน package.apkเป็นแพคเกจ. apkของคุณ

นอกจากนี้คุณสามารถใช้คำสั่ง Unix pipe เพื่อล้างเอาต์พุต ตัวอย่างเช่น:

$ aapt dump permissions package.apk | sed 1d | awk '{ print $NF }'

นี่คือสคริปต์ Python สำหรับโปรแกรมนั้น:

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

เครื่องมือนี้มีอยู่เสมอใน Android Roms หรือไม่? มันเป็นสิ่งที่สร้างขึ้นภายในหรือไม่?
นักพัฒนา android

วิธีที่ดีกว่าถ้าคุณถามฉัน :) ฉันมีปัญหากับ apktool และ AXMLPrinter2: บางครั้งพวกเขาก็โยนข้อยกเว้น ฯลฯ aapt ทำงานได้ทุกครั้งและมีความหลากหลายมากกว่า ไม่ต้องพูดถึงว่าเป็นเครื่องมืออย่างเป็นทางการ
Albus Dumbledore

16

คุณสามารถใช้เครื่องมือaxml2xml.plที่ได้รับการพัฒนาเมื่อไม่นานมานี้ภายในโครงการสุ่มด้วย Android มันจะสร้างไฟล์รายการข้อความ (AndroidManifest.xml) จากไบนารีหนึ่ง

ฉันบอกว่า " ต้นฉบับเดิม " และไม่ " เดิม " เพราะชอบเครื่องมือวิศวกรรมย้อนกลับหลายอันนี้ไม่สมบูรณ์และผลที่ตามมาจะไม่เสร็จสมบูรณ์ ฉันคิดว่ามันไม่เคยมีคุณสมบัติสมบูรณ์หรือเพียงแค่ไม่เข้ากันได้ไปข้างหน้า (ด้วยรูปแบบการเข้ารหัสไบนารีใหม่) ไม่ว่าด้วยเหตุผลใดaxml2xml.plเครื่องมือจะไม่สามารถแยกค่าแอตทริบิวต์ทั้งหมดได้อย่างถูกต้อง คุณลักษณะดังกล่าวคือ minSdkVersion, targetSdkVersion และโดยทั่วไปคุณลักษณะทั้งหมดที่อ้างอิงทรัพยากร (เช่นสตริง, ไอคอน, ฯลฯ ) กล่าวคือเฉพาะชื่อคลาส (ของกิจกรรมบริการ ฯลฯ ) จะถูกแยกออกอย่างถูกต้อง

อย่างไรก็ตามคุณยังสามารถค้นหาข้อมูลที่ขาดหายไปเหล่านี้ได้โดยการเรียกใช้เครื่องมือaaptในไฟล์แอป Android ต้นฉบับ ( .apk ):

aapt l -a <someapp.apk>


1
ขอบคุณ @Shonzilla ฉันต้องการชื่อแพคเกจและข้อมูลรหัสเวอร์ชั่น aapt ทำงานได้ ขณะที่ฉันทำงานกับ LAMP ฉันจะรันคำสั่ง aapt ใน PHP และประมวลผลเอาต์พุตด้วย PHP
ต้มตุ๋น

โซลูชัน Java / Kotlin ใดที่สามารถใช้กับ Android ได้
นักพัฒนา android

12

ด้วย SDK-Tools ล่าสุดคุณสามารถใช้เครื่องมือที่เรียกว่า apkanalyzer เพื่อพิมพ์ AndroidManifest.xml ของ APK (รวมถึงส่วนอื่น ๆ เช่นทรัพยากร)

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

apkanalyzer


ขอบคุณมาก! ฉันได้ทำสิ่งนี้มาหลายวันแล้วและไม่ต้องการโซลูชันของบุคคลที่สามที่ต้องใช้ python หรือ perl หรือ Java JARs หรือสิ่งที่คุณมี
Jeremy

นี่คือคำตอบที่ดีที่สุดสำหรับแนวนอนของเครื่องมือในปัจจุบัน
greg7gkb

11

ตรวจสอบโครงการ WPFต่อไปนี้ซึ่งถอดรหัสคุณสมบัติอย่างถูกต้อง


1
+1 สำหรับสิ่งนี้ขอบคุณ !!! สำหรับนักพัฒนา C # ฉันขอแนะนำอย่างนี้ ช่วยฉันได้มากเวลา =) มันรั้งฉันไว้ครู่หนึ่งเพราะฉันต้องเรียกใช้ "aapt" เพื่อดึงหมายเลขเวอร์ชั่นและชื่อแพ็คเกจ (ซึ่งเป็นไปไม่ได้เนื่องจากสถานการณ์ของฉันอยู่ในสภาพแวดล้อมของเว็บและผู้ใช้ต้องการความคิดเห็นหลังจากเรียกทั้งสอง ชื่อแพ็คเกจและหมายเลขเวอร์ชั่น)
Jonathan Liono

คุณสามารถลบ PresentationCore ได้อย่างง่ายดายจริง ๆ มันถูกใช้สำหรับคลาสสีเท่านั้น คุณสามารถสร้างขึ้นเองหรือใช้ System.Drawing
Alexis

มีวิธีแก้ปัญหาเช่นนี้ แต่เป็นวิธีที่ใช้ได้ในแอพ Android หรือไม่
นักพัฒนา android

11

apk-parser, https://github.com/caoqianli/apk-parser , impl ที่มีน้ำหนักเบาสำหรับ java โดยไม่ต้องพึ่งพา aapt หรือไบนารีอื่น ๆ เหมาะสำหรับการแยกวิเคราะห์ไฟล์ xml ไบนารีและ apk infos อื่น ๆ

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

ไม่ผ่านการทดสอบ มันควรจะทำงานได้ แต่มีคนรายงานปัญหาเกี่ยวกับ android L.
Liu Dong

ฉันเห็น. คุณจะได้รับตัวกรองเจตนาด้วยวิธีนี้หรือไม่?
นักพัฒนา android

Intent-filter สามารถรับได้โดยการแจงไฟล์ 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

ในกรณีที่มีประโยชน์ต่อไปนี้เป็นตัวอย่างโค้ด 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);
    }
};

สองข้อบกพร่อง: ใน compXMLStringAt: charsจะถูกจัดสรรโดยถ่านใหม่ [] แต่แล้ว free'd delete[] chars;แทนการที่ถูกต้อง ในตอนท้ายของ decompressXML ctor มันจะต้องเป็นprt(" end at offset "+toIntString(off));อย่างอื่นมิฉะนั้นจะใช้เลขคณิตของตัวชี้ ...
smilingthax

3

สำหรับการอ้างอิงนี่คือรหัสของ Ribo ของฉัน ข้อแตกต่างที่สำคัญคือ decompressXML () จะคืนค่าสตริงโดยตรงซึ่งสำหรับวัตถุประสงค์ของฉันคือการใช้ที่เหมาะสมกว่า

หมายเหตุ: วัตถุประสงค์เพียงอย่างเดียวของฉันในการใช้โซลูชันของ Ribo คือการดึงรุ่นที่เผยแพร่ของไฟล์. APK จากไฟล์ Manifest XML และฉันยืนยันว่าสำหรับวัตถุประสงค์นี้ใช้งานได้อย่างสวยงาม

แก้ไข [2013-03-16]: มันทำงานได้อย่างสวยงามถ้ารุ่นถูกตั้งค่าเป็นข้อความธรรมดา แต่ถ้าตั้งไว้เพื่ออ้างถึง Resource 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

หวังว่ามันจะช่วยคนอื่นได้เช่นกัน


หากไฟล์ Manifest ของ APK อ้างอิงถึง String ทรัพยากร xml สำหรับเวอร์ชั่นรหัสนี้ของคุณจะล้มเหลว ในกรณีของฉันฉันดาวน์โหลด APK จากgithub.com/stephanenicolas/RoboDemo/robodemo-sample-1.0.1.apk/…และรันโค้ดของคุณ แทนที่จะพิมพ์เวอร์ชันจะพิมพ์ Resource ID เช่น "resourceID 0x1" ซึ่งไม่มีประโยชน์และการค้นหารหัสทรัพยากรนั้นเราต้องการโปรแกรมอื่นที่สามารถค้นหาไฟล์ทรัพยากรนั้นและถอดรหัสมัน
Yahya Arshad

เรื่องนี้ทำให้รู้สึกรวม ตามจริงแล้วมันไม่ได้เกิดขึ้นกับฉันเลยว่าเวอร์ชันอาจถูกอ้างถึงใน Resource XML แทนข้อความธรรมดา ฉันจะแก้ไขโพสต์ของฉันเพื่อให้ครอบคลุมถึงความพิเศษนั้น
Mathieu

คุณช่วยบอกฉันทีว่าฉันสามารถถอดรหัส String.xml และค้นหา id ทรัพยากรเฉพาะนั้นได้อย่างไร ฉันต้องการใช้มันในโครงการมหาวิทยาลัยของฉัน เพื่อสร้างระบบจัดการการสร้าง
Yahya Arshad

@ Cheeta บอกตามตรงฉันไม่รู้อะไรมากกว่านี้ ฉันแค่เอาโค้ดของ Ribo และแก้ไขมันตามความต้องการเฉพาะของฉันจากนั้นแชร์มันในกรณีที่คนอื่นได้ประโยชน์ ฉันขอแนะนำให้หาวิธีแก้ปัญหาเฉพาะเพื่อดึงข้อมูลทรัพยากรของสตริงจาก. APK และจับคู่กับวิธีที่ฉันเผยแพร่ที่นี่ โชคดี!
Mathieu

3

ใน Android studio 2.2 คุณสามารถวิเคราะห์ apk ได้โดยตรง ไปที่ build- วิเคราะห์ 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
พัฒนา android

0

ฉันพบ AXMLPrinter2 ซึ่งเป็นแอป Java ที่โครงการ Android4Me ทำงานได้ดีบน AndroidManifest.xml ที่ฉันมี (และพิมพ์ XML ออกมาในรูปแบบที่เหมาะสม) http://code.google.com/p/android4me/downloads/detail?name=AXMLPrinter2.jar

One note .. it (และรหัสของคำตอบนี้จาก Ribo) ไม่ปรากฏว่าจัดการไฟล์ XML ที่คอมไพล์ทุกไฟล์ที่ฉันเจอ ฉันพบหนึ่งที่สตริงถูกเก็บไว้กับหนึ่งไบต์ต่อตัวอักษรมากกว่ารูปแบบไบต์คู่ที่มันถือว่า


ฉันไม่สามารถเข้าถึงลิงก์นี้ ทางเลือกใด ๆ
นักพัฒนา android

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 ที่โพสต์ไว้ด้านบนมานานกว่าหนึ่งปีและมันให้บริการเราดี ด้วยการอัปเดตล่าสุด (Gradle 3.x) แม้ว่าฉันจะไม่สามารถแยกวิเคราะห์ AndroidManifest.xml อีกต่อไปฉันได้รับดัชนีจากข้อผิดพลาดที่ไม่มีขอบเขตและโดยทั่วไปจะไม่สามารถแยกวิเคราะห์ไฟล์ได้อีก

อัปเดต:ตอนนี้ฉันเชื่อว่าปัญหาของเราเกิดจากการอัปเกรดเป็น Gradle 3.x บทความนี้อธิบายว่า AirWatch มีปัญหาอย่างไรและสามารถแก้ไขได้โดยใช้การตั้งค่า Gradle เพื่อใช้ aapt แทน aapt2 AirWatch ดูเหมือนจะไม่เข้ากันกับปลั๊กอิน Android สำหรับ Gradle 3.0.0-beta1

ในการค้นหารอบ ๆ ฉันเจอโครงการโอเพ่นซอร์สนี้และมันก็ยังคงอยู่และฉันก็สามารถไปถึงจุดนั้นและอ่านทั้ง APK เก่าของฉันที่ฉันสามารถแยกวิเคราะห์ก่อนหน้านี้และ APK ใหม่ที่ตรรกะจาก 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();
     }

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
อ้างถึง @Alther คำตอบ
เซเว่น
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.