How to parse the AndroidManifest.xml file inside an .apk package

前端 未结 16 1401
暖寄归人
暖寄归人 2020-11-22 10:32

This file appears to be in a binary XML format. What is this format and how can it be parsed programmatically (as opposed to using the aapt dump tool in the SDK)?

16条回答
  •  忘掉有多难
    2020-11-22 10:45

    This Java method, that runs on an Android, documents (what I've been able to interpret about) the binary format of the AndroidManifest.xml file in the .apk package. The second code box shows how to call decompressXML and how to load the byte[] from the app package file on the device. (There are fields whose purpose I don't understand, if you know what they mean, tell me, I'll update the info.)

    // 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");
        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, "  (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

    This method reads the AndroidManifest into a byte[] for processing:

    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
    

    Most apps are stored in /system/app which is readable without root my Evo, other apps are in /data/app which I needed root to see. The 'path' argument above would be something like: "/system/app/Weather.apk"

提交回复
热议问题