安卓逆向系列教程(二)APK 和 DEX

安卓逆向系列教程(二)APK 和 DEX

作者:飛龍

APK

APK 是 Android 軟件包的分發(fā)格式帕胆,它本身是個 Zip 壓縮包谎柄。APK 根目錄下可能出現(xiàn)的目錄和文件有:

名稱 用途
META-INF 存放元數(shù)據(jù)
AndroidManifest.xml 編譯后的全局配置文件
assets 存放資源文件乍狐,不會編譯
classes.dex 編譯并打包后的源代碼
lib 存放二進制共享庫荚孵,含有armeabi-*余耽、mips耍缴、x86等文件夾戴而,對應具體的平臺
res 存放資源文件
resources.arsc 編譯并打包后的res/values中的文件

res

res 中可能出現(xiàn)的目錄如下:

名稱 用途
anim 存放編譯后的動畫 XML 文件(<XXXAnimation>
color 存放編譯后的選擇器 XML 文件(<selector>
drawable-* 存放圖片凑术,*為不同分辨率,圖片按照不同分辨率歸類所意。其中帶.9的圖片為可拉伸的圖片淮逊。
layout 存放編譯后的布局 XML 文件(<XXXLayout>
menu 存放編譯后的菜單 XML 文件(<menu>
mipmap-* 存放使用 mipmap 技術加速的圖片,一般用來存放應用圖標扶踊,其它同drawable-*
raw 存放資源文件泄鹏,不會編譯,比如音樂秧耗、視頻备籽、純文本等
xml 存放編譯后的自定義 XML 文件

resources.arsc

在 APK 中是找不到res/values這個目錄的,因為它里面的文件編譯后打包成了resources.arsc分井。為了理解它车猬,我們先看一看原始的res/values霉猛。

res/values中保存資源 XML 文件,根節(jié)點為<resources>诈唬。一般可能會出現(xiàn)以下幾種文件:

名稱 用途
arrays.xml 存放整數(shù)數(shù)組和字符串數(shù)組韩脏,使用<integer-array><string-array>定義,元素使用<item>定義
bools.xml 存放布爾值铸磅,使用<bool>定義
colors.xml 存放顏色赡矢,使用<color>定義
dimens.xml 存放尺寸,使用<dimen>定義
drawables.xml 存放顏色阅仔,使用<drawable>定義
ids.xml 存放 ID吹散,使用<item type="id">定義
integers.xml 存放整數(shù),使用<integers>定義
strings.xml 存放字符串八酒,使用<strings>定義
styles.xml 存放顏色空民,使用<style>定義,元素使用<item>定義

res/values中的文件名稱是無所謂的羞迷,這些名稱只是約定界轩。也就是說,任何res/values中的文件中的字符串都會出現(xiàn)在R.strings里面衔瓮。

雖然我們在 APK 中無法直接看到這些文件浊猾,但是反編譯之后就可以了。反編譯之后热鞍,我們也會找到一個public.xml文件葫慎,是res里所有東西的索引:

<resources>
    <public type="drawable" name="ic_launcher" id="0x7f020000" />
    <public type="layout" name="activity_main" id="0x7f030000" />
    <public type="layout" name="activity_sub" id="0x7f030001" />
    <public type="dimen" name="activity_horizontal_margin" id="0x7f040000" />
    <public type="dimen" name="activity_vertical_margin" id="0x7f040001" />
    <public type="string" name="action_settings" id="0x7f050000" />
    <public type="string" name="app_name" id="0x7f050001" />
    <public type="string" name="hello_world" id="0x7f050002" />
    <public type="string" name="title_activity_sub" id="0x7f050003" />
    <public type="style" name="AppTheme" id="0x7f060000" />
    <public type="menu" name="main" id="0x7f070000" />
    <public type="menu" name="sub" id="0x7f070001" />
    <public type="id" name="button1" id="0x7f080000" />
    <public type="id" name="action_settings" id="0x7f080001" />
</resources>

DEX

DEX 即 Dalvik Executable,Dalvik 可執(zhí)行文件薇宠。它的結(jié)構(gòu)如下:

struct DexFile{
    DexHeader    Header;
    DexStringId  StringIds[stringIdsSize];
    DexTypeId    TypeIds[typeIdsSize];
    DexFieldId   FieldIds[fieldIdsSize];
    DexMethodId  MethodIds[methodIdsSize];
    DexProtoId   ProtoIds[protoIdsSize];
    DexClassDef  ClassDefs[classDefsSize];
    DexData      Data;
    DexLink      LinkData;
};

我們可以看到偷办,它可以分為九個區(qū)段,如下:

Header
StringIds
TypeIds
FieldIds
MethodIds
ProtoIds
ClassDefs
Data
LinkData

大體結(jié)構(gòu)如這張圖所示:

另外澄港,在講解各個區(qū)段之前椒涯,需要首先了解一些數(shù)據(jù)類型的定義:

類型 定義
u1 等同于uint8_t,表示 1 字節(jié)的無符號數(shù)
u2 等同于uint16_t回梧,表示 2 字節(jié)的無符號數(shù)
u4 等同于uint32_t逐工,表示 4 字節(jié)的無符號數(shù)
u8 等同于uint64_t,表示 8 字節(jié)的無符號數(shù)

Header 區(qū)段

Header 區(qū)段用于儲存版本標識漂辐、校驗和泪喊、文件大小、各部分的大小及偏移髓涯。結(jié)構(gòu)以及描述如下:

struct DexHeader {
    u1  magic[8];           /* 版本標識 */  
    u4  checksum;           /* adler32 檢驗和 */  
    u1  signature[kSHA1DigestLen]; /* SHA-1 哈希值 */
    u4  fileSize;           /* 整個文件大小 */
    u4  headerSize;         /* Header 區(qū)段大小 */
    u4  endianTag;          /* 字節(jié)序標記 */
    u4  linkSize;           /* 鏈接區(qū)段大小 */
    u4  linkOff;            /* 鏈接區(qū)段偏移 */
    u4  mapOff;             /* MapList 的偏移 */
    u4  stringIdsSize;      /* StringId 的個數(shù) */
    u4  stringIdsOff;       /* StringIds 區(qū)段偏移 */
    u4  typeIdsSize;        /* TypeId 的個數(shù) */
    u4  typeIdsOff;         /* TypeIds 區(qū)段偏移 */
    u4  protoIdsSize;       /* ProtoId 的個數(shù) */
    u4  protoIdsOff;        /* ProtoIds 區(qū)段偏移 */
    u4  fieldIdsSize;       /* FieldId 的個數(shù) */
    u4  fieldIdsOff;        /* FieldIds 區(qū)段偏移 */
    u4  methodIdsSize;      /* MethodId 的個數(shù) */
    u4  methodIdsOff;       /* MethodIds 區(qū)段偏移 */
    u4  classDefsSize;      /* ClassDef 的個數(shù) */
    u4  classDefsOff;       /* ClassDefs 區(qū)段偏移 */
    u4  dataSize;           /* 數(shù)據(jù)區(qū)段的大小 */
    u4  dataOff;            /* 數(shù)據(jù)區(qū)段的文件偏移 */
};

有幾個條目需要特別提醒袒啼。

  • magic:必須為DEX_FILE_MAGIC

    ubyte[8] DEX_FILE_MAGIC = { 0x64 0x65 0x78 0x0a 0x30 0x33 0x35 0x00 }
                            = "dex\n035\0";
    
  • checksum:是整個文件除去它本身以及魔數(shù)的校驗和。

  • signature:是整個文件除去它本身、校驗和以及魔數(shù)的哈希值蚓再。

  • headerSize:一般為 70滑肉。

  • endianTag:有兩種順序,小端和大端摘仅,定義如下:

    uint ENDIAN_CONSTANT = 0x12345678; /* 小端序 */
    uint REVERSE_ENDIAN_CONSTANT = 0x78563412; /* 大端序 */
    

    一般為小端序靶庙,反正我還沒見過大端的。

  • stringIdsOff:由于前一個區(qū)段的偏移加上它的長度一般為后一個區(qū)段的偏移娃属,所以這個條目一般也為 70六荒。

  • xxxSize:要注意有幾個是個數(shù),后綴也是Size矾端。

  • xxxOff:如果對應的xxxSize為 0掏击,那么它也為 0(很奇怪)。

StringIds 區(qū)段

StringIds 區(qū)段包含stringIdsSizeDexStringId結(jié)構(gòu)秩铆,如下:

struct DexStringId {
    u4 stringDataOff;   /* 字符串內(nèi)容砚亭,字符串數(shù)據(jù)偏移 */
};

其中數(shù)據(jù)偏移指向 Data 區(qū)段的字符串數(shù)據(jù)。

TypeIds 區(qū)段

TypeIds 包含typeIdsSizeDexTypeId結(jié)構(gòu)殴玛,如下:

struct DexTypeId {
    u4 descriptorIdx;    /* 類型的完全限定符捅膘,指向 DexStringId 列表的索引 */
};

索引是一個從 0 開始的數(shù)字,表示對應第幾個DexStringId滚粟。這些DexStringId指向的字符串都是類型名稱寻仗,比如ILjava/lang/String;之類的坦刀。DexTypeId的索引也會用于后面的結(jié)構(gòu)。

ProtoIds 區(qū)段

ProtoIds 包含ProtoIdsSizeDexProtoId結(jié)構(gòu)蔬咬。這里的 Proto 指方法原型鲤遥,包含返回類型和參數(shù)類型。

struct DexProtoId {
    u4 shortyIdx;       /* 原型縮寫林艘,指向 DexStringId 列表的索引 */
    u4 returnTypeIdx;   /* 返回類型盖奈,指向 DexTypeId 列表的索引 */
    u4 parametersOff;   /* 參數(shù)類型列表,指向 DexTypeList 的偏移 */
};

struct DexTypeList {
    u4 size;             /* 接下來 DexTypeItem 的個數(shù) */
    DexTypeItem list[size]; /* DexTypeItem 結(jié)構(gòu) */
};

struct DexTypeItem {
    u2 typeIdx;    /* 參數(shù)類型狐援,指向 DexTypeId 列表的索引 */
};

原型縮寫是把所有返回類型和參數(shù)類型的名稱拼在一起钢坦,對象的話只寫L。比如int(int,int)寫為III啥酱,void()寫為V爹凹,void(String)寫為VL

參數(shù)類型列表一般保存在Data區(qū)段中镶殷,如果沒有禾酱,parametersOff為 0。

FieldIds 區(qū)段

TypeIds 包含fieldIdsSizeDexFieldId結(jié)構(gòu),如下:

struct DexFieldId {
    u2 classIdx;   /* 類的類型颤陶,指向 DexTypeId 列表的索引 */
    u2 typeIdx;    /* 字段類型颗管,指向 DexTypeId 列表的索引 */
    u4 nameIdx;    /* 字段名稱,指向 DexStringId 列表的索引 */
};

MethodIds 區(qū)段

MethodIds 包含methodIdsSizeDexMethodId結(jié)構(gòu)滓走,如下:

struct DexMethodId {
    u2 classIdx;  /* 類的類型垦江,指向 DexTypeId 列表的索引 */
    u2 protoIdx;  /* 方法原型,指向 DexProtoId 列表的索引 */
    u4 nameIdx;   /* 方法名稱搅方,指向 DexStringId 列表的索引 */
};

ClassDefs 區(qū)段

ClassDefs 包含classDefsSizeDexClassDef結(jié)構(gòu)比吭,如下:

struct DexClassDef {
    u4 classIdx;         /* 類的類型,指向 DexTypeId 列表的索引 */
    u4 accessFlags;      /* 訪問標志 */
    u4 superclassIdx;    /* 父類類型腰懂,指向 DexTypeId列表的索引 */
    u4 interfacesOff;    /* 接口梗逮,指向 DexTypeList 的偏移 */
    u4 sourceFileIdx;    /* 源文件名,指向 DexStringId 列表的索引 */
    u4 annotationsOff;   /* 注解绣溜,指向 DexAnnotationsDirectoryItem 結(jié)構(gòu) */
    u4 classDataOff;     /* 指向 DexClassData 結(jié)構(gòu)的偏移 */
    u4 staticValuesOff;  /* 指向 DexEncodedArray 結(jié)構(gòu)的偏移 */
};

struct DexClassData {
    DexClassDataHeader header;                    /* 各個字段與方法的個數(shù) */
    DexField staticFields[staticFieldsSize];      /* 靜態(tài)字段 */
    DexField instanceFields[instanceFieldsSize];  /* 實例字段 */
    DexMethod directMethods[directMethodsSize];   /* 直接方法 */
    DexMethod virtualMethods[virtualMethodsSize]; /* 虛方法 */
};

struct DexClassDataHeader {
    u4 staticFieldsSize;   /* 靜態(tài)字段個數(shù) */
    u4 instanceFieldsSize; /* 實例字段個數(shù) */
    u4 directMethodsSize;  /* 直接方法個數(shù) */
    u4 virtualMethodsSize; /* 虛方法個數(shù) */
};

struct DexField {
    u4 fieldIdx;    /* 指向 DexFieldId 的索引 */
    u4 accessFlags; /* 訪問標志 */
};

struct DexMethod {
    u4 methodIdx;   /* 指向 DexMethodId 的索引 */
    u4 accessFlags; /* 訪問標志 */
    u4 codeOff;     /* 方法指令慷彤,指向DexCode結(jié)構(gòu)的偏移 */
};

struct DexCode {
    u2 registersSize;    /* 使用的寄存器個數(shù) */
    u2 insSize;          /* 參數(shù)個數(shù) */
    u2 outsSize;         /* 調(diào)用其他方法時使用的寄存器個數(shù) */
    u2 triesSize;        /* Try/Catch個數(shù) */
    u4 debugInfoOff;     /* 指向調(diào)試信息的偏移 */
    u4 insnsSize;        /* 指令集個數(shù),以2字節(jié)為單位 */
    u2 insns[insnsSize]; /* 指令集 */
};

DexClassDataDexCode保存在 Data 區(qū)段中怖喻。

Data 區(qū)段

這個區(qū)段中除了存放二級結(jié)構(gòu)和字符串底哗,還有個重要的結(jié)構(gòu)叫做DexMapList,它實際上 DEX 中所有東西的索引锚沸,包括各種二級結(jié)構(gòu)跋选、字符串和它本身。DEX 中同類結(jié)構(gòu)都會保存在一起哗蜈,所以一類結(jié)構(gòu)只占用一個條目前标。

struct DexMapList {
    u4 size;                  /* 條目個數(shù) */
    DexMapItem list[size];    /* 條目列表 */
};

struct DexMapItem {   
    u2 type;      /* 結(jié)構(gòu)類型,kDexType 開頭 */
    u2 unused;    /* 未使用距潘,用于字節(jié)對齊 */
    u4 size;      /* 連續(xù)存放的結(jié)構(gòu)個數(shù) */
    u4 offset;    /* 結(jié)構(gòu)的偏移 */
};

/* 結(jié)構(gòu)類型代碼 */
enum {
    kDexTypeHeaderItem               = 0x0000,
    kDexTypeStringIdItem             = 0x0001,
    kDexTypeTypeIdItem               = 0x0002,
    kDexTypeProtoIdItem              = 0x0003,
    kDexTypeFieldIdItem              = 0x0004,
    kDexTypeMethodIdItem             = 0x0005,
    kDexTypeClassDefItem             = 0x0006,
    kDexTypeMapList                  = 0x1000,
    kDexTypeTypeList                 = 0x1001,
    kDexTypeAnnotationSetRefList     = 0x1002,
    kDexTypeAnnotationSetItem        = 0x1003,
    kDexTypeClassDataItem            = 0x2000,
    kDexTypeCodeItem                 = 0x2001,
    kDexTypeStringDataItem           = 0x2002,
    kDexTypeDebugInfoItem            = 0x2003,
    kDexTypeAnnotationItem           = 0x2004,
    kDexTypeEncodedArrayItem         = 0x2005,
    kDexTypeAnnotationsDirectoryItem = 0x2006,
};

參考

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末炼列,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子音比,更是在濱河造成了極大的恐慌俭尖,老刑警劉巖击胜,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桨昙,死亡現(xiàn)場離奇詭異,居然都是意外死亡陨帆,警方通過查閱死者的電腦和手機骚亿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門已亥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人来屠,你說我怎么就攤上這事陷猫★啵” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵绣檬,是天一觀的道長足陨。 經(jīng)常有香客問我,道長娇未,這世上最難降的妖魔是什么墨缘? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮零抬,結(jié)果婚禮上镊讼,老公的妹妹穿的比我還像新娘。我一直安慰自己平夜,他們只是感情好蝶棋,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著忽妒,像睡著了一般玩裙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上段直,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天吃溅,我揣著相機與錄音,去河邊找鬼鸯檬。 笑死决侈,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的喧务。 我是一名探鬼主播赖歌,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼功茴!你這毒婦竟也來了庐冯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤痊土,失蹤者是張志新(化名)和其女友劉穎肄扎,沒想到半個月后墨林,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赁酝,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年旭等,在試婚紗的時候發(fā)現(xiàn)自己被綠了酌呆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡搔耕,死狀恐怖隙袁,靈堂內(nèi)的尸體忽然破棺而出痰娱,到底是詐尸還是另有隱情,我是刑警寧澤菩收,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布梨睁,位于F島的核電站,受9級特大地震影響娜饵,放射性物質(zhì)發(fā)生泄漏坡贺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一箱舞、第九天 我趴在偏房一處隱蔽的房頂上張望遍坟。 院中可真熱鬧,春花似錦晴股、人聲如沸愿伴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽隔节。三九已至,卻和暖如春胡桨,著一層夾襖步出監(jiān)牢的瞬間官帘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工昧谊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留刽虹,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓呢诬,卻偏偏與公主長得像涌哲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子尚镰,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容