AndroidManifest Ambiguity方案原理及代碼


1簡述

前段時間在bluebox的一份android安全pdf中看到一個AndroidManifest Ambiguity方案。該方案基於android系統解析AXML的一個特點:android在解析AXML的屬性的時候,是通過該屬性的res id號而非屬性名定位的。所謂的AXML就是AndroidManifest.xml對應的二進制文件,APK包中存儲的就是AXML。比如屬性:

<public type="attr" name="name" id="0x01010003" />

它的屬性名為name,id號為0x01010003。

該方案的大致原理如下圖所示:

 

我簡要概括一下:

我們在axml(注意是axml不是AndroidManifest.xml)中添加一個屬性,該屬性的屬性名是name,屬性的值是some.class,屬性的ID號為0。根據前文所述,android系統對於非法的res ID號是不會解析的。所以我們添加這個無用的屬性后,並不影響該APK的正常工作(上圖左下角所示),但是對於apktool之類的逆向工具而言,他們卻會對這個無用的屬性進行解析(上圖右下角所示)。所以,如果我們進行重打包的話,apktool就會將該屬性變更為一個ID號0x01010003的可以被系統解析的屬性。這樣造成的后果就是:由於我們的APK中並沒有實現trap.class類,所以APK啟動時會報錯“there is no trap.class~~”。

2 實現方案

該PDF雖然提出了這個方案,但並沒有給出實現的代碼(其實它就給了上面那張圖~其它什么都木有了~),google也是空白。所以當我看懂原理之后,就想自己將它實現出來。哪知事情並沒有我想的那么簡單~~

2.1 AXML文件格式

遇到的第一個挑戰就是:網上竟然搜不到AXML文件的格式!!!當時差點就放棄了,不過后來一想,既然apktool能解析AXML那就說明它是了解AXML的文件格式的,所以就上網搜索了一下解析AXML的各種解析代碼,綜合過后覺得Claud大大的AXML Parser代碼比較利於總結AXML的文件格式。所以就以該代碼問藍本總結了一下文件格式,如下表所示:

0x0~0x3    magic: 0x03000800固定值

0x4~0x7    filesize: 文件整體大小

0x8~0xb     StringTag: 字符串塊開始標志,0x01001c00固定值

0xc~0xf      StringChunkSize:字符串塊大小

0x10~0x13   count of strings:字符串個數,

0x14~0x17   count of styles: 類型個數

0x18~0x1b   reserve field: 保留的,為0

0x1c~0x1f    string的起始偏移值:注意,這個偏移值是相對於stringChunk而言的!

0x20~0x23    styles的起始偏移值:同上

下面存儲的就是n個連續的string的偏移值,每個偏移值占4字節,需要注意的是,這個偏移值加上string的起始偏移值和0x8才是真正的偏移值!n的大小就是0x10~0x13的大小

然后就是n個連續的style的偏移值,同上~

String數據塊

........

Style數據塊

........

注意:到這里,stringchunk就算是結束了

下面就是ResourceChunk了,里面保存的就是資源ID號

ResourceTag: 0x80010800  

ResourceChunkSize: 資源ID塊的大小

連續 ResourceChunkSize/4 -2個res id值。-2主要是除去上面的8字節resourceChunkHeader。

每個res id占4字節

                           ResourceChunk結束

下面就是一些連續的chunk塊了:

CHUNK_STARTNS:  doc開始標志,0x00011000

CHUNK SIZE:

line number

unknown, 0xffffffff

下面就是一個namespace record結構體,簡稱NsRecord

NsRecord->prefix

NsRecord->uri,

然后就是遞歸地進行chunk操作,因為一個命名空間里面往往含有很多子chunk

CHUNK_TYPE:0x02011000->0x00100102為CHUNK_STARTTAG

CHUNK_SIZE 

line number

unknown, 0xffffffff 

current tag's namespace's uri

當前tag的名字 所一個string索引值

flags, unknown usage

當前標簽含有的attr個數,注意最后結果要&0x0000ffff

classAttribute, unknown usage

下面就是連續的n個attribution chunk,attribution的結構體如下:

/* attribute structure within tag */

typedef struct{

      uint32_t uri;        /* uri of its namespace  index  of strings*/

      uint32_t name;   /*屬性名,索引值 index  of strings */

      uint32_t string;   /* attribute value if type == ATTR_STRING ,索引值*/

      uint32_t type;             /* attribute type, == ATTR_* * / 注意該值需要右移24位

      uint32_t data;            /* attribute value, encoded on type */

} Attribute_t;

依次類推

........

 

2.2修改AXML的注意事項

了解了AXML的文件格式,我們就可以想法進行屬性插入了。不過在屬性插入之前,我們必須規划好具體地實施方案,因為它涉及到的東西並不算少。

1)首先,需要對屬性結構體做進一步分析。它的格式如下:

/* attribute structure within tag */

typedef struct{

         uint32_t uri;               /* uri of its namespace  index  of strings*/

         uint32_t name;      /*屬性名,索引值 index  of strings */

         uint32_t string;     /* attribute value if type == ATTR_STRING ,索引值*/

         uint32_t type;            /* attribute type, == ATTR_* * / 注意該值需要右移24位

         uint32_t data;           /* attribute value, encoded on type */

} Attribute_t;

重點是name, string, data。我提取出了一個AXML中屬性片段,如下所示:

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

0C 00 00 00  05 00 00 00   FF FF FF FF   08 00 00 01   01 00 06 7F 

0C 00 00 00  06 00 00 00   FF FF FF FF   08 00 00 01   00 00 05 7F

0C 00 00 00[w1]   04 00 00 00[w2]    17 00 00 00[w3]   08 00 00 03 [w4]   17 00 00 00[w5] 

 [w1]uri:命名空間的URI,是string的索引值

 [w2]name:屬性名,也是一個string的索引值

 [w3]string:如果屬性type為ATTR_STRING的話,此值就是屬性android:name="xxx",xxx在string的索引值。其余情況均為0xffffffff

 [w4]type:屬性的類型,對於android:name,類型值為0x03000008

 [w5]data:屬性的數據值,對於ATTR_STRING而言,它的值就是string的值。

可以發現,結構體里面並沒有一個叫做res ID的成員,那么系統又是如何獲取某個屬性的ID號的呢?原來這里的name成員是身兼兩職,即作為屬性名的一個string索引,又作為res ID的索引。比如這里name = 4,它對應StringChunk中的字符串為"name",對應ResourceChunk中的res ID 0x01010003。所以要插入一個屬性名為name,ID號又為0的屬性,我們就必須新建一個string,該string的值為name,再新建一個res ID,值為0,且兩者在各自Chunk區域的索引值是相等的(這是重點)

2)其次,就是在StringChunk中string的對齊問題(最初被弄得腦洞大開~)。

AXML中幾乎所有的成員都是uint32型的,除了使用UTF-16編碼的string數據塊之外。所以在加入string后必須對string數據塊進行4字節對齊。而如果原AXML的string數據塊已經進行過4字節對齊(即人為地填充了幾個0x00)的話,我們就需要注意UTF-16編碼的最后一個string的第一個字節的大小並不包含這幾個填充的0x00(這個字節表示該string所占用的字節數,詳情可查閱UTF-16編碼相關資料)。為了繞過煩人的對齊問題,我們使用取巧的方式獲取字符串的長度:

stringLen = stringChunkSize - stringOffset; //此時的stringLen肯定是4字節對齊的

當然,這是在沒有style的情況下,如果有的話,還得采取額外的操作(實現代碼中有~)。為了簡便,我是直接將添加的string加在這個對齊后的字符串之后的,這樣就只需要考慮添加的字符串是否需要對齊了~

3)然后,就是ResourceChunk的擴充。

在1)中已經提到插入的屬性的name的值同時充當res ID索引值。而通常ResourceChunk中的res ID個數是遠少於string 的個數的,那么這就需要我們將ResourceChunk進行擴充。擴充很簡單,全部賦值為0即可。

4)最后,除了需要添加數據外,還需要修改原文件的某些“計量值”,這些計量值都是與數據塊大小或偏移值有關的,總結如下:

①fileSize

②StringChunkSize

③count of string

④styles的起始偏移值(如果有style的話就需要修改)

⑤ResourceChunkSize

⑥application所屬chunk的chunksize

⑦applicationh含有的屬性個數

 

2.3 修改AXML步驟

1)修改StringChunk,添加UTF-16表示的字符串chouchou.class和name,並為這兩個字符串添加偏移值條目。同時對StringChunkSize、count of string、styles的起始偏移值進行修復;

2)修改ResourceChunk,主要是進行res ID擴充和對ResourceChunkSize的修復

3)修改application所在的chunk,插入屬性,同時對chunksize和applicationh含有的屬性個數進行修復;

4)將不需要修改的部分copy到合適的位置;

5)修復fileSize

當然,具體地實現肯定比上訴步驟復雜一些,不過實現源碼中有較為詳細的注釋,大家可參照源碼閱讀~

3 代碼說明

AxmlParser.h/.c是Claud大大解析axml的源碼,出於對作者的感謝以及讓大家更詳細地了解AXML的解析過程(其實,是我實在是不想自己寫解析代碼o(╯□╰)o),我將實現代碼跟它合並到一塊了。AxmlModify.c就是我寫的實現AXML修改功能。

4 使用方法

當前代碼還不完善,只是初步實現了插入application.attr("name", "chouchou.class",0x0)的功能。所以並非最終版。

代碼只能在linux下運行,下載代碼后make即可生成可執行文件manifestAmbiguity。然后直接運行./manifestAmbiguity可以得到完整的使用說明。

修改前:

 

修改后:

 

將修改后的xml覆蓋原APK中的xml,然后刪掉原來的簽名文件夾再進行簽名即可。這時候如果對按照此方案修改后的APK進行重打包,就會發現重打包的APK已經無法啟動了。

5 下一步工作

由於目前的apk軟件保護主要是基於dex代碼加密和so庫文件加密,對AndroidManifest.xml並沒有進行任何操作,而AndroidManifest.xml作為apk的入口文件,其重要性是不言而喻的。所以我想能不能在此文件中做些“手腳”,然后結合相應的處理代碼實現另一角度的軟件保護。

比如,我們完全可以實現那個陷阱類trap.class,且這個類繼承自application等等,以便被重打包的apk也可運行。只是,從一開始,該apk就運行在一個錯誤的環境中,至於之后的操作,那就可以盡情發揮了。

或者,我們可以在其他tag中插入一些不會影響apk運行的屬性(即新添加的屬性不可被系統識別,重打包后該屬性能被系統識別但又不會影響apk的運行),然后在代碼中檢查AndroidManifest.xml是否含有該屬性,如果有就說明軟件被重打包了。

等等~

如果大家有好的建議或方法,請一定不吝賜教~謝謝!

代碼地址:
https://github.com/wanchouchou/ManifestAmbiguity

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM