2011-12-13 65 views
7

我正在開發一個管理Android應用程序包(apk)的應用程序(使用.NET Framework開發),並且該程序必須通過閱讀駐留的ApplicationManifest.xml在apk裏面。從apk中提取Manifest文件是沒有問題的,因爲我可以使用標準的zip工具從apk中提取所有文件。使用.NET閱讀Android應用程序(apk)的ApplicationManifest使用.NET

但是讀取ApplicationManifest是不可能的。我期望它包含原始的XML數據,但它以某種方式加密或編碼。

有沒有一種方法使用.NET Framework的BCL從ApplicationManifest中讀取XML數據?還是有一個.NET Framework的庫來讀取ApplicationManifest?

回答

7

我不會推薦使用上述的apktool,因爲它不是安全的自動解碼 - 我過去使用它的原因和你一樣,但在某些情況下失敗了。

有一些工具,如axml2xml專門用於Android編碼的XML文件或其他工具,如Android Asset Packaging Tool (aapt)。您也可以自己編碼解碼器。

axml2xml是不完美的,所以我會建議運行在您的APK AAPT(包含在Android SDK中),因爲它打印大量的信息,您可以解析:aapt l -a <your.apk>

如果你想實現的代碼自己解析清單,看看ribo的社區維基條目:

// 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成一個byte [處理]:

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 

最後,我會建議解析aapt輸出(你可以與你的應用程序中捆綁的話)。

+0

你好力/彼得,我正在使用上面的代碼來讀取XML文件。現在我想要做的是在我的xml文件中,我有一個屬性名稱,其值由「@ string/abc」指定,我想將它硬編碼爲一些字符串。即;刪除字符串引用。但問題是我得到attrValueSi的值爲-1。我在地圖中添加了鍵,我在地圖中有鍵入,我想把值放在attrValueSi中。我如何繼續? Plz的幫助。 – AndroidGuy

1

Afaik,最簡單(也是最常用)的方法是運行apktool實用程序作爲外部命令,並讓它爲您提取AndroidManifest.xml(如果您需要原始格式),例如調用

apktool d -s -f /path/to/the/downloaded/apk/SomeName.apk /path/to/output 

將解碼AndroidManifest.xml中,並將其放置在你的輸出目錄供您閱讀。它可以在Windows,OSX和Linux上執行,所以你如果不介意你的.NET應用程序中的一些外部依賴關係..