日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

awx文件解析_Android so(ELF)文件解析

發布時間:2024/9/19 Android 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 awx文件解析_Android so(ELF)文件解析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、前言

so文件是啥?so文件是elf文件,elf文件后綴名是.so,所以也被chang常稱之為so文件,elf文件是linux底下二進制文件,可以理解為windows下的PE文件,在Android中可以比作dll,方便函數的移植,在常用于保護Android軟件,增加逆向難度。解析elf文件有啥子用?最明顯的兩個用處就是:1、so加固;2、用于frida(xposed)的檢測!

本文使用c語言,編譯器為vscode。如有錯誤,還請斧正!!!

PS:該文已經首發于某公眾號,介意者勿噴!!!

二、SO文件整體格式

so文件大體上可分為四部分,一般來說從上往下是ELF頭部->Pargarm頭部->節區(Section)->節區頭,其中,除了ELF頭部在文件位置固定不變外,其余三部分的位置都不固定。整體結構圖可以參考非蟲大佬的那張圖,圖片如下:

解析語言之所以選擇c語言,有兩個原因:1、做so加固的時候可以需要用到,這里就干脆用c寫成一個模板,哪里需要就哪里改,不像上次解析dex文件的時候用python寫,結果后面寫指令還原的時候需要用的時候在寫一遍c版本代價太大了;2、在安卓源碼中,有個elf.h文件,這個文件定義了我們解析時需要用到的所有數據結構,并且給出了參考注釋,是很好的參考資料。elf.h文件路徑如下:

三、解析ELF頭部

ELF頭部數據格式在elf.h文件中已經給出,如下圖所示:

每個字段解釋如下:

1、e_ident數組:前4個字節為.ELF,是elf標志頭,第5個字節為該文件標志符,為1代表這是一個32位的elf文件,后面幾個字節代表版本等信息。

2、e_type字段:表示是可執行文件還是鏈接文件等,安卓上的so文件就是分享文件,一般該字段為3,詳細請看下圖。

3、e_machine字段:該字段標志該文件運行在什么機器架構上,例如ARM。

4、e_version字段:該字段表示當前so文件的版本信息,一般為1.

5、e_entry字段:該字段是一個偏移地址,為程序啟動的地址。

6、e_phoff字段:該字段也是一個偏移地址,指向程序頭(Pargram Header)的起始地址。

7、e_shoff字段:該字段是一個偏移地址,指向節區頭(Section Header)的起始地址。

8、e_flags字段:該字段表示該文件的權限,常見的值有1、2、4,分別代表read、write、exec。

9、e_ehsize字段:該字段表示elf文件頭部大小,一般固定為52.

10、e_phentsize字段:該字段表示程序頭(Program Header)大小,一般固定為32.

11、e_phnum字段:該字段表示文件中有幾個程序頭。

12、e_shentsize:該字段表示節區頭(Section Header)大小,一般固定為40.

13、e_shnum字段:該字段表示文件中有幾個節區頭。

14、e_shstrndx字段:該字段是一個數字,這個表明了.shstrtab節區(這個節區存儲著所有節區的名字,例如.text)的節區頭是第幾個。

e_type具體值(相關值后面有英文注釋,這里就不再添加中文注釋了):

解析代碼如下:

struct DataOffest parseSoHeader(FILE *fp,struct DataOffest off)

{

Elf32_Ehdr header;

int i = 0;

fseek(fp,0,SEEK_SET);

fread(&header,1,sizeof(header),fp);

printf("ELF Header:\n");

printf(" Header Magic: ");

for (i = 0; i < 16; i++)

{

printf("%02x ",header.e_ident[i]);

}

printf("\n");

printf(" So File Type: 0x%02x",header.e_type);

switch (header.e_type)

{

case 0x00:

printf("(No file type)\n");

break;

case 0x01:

printf("(Relocatable file)\n");

break;

case 0x02:

printf("(Executable file)\n");

break;

case 0x03:

printf("(Shared object file)\n");

break;

case 0x04:

printf("(Core file)\n");

break;

case 0xff00:

printf("(Beginning of processor-specific codes)\n");

break;

case 0xffff:

printf("(Processor-specific)\n");

break;

default:

printf("\n");

break;

}

printf(" Required Architecture: 0x%04x",header.e_machine);

if (header.e_machine == 0x28)

{

printf("(ARM)\n");

}

else

{

printf("\n");

}

printf(" Version: 0x%02x\n",header.e_version);

printf(" Start Program Address: 0x%08x\n",header.e_entry);

printf(" Program Header Offest: 0x%08x\n",header.e_phoff);

off.programheadoffset = header.e_phoff;

printf(" Section Header Offest: 0x%08x\n",header.e_shoff);

off.sectionheadoffest = header.e_shoff;

printf(" Processor-specific Flags: 0x%08x\n",header.e_flags);

printf(" ELF Header Size: 0x%04x\n",header.e_ehsize);

printf(" Size of an entry in the program header table: 0x%04x\n",header.e_phentsize);

printf(" Program Header Size: 0x%04x\n",header.e_phnum);

off.programsize = header.e_phnum;

printf(" Size of an entry in the section header table: 0x%04x\n",header.e_shentsize);

printf(" Section Header Size: 0x%04x\n",header.e_shnum);

off.sectionsize = header.e_shnum;

printf(" String Section Index: 0x%04x\n",header.e_shstrndx);

off.shstrtabindex = header.e_shstrndx;

return off;

}

四、程序頭(Program Header)解析

程序頭在elf.h文件中的數據格式是Elf32_Phdr,如下圖所示:

每個字段解釋如下:

1、p_type字段:該字段表明了段(Segment)類型,例如PT_LOAD類型,具體值看下圖,實在有點多,沒辦法這里寫完。

2、p_offest字段:該字段表明了這個段在該so文件的起始地址。

3、p_vaddr字段:該字段指明了加載進內存后的虛擬地址,我們靜態解析時用不到該字段。

4、p_paddr字段:該字段指明加載進內存后的實際物理地址,跟上面的那個字段一樣,解析時用不到。

5、p_filesz字段:該字段表明了這個段的大小,單位為字節。

6、p_memsz字段:該字段表明了這個段加載到內存后使用的字節數。

7、p_flags字段:該字段跟elf頭部的e_flags一樣,指明了該段的屬性,是可讀還是可寫。

8、p_align字段:該字段用來指明在內存中對齊字節數的。

p_type字段具體取值:

解析代碼:

struct DataOffest parseSoPargramHeader(FILE *fp,struct DataOffest off)

{

Elf32_Half init;

Elf32_Half addr;

int i;

Elf32_Phdr programHeader;

init = off.programheadoffset;

for (i = 0; i < off.programsize; i++)

{

addr = init + (i * 0x20);

fseek(fp,addr,SEEK_SET);

fread(&programHeader,1,32,fp);

switch (programHeader.p_type)

{

case 2:

off.dynameicoff = programHeader.p_offset;

off.dynameicsize = programHeader.p_filesz;

break;

default:

break;

}

printf("\n\nSegment Header %d:\n",(i + 1));

printf(" Type of segment: 0x%08x\n",programHeader.p_type);

printf(" Segment Offset: 0x%08x\n",programHeader.p_offset);

printf(" Virtual address of beginning of segment: 0x%08x\n",programHeader.p_vaddr);

printf(" Physical address of beginning of segment: 0x%08x\n",programHeader.p_paddr);

printf(" Num. of bytes in file image of segment: 0x%08x\n",programHeader.p_filesz);

printf(" Num. of bytes in mem image of segment (may be zero): 0x%08x\n",programHeader.p_memsz);

printf(" Segment flags: 0x%08x\n",programHeader.p_flags);

printf(" Segment alignment constraint: 0x%08x\n",programHeader.p_align);

}

return off;

}

五、節區頭(Section Header)解析

節區頭在elf.h文件中的數據結構為Elf32_Shdr,如下圖所示:

每個字段解釋如下:

1、sh_name字段:該字段是一個索引值,是.shstrtab表(節區名字字符串表)的索引,指明了該節區的名字。

2、sh_type字段:該字段表明該節區的類型,例如值為SHT_PROGBITS,則該節區可能是.text或者.rodata,至于具體怎么區分,當然看sh_name字段。具體取值看下圖。

3、sh_flags字段:跟上面的一樣,就不再細說了。

4、sh_addr字段:該字段是一個地址,是該節區加載進內存后的地址。

5、sh_offset字段:該字段也是一個地址,是該節區在該so文件中的偏移地址。

6、sh_size字段:該字段表明了該節區的大小,單位是字節。

7、sh_link和sh_info字段:這兩個字段只適用于少數節區,我們這里解析用不到,感興趣的可以去看官方文檔。

8、sh_addralign字段:該字段指明在內存中的對齊字節。

9、sh_entsize字段:該字段指明了該節區中每個項占用的字節數。

sh_type取值:

解析代碼:

struct DataOffest parseSoSectionHeader(FILE *fp,struct DataOffest off,struct ShstrtabTable StrList[100])

{

Elf32_Half init;

Elf32_Half addr;

Elf32_Shdr sectionHeader;

int i,id,n;

char ch;

int k = 0;

init = off.sectionheadoffest;

for (i = 0; i < off.sectionsize; i++)

{

addr = init + (i * 0x28);

fseek(fp,addr,SEEK_SET);

fread(&sectionHeader,1,40,fp);

switch (sectionHeader.sh_type)

{

case 2:

off.symtaboff = sectionHeader.sh_offset;

off.symtabsize = sectionHeader.sh_size;

break;

case 3:

if(k == 0)

{

off.stroffset = sectionHeader.sh_offset;

off.strsize = sectionHeader.sh_size;

k++;

}

else if (k == 1)

{

off.str1offset = sectionHeader.sh_offset;

off.str1size = sectionHeader.sh_size;

k++;

}

else

{

off.str2offset = sectionHeader.sh_offset;

off.str2size = sectionHeader.sh_size;

k++;

}

break;

default:

break;

}

id = sectionHeader.sh_name;

printf("\n\nSection Header %d\n",(i + 1));

printf(" Section Name: ");

for (n = 0; n < 50; n++)

{

ch = StrList[id].str[n];

if (ch == 0)

{

printf("\n");

break;

}

else

{

printf("%c",ch);

}

}

printf(" Section Type: 0x%08x\n",sectionHeader.sh_type);

printf(" Section Flag: 0x%08x\n",sectionHeader.sh_flags);

printf(" Address where section is to be loaded: 0x%08x\n",sectionHeader.sh_addr);

printf(" Offset: 0x%x\n",sectionHeader.sh_offset);

printf(" Size of section, in bytes: 0x%08x\n",sectionHeader.sh_size);

printf(" Section type-specific header table index link: 0x%08x\n",sectionHeader.sh_link);

printf(" Section type-specific extra information: 0x%08x\n",sectionHeader.sh_info);

printf(" Section address alignment: 0x%08x\n",sectionHeader.sh_addralign);

printf(" Size of records contained within the section: 0x%08x\n",sectionHeader.sh_entsize);

}

return off;

}

六、字符串節區解析

PS:從這里開始網上的參考資料很少了,特別是參考代碼,所以有錯誤的地方還請斧正;因為以后的so加固等只涉及到幾個節區,所以只解析了.shstrtab、.strtab、.dynstr、.text、.symtab、.dynamic節區!!!

在elf頭部中有個e_shstrndx字段,該字段指明了.shstrtab節區頭部是文件中第幾個節區頭部,我們可以根據這找到.shstrtab節區的偏移地址,然后讀取出來,就可以為每個節區名字賦值了,然后就可以順著鎖定剩下的兩個字符串節區。

在elf文件中,字符串表示方式如下:字符串的頭部和尾部用標示字節00標志,同時上一個字符串尾部標識符00作為下一個字符串頭部標識符。例如我有兩個緊鄰的字符串分別是a和b,那么他們在elf文件中16進制為00 97 00 98 00。

解析代碼如下(PS:因為編碼問題,第一次打印字符串表沒問題,但填充進sh_name就亂碼,所以這里只放上解析.shstrtab的代碼,但剩下兩個節區節區代碼一樣):

void parseStrSection(FILE *fp,struct DataOffest off,int flag)

{

int total = 0;

int i;

int ch;

int mark;

Elf32_Off init;

Elf32_Off addr;

Elf32_Word count;

mark = 1;

if (flag == 1)

{

count = off.strsize;

init = off.stroffset;

}

else if (flag == 2)

{

count = off.str1size;

init = off.str1offset;

}

else

{

count = off.str2size;

init = off.str2offset;

}

printf("String Address==>0x%x\n",init);

printf("String List %d:\n\t[1]==>",flag);

for (i = 0; i < count; i++)

{

addr = init + (i * 1);

fseek(fp,addr,SEEK_SET);

fread(&ch,1,1,fp);

if (i == 0 && ch == 0)

{

continue;

}

else if (ch != 0)

{

printf("%c",ch);

}

else if (ch == 0 && i !=0)

{

printf("\n\t[%d]==>",(++mark));

}

}

printf("\n");

}

七、.dynamic解析

.dynamic在elf.h文件中的數據結構是Elf32-Dyn,如下圖所示:

第一個字段表明了類型,占4個字節;第二個字段是一個共用體,也占四個字節,描述了具體的項信息。解析代碼如下:

void parseSoDynamicSection(FILE *fp,struct DataOffest off)

{

int dynamicnum;

Elf32_Off init;

Elf32_Off addr;

Elf32_Dyn dynamicData;

int i;

init = off.dynameicoff;

dynamicnum = (off.dynameicsize / 8);

printf("Dynamic:\n");

printf("\t\tTag\t\t\tType\t\t\tName/Value\n");

for (i = 0; i < dynamicnum; i++)

{

addr = init + (i * 8);

fseek(fp,addr,SEEK_SET);

fread(&dynamicData,1,8,fp);

printf("\t\t0x%08x\t\tNOPRINTF\t\t0x%x\n",dynamicData.d_tag,dynamicData.d_un);

}

}

八、.symtab解析

該節區是該so文件的符號表,它在elf.h文件中的數據結構是Elf32_Sym,如下所示:

每個字段解釋如下:

1、st_name字段:該字段是一個索引值,指明了該項的名字。

2、st_value字段:該字段表明了相關聯符號的取值。

3、stz-size字段:該字段指明了每個項所占用的字節數。

4、st_info和st_other字段:這兩個字段指明了符號的類型。

5、st_shndx字段:相關索引。

解析代碼如下(PS:由于亂碼問題,索引手動固定了地址測試,有興趣的挨個解析字符應該可以解決亂碼問題):

void parseSoDynamicSection(FILE *fp,struct DataOffest off)

{

int dynamicnum;

Elf32_Off init;

Elf32_Off addr;

Elf32_Dyn dynamicData;

int i;

init = off.dynameicoff;

dynamicnum = (off.dynameicsize / 8);

printf("Dynamic:\n");

printf("\t\tTag\t\t\tType\t\t\tName/Value\n");

for (i = 0; i < dynamicnum; i++)

{

addr = init + (i * 8);

fseek(fp,addr,SEEK_SET);

fread(&dynamicData,1,8,fp);

printf("\t\t0x%08x\t\tNOPRINTF\t\t0x%x\n",dynamicData.d_tag,dynamicData.d_un);

}

}

void parseSymtabSection(FILE *fp,struct DataOffest off)

{

Elf32_Off init;

Elf32_Off addr;

Elf32_Word count;

Elf32_Sym symtabSection;

int k,i;

init = off.symtaboff;

count = off.symtabsize;

printf("SymTable:\n");

for (i = 0; i < count; i++)

{

addr = init + (i * 16);

fseek(fp,addr,SEEK_SET);

fread(&symtabSection,1,16,fp);

printf("Symbol Name Index: 0x%x\n",symtabSection.st_name);

printf("Value or address associated with the symbol: 0x%08x\n",symtabSection.st_value);

printf("Size of the symbol: 0x%x\n",symtabSection.st_size);

printf("Symbol's type and binding attributes: %c\n",symtabSection.st_info);

printf("Must be zero; reserved: 0x%x\n",symtabSection.st_other);

printf("Which section (header table index) it's defined in: 0x%x\n",symtabSection.st_shndx);

}

}

九、.text解析

PS:這部分沒代碼了,只簡單解析一下,因為解析arm指令太麻煩了,估計得寫個半年都不一定能搞定,后續寫了會同步更新在github!!!

.text節區存儲著可執行指令,我們可以通過節區頭部的名字鎖定.text的偏移地址和大小,找到該節區后,我們會發現這個節區存儲的就是arm機器碼,直接照著指令集翻譯即可,沒有其他的結構。通過ida驗證如下:

十、代碼測試相關截圖

十一、frida反調試和后序

frida反調試最簡單的就是檢查端口,檢查進程名,檢查so文件等,但最準確以及最復雜的是檢查匯編指令,我們知道frida是通過一個大調整實現hook,而跳轉的指令就那么幾條,我們是否可以通過檢查每個函數第一條指令來判斷是否有frida了!!!(ps:簡單寫一下原理,拉開寫就太多了,這里感謝某大佬和我討論的這個話題!!!)

本來因為這個so文件解析要寫到明年去了,沒想到看起來代碼量大,但實際要用到的地方代碼量很少。。。

總結

以上是生活随笔為你收集整理的awx文件解析_Android so(ELF)文件解析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。