Android安全/安全技術--21--基礎檔案格式解析
4-1、so檔案格式解析
1、ELF檔案格式
Android中的so檔案就是ELF檔案,瞭解so檔案首先需要了解ELF檔案的格式,使用工具為readelf,常用命令如下:
1、檢視so檔案的頭部資訊
readelf -h xxx.so //如:libhello.so等
2、檢視so檔案的節(Section)頭的資訊
readelf -S xxx.so //如:libhello.so等
3、檢視so檔案的程式段頭資訊(Program)
readelf -l xxx.so //如:libhello.so等
4、檢視so檔案的全部內容
readelf -a xxx.so //如:libhello.so等
2、解析ELF檔案
1、定義ELF檔案中各個結構體的內容,需要閱讀原始碼中elf.h檔案,路徑如下:
\external\kernel-headers\original\asm-x86\elf.h
在看elf.h檔案中定義的資料結構時,記得每個欄位佔用的位元組數的可以了。
解析ELF檔案的頭部資訊
ELF頭部資訊資料結構中常用的幾個重要欄位:
e_phoff:是程式頭內容在整個檔案的偏移值,可以用這個偏移值來定位程式頭的開始位置,用於解析程式頭資訊。 e_shoff:是段頭內容在這個檔案的偏移值,可以用這個偏移值來定位段頭的開始位置,用於解析段頭資訊。 e_phnum:是程式頭的個數。 e_shnum:是段頭的個數。 e_shstrndx:是String段在整個段列表中的索引值,用於後面定位String段的位置。
3、解析段頭資訊,大致同上
4、解析程式頭資訊,大致同上
4-2、AndroidManifest.xml檔案格式解析
1、解析頭部資訊
任何一個檔案都有頭部資訊,頭部資訊一般都有固定格式頭部資訊還有以下這些欄位資訊:
檔案魔數(Magic Number):四個位元組
檔案大小(File Size):四個位元組
2、解析String Chunk
String Chunk主要用於存放AndroidManifest檔案中所有的字串資訊。
ChunkType:StringChunk的型別,固定四個位元組:0X001C0001 ChunkSize:StringChunk的大小,四個位元組 StringCount:StringChunk中字串的個數,四個位元組 StyleCount:StringChunk中樣式個數,四個位元組,一直是0X00000000 Unknown:區域位置,四個位元組,在解析過程中,這裡需要略過四個位元組 StringPoolOffset:字串池的偏移值,四個位元組,這個偏移值是相對與StringChunk的頭部位置 StylePoolOffset:樣式池的偏移值,四個位元組,這裡沒有Style,這個欄位可以忽略 StringOffsets:每個字串的偏移值,它的大小應該是StringCount*4個位元組 StyleOffsets:每個樣式的偏移值,它的大小應該是StyleCount*4個位元組
3、解析Resourceld Chunk
Resourceld Chunk主要是用來存放AndroidManifest中用到的系統屬性值對應的資源ID。
ChunkType:Resourceld Chunk的型別,固定四個位元組:0X00080108
ChunkSize:Resourceld Chunk的大小,四個位元組
Resourcelds:Resourceld的內容,這裡大小是Resourceld Chunk大小除以4,減去頭部的大小8個位元組(ChunkType和ChunkSize)
4、解析Start Namespace Chunk
這個Chunk主要包含一個AndroidManifest檔案中的命令空間的內容
ChunkType:Chunk的型別,固定四個位元組:0X00100100
ChunkSize:Chunk的大小,四個位元組
LineNumber:在AndroidManifest檔案中的行號,四個位元組
Unknown:未知區域,四個位元組
Prefix:名稱空間的字首,比如:android
Uri:名稱空間的URI
5、解析Start Tag Chunk
這個Chunk主要是存放AndroidManifest中的標籤資訊,是最核心、最複雜的內容
ChunkType:Chunk的型別,固定四個位元組:0X00100102
ChunkSize:Chunk的大小
LineNumber:在AndroidManifest檔案中的行號,四個位元組
Unkonwn:未知區域,四個位元組
NamespaceUri:該標籤用到的名稱空間的URI,四個位元組
Name:標籤名稱,四個位元組
Flags:標籤的型別,四個位元組,比如是開始標籤還是結束標籤等
AttributeCount:標籤包含的屬性個數,四個位元組
ClassAttribute:標籤包含的類屬性,四個位元組
Attributes:屬性內容,每個屬性是一個大小為5位元組的Entry陣列
4-3、resource.arsc檔案格式解析
1、頭部資訊
Resources.arsc檔案格式是由一系列的Chunk構成,每一個Chunk均包含ResChunk_header,用來描述這個Chunk的基本資訊。
type:當前Chunk的型別
headerSize:當前Chunk的頭部大小
size:當前Chunk的大小
2、資源索引表的頭部資訊
Resources.arsc檔案的第一個結構是資源索引表頭部,其結構描述了Resources.arsc檔案的大小和資源包數量。
header:就是標準的Chunk頭部資訊格式
packageCount:被編譯的資源包的個數
3、資源項的值字串資源池
緊跟頭部的是資源項的值字串資源池,該字串資源池包含了所有在資源包裡面定義的資源項的值字串。
header:標準的Chunk頭部資訊結構
stringCount:字串的個數
styleCount:字串樣式的個數
flags:字串的屬性,可取值包括0x000、0x001、0x100和它們的組合值
stringStart:字串內容塊相對於其頭部的距離
stylesStart:字串樣式塊相對於其頭部的距離
4、Package資料塊
接著資源項的值字串資源池後面的部分就是Package資料塊,這個資料塊記錄編譯包的元資料。
header:Chunk的頭部資訊資料結構
id:包的ID,等於Package ID,一般使用者包為0x7f,系統資源包為0x01
name:包名
typeString:型別字串資源池相對頭部的偏移
lastPublicType:最後一個匯出的Public型別字串在型別字串資源池中的索引
keyStrings:資源項名稱字串相對頭部的偏移
lastPublicKey:最後一個匯出的Public資源項名稱字串在資源項名稱字串資源池中的索引
5、型別規範資料塊
型別規範資料塊用來描述資源項的配置差異性。
header:Chunk的頭部資訊結構
id:標識資源的Type ID
res0:保留,始終為0
res1:保留,始終為0
entryCount:等於本型別的資源項個數,指名稱相同的資源項的個數
6、資源型別項資料塊
用來描述資源項的具體資訊,如此就可以知道每一個資源項的名稱、值和配置等資訊。
header:Chunk的頭部資訊結構
id:標識資源的Type ID
res0:保留,始終為0
res1:保留,始終為0
entryCount:等於本型別的資源項個數,指名稱相同的資源項的個數
entriesStart:等於資源項資料塊相對頭部的偏移值
resConfig:指向一個ResTable_config,用來描述配置資訊、地區、語言、解析度等。
ResTable_type後接著是一個大小為entryCount的uint32_t陣列,每一個數組元素都用於描述一個資源型別項資料塊的偏移位置。緊跟在這個偏移陣列後面的是一個大小為entryCount的ResTable_entry陣列,每一個數組元素都用來描述一個資源項的具體資訊。
ResTable_entry根據flags的不同,後面跟的資料也不相同:
flags為1:
ResTable_entry是ResTable_map_entry,ResTable_map_entry繼承自ResTable_entry,而ResTable_map_entry其後跟著count個ResTable_map型別的陣列。
flags為0:
則ResTable_entry其後跟隨的是一個Res_value,描述一個普通資源的值。
4-4、dex檔案格式解析
dex檔案格式如下:
檔案頭 | header | 檔案頭 |
---|---|---|
索引區 | string_ids | 字串的索引 |
type_ids | 型別的索引 | |
proto_ids | 方法原型的索引 | |
field_ids | 域的索引 | |
method_ids | 方法的索引 | |
資料區 | class_defs | 類的定義區 |
data | 資料區 | |
link_data | 連結資料區 |
上我寫的文件截圖:
1、頭部資訊header結構
dex檔案裡的header除了描述.dex檔案的資訊外,還有檔案裡其它各個區域的索引。header對應為結構體型別,邏輯上的描述用結構體header_item來理解它。
檔案頭摘要:
偏移值 | 大小 | 說明 |
---|---|---|
0x0 | 8 | 幻數(magic number):”dex\n009\0” |
0x8 | 4 | 校驗和 |
0xC | 20 | SHA-1 Signature |
0x20 | 4 | 檔案長度(位元組) |
0x24 | 4 | 頭長度(始終為0x5C) |
0x28 | 8 | 填充(預留空間) |
0x30 | 4 | 字元列表中的字元值 |
0x34 | 4 | 字元列表的絕對位移值 |
0x38 | 4 | 字元相關空間 |
0x3C | 4 | 類專案中的類的數量 |
0x40 | 4 | 類專案的絕對位移值 |
0x44 | 4 | 欄位列表中的欄位數量 |
0x48 | 4 | 欄位列表的絕對位移值 |
0x4C | 4 | 方法列表中的方法數量 |
0x50 | 4 | 方法列表的絕對位移值 |
0x54 | 4 | 類定義列表中的類定義數量 |
0x58 | 4 | 類定義列表的絕對位移值 |
magic:
頭區域的起始8位元組是magic number。最後n035\0是換行回車符和Null值,用於防止破壞magic number的數值
Checksum:
除magic number以外的其他檔案的校驗和值,使用了adler32演算法。因為是小端序儲存的,所以是0x07784031
Signature:
除magic number、校驗和之外的其他檔案的SHA-1演算法識別標誌,用於識別固有檔案
file_size:
檔案大小。因使用了小端序,所以是0x6100
Header_size:
頭大小,始終為0x70
endian_tag:
小端序標籤。值為0x12345678,表示資料使用小端序方式儲存
link_size:
Link節區大小。非靜態連線時會設定為“0”
link_off:
從檔案起始到連線會話的位移值。如果link_size是0,它也會被設定為0。如果不是0,位移值應該是link_data節區的位移值
2、string_ids資料結構
string_ids區索引了dex檔案所有的字串,這個區裡的元素格式為string_ids_item。以_ids結尾的各個段裡放置的都是對應資料的偏移地址,只是為一個索引,所以才會在dex檔案佈局裡把這些區歸類為“索引區”。
LEB128格式:是基於一個位元組的一種不定長的編碼方式,若第一個位元組的最高位為1,則表示還需要下一個位元組來描述,直至最後一個位元組最高位為0。將LEB128編碼的數字轉換為可讀數字的規則是:除去每個位元組的第7位(左邊最高位),將每個位元組剩餘的7個位拼接在一起,即為要實際使用的數字。
3、type_ids資料結構
type_ids資料結構中存放的資料主要是描述dex中所有的型別,比如類型別、基本型別等資訊。type_ids區索引了dex檔案裡的所有資料型別,包括class型別、陣列型別、基本型別。本區域裡的元素格式為:type_ids_item。
4、proto_ids資料結構
該資料結構裡的元素為proto_id_item。其中引數如下:
shorty_idx:它的值是一個string_ids的index號,最終是一個簡短的字串描述,用來說明該method原型。
return_type_idx:它的值是一個type_ids的index號,表示該method原型的返回值型別。
parameters_off:字尾off是offset,指向method原型的引數列表type_list,若method沒有引數,則值為0。
5、field_ids資料結構
field_ids區裡面存放的是dex檔案引用的所有的field。這個區的元素格式是field_id_item。
class_idx:表示本field所屬的class型別,class_idx的值是type_ids的一個index,並且必須指向一個class型別。
type_idx:表示本field的型別,它的值也是type_ids的一個index
name_idx:表示本field的名稱,它的值是string_ids的一個index
6、method_ids資料結構
method_ids是索引區的最後一個條目,它索引了dex檔案裡的所有method。method_ids的元素格式是method_id_item。
class_idx:表示該method所屬的class型別,class_idx的值是type_ids的一個index,並且必須指向一個class型別。
name_idx:表示該method的名稱,它的值是string_ids的一個index。
proto_idx:描述該method的原型,指向proto_ids的一個index。
7、class_defs資料結構
1、class_def_item:class_defs區域裡存放著class的定義,並且部分資料指向了data區域裡面。
偏移值 | 大小 | 說明 |
---|---|---|
0x0 | 4 | 類索引 |
0x4 | 4 | 訪問插口 |
0x8 | 4 | 超級類的索引 |
0xC | 4 | 介面專案的絕對位移 |
0x10 | 4 | 靜態欄位專案的絕對位移 |
0x14 | 4 | 例項欄位專案的絕對位移0x18 4 非虛擬方法專案的絕對位移 |
0x1C | 4 | 虛擬方法專案的絕對位移 |
class_idx:
描述具體的class型別,值是type_ids的一個index
access_flags:
描述class的訪問型別,諸如public、final、static等
superclass_idx:
描述supperclass的型別,值的形式跟class_idx一樣
interfaces_off:
值為偏移地址,指向class的interfaces,被指向的資料結構為type_list。class若沒有interfaces,值為0
source_file_idx:
表示原始碼檔案的資訊,值是string_ids的一個index
annotions_off:
值是一個偏移地址,指向的內容是該class的註釋,位置在data區,格式為:annotations_directory_item,若沒有此項內容,值為0
class_data_off:
值是一個偏移地址,指向的內容是該class的使用到的資料,位置在data區,格式為class_data_item,若沒有此項內容,值為0
static_value_off:
值是一個偏移地址,指向data區裡的一個列表,格式為encoded_array_item,若沒有此項內容,值為0
2、class_def_item => class_data_item
class_data_off指向data區裡的class_data_item結構,class_data_item裡存放著本class使用到的各種資料。
3、class_def_item => class_data_item => code_item
如何走到這一步:
1、一個dex檔案被分成了9個區,其中一個索引區叫做class_defs,索引了dex裡面用到的class,以及對這個class的描述。
2、class_defs區,其實是class_def_item結構,描述了諸如名稱、superclass、access flag、interface等。
3、class_data_item結構,描述class裡使用到的static field、interface field、direct_method、virtual_method的數目和描述。
4、encoded_method結構,描述了某個method和method型別,access flags和一個指向code_item的偏移地址,裡面存放的是該method的具體實現。
5、code_item,該結構裡描述著某個method的具體實現。