




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
【移動應(yīng)用開發(fā)技術(shù)】AndroidLinker與SO加殼技術(shù)之上篇
1.前言Android系統(tǒng)安全愈發(fā)重要,像傳統(tǒng)pc安全的可執(zhí)行文件加固一樣,應(yīng)用加固是Android系統(tǒng)安全中非常重要的一環(huán)。目前Android應(yīng)用加固可以分為dex加固和Native加固,Native加固的保護(hù)對象為Native層的SO文件,使用加殼、反調(diào)試、混淆、VM等手段增加SO文件的反編譯難度。目前最主流的SO文件保護(hù)方案還是加殼技術(shù),在SO文件加殼和脫殼的***技術(shù)領(lǐng)域,最重要的基礎(chǔ)的便是對于Linker即裝載鏈接機(jī)制的理解。對于非安全方向開發(fā)者,深刻理解系統(tǒng)的裝載與鏈接機(jī)制也是進(jìn)階的必要條件。本文詳細(xì)分析了Linker對SO文件的裝載和鏈接過程,最后對SO加殼的關(guān)鍵技術(shù)進(jìn)行了簡要的介紹。對于Linker的學(xué)習(xí),還應(yīng)該包括Linker自舉、可執(zhí)行文件的加載等技術(shù),但是限于本人的技術(shù)水平,本文的討論范圍限定在SO文件的加載,也就是在調(diào)用dlopen("libxx.SO")之后,Linker的處理過程。本文基于Android5.0AOSP源碼,僅針對ARM平臺,為了增強(qiáng)可讀性,文中列舉的源碼均經(jīng)過刪減,去除了其他CPU架構(gòu)的相關(guān)源碼以及錯(cuò)誤處理。另:閱讀本文的讀者需要對ELF文件結(jié)構(gòu)有一定的了解。2.SO的裝載與鏈接2.1整體流程說明1.do_dlopen調(diào)用dl_open后,中間經(jīng)過dlopen_ext,到達(dá)第一個(gè)主要函數(shù)do_dlopen:soinfo*do_dlopen(constchar*name,intflags,constAndroid_dlextinfo*extinfo){
protect_data(PROT_READ|PROT_WRITE);
soinfo*si=find_library(name,flags,extinfo);//查找SO
if(si!=NULL){
si->CallConstructors();//調(diào)用SO的init函數(shù)
}
protect_data(PROT_READ);
returnsi;}do_dlopen調(diào)用了兩個(gè)重要的函數(shù),第一個(gè)是find_library,第二個(gè)是soinfo的成員函數(shù)CallConstructors,find_library函數(shù)是SO裝載鏈接的后續(xù)函數(shù),完成SO的裝載鏈接后,通過CallConstructors調(diào)用SO的初始化函數(shù)。2.find_library_internalfind_library直接調(diào)用了find_library_internal,下面直接看find_library_internal函數(shù):staticsoinfo*find_library_internal(constchar*name,intdlflags,constAndroid_dlextinfo*extinfo){
if(name==NULL){
returnsomain;
}
soinfo*si=find_loaded_library_by_name(name);
//判斷SO是否已經(jīng)加載
if(si==NULL){
TRACE("['%s'hasnotbeenfoundbyname.
Tryingharder...]",name);
si=load_library(name,dlflags,extinfo);
//繼續(xù)SO的加載流程
}
if(si!=NULL&&(si->flags&FLAG_LINKED)==0){
DL_ERR("recursivelinkto\"%s\"",si->name);
returnNULL;
}
returnsi;}find_library_internal首先通過find_loaded_library_by_name函數(shù)判斷目標(biāo)SO是否已經(jīng)加載,如果已經(jīng)加載則直接返回對應(yīng)的soinfo指針,沒有加載的話則調(diào)用load_library繼續(xù)加載流程,下面看load_library函數(shù)。3.load_librarystaticsoinfo*load_library(constchar*name,intdlflags,constAndroid_dlextinfo*extinfo){
intfd=-1;
...
//Openthefile.
fd=open_library(name);
//打開SO文件,獲得文件描述符fd
ElfReaderelf_reader(name,fd);
//創(chuàng)建ElfReader對象
...
//ReadtheELFheaderandloadthesegments.
if(!elf_reader.Load(extinfo)){
//使用ElfReader的Load方法,完成SO裝載
returnNULL;
}
soinfo*si=soinfo_alloc(SEARCH_NAME(name),&file_stat);
//為SO分配新的soinfo結(jié)構(gòu)
if(si==NULL){
returnNULL;
}
si->base=elf_reader.load_start();
//根據(jù)裝載結(jié)果,更新soinfo的成員變量
si->size=elf_reader.load_size();
si->load_bias=elf_reader.load_bias();
si->phnum=elf_reader.phdr_count();
si->phdr=elf_reader.loaded_phdr();
...
if(!soinfo_link_p_w_picpath(si,extinfo)){
//調(diào)用soinfo_link_p_w_picpath完成SO的鏈接過程
soinfo_free(si);
returnNULL;
}
returnsi;}load_library函數(shù)呈現(xiàn)了SO裝載鏈接的整個(gè)流程,主要有3步:裝載:創(chuàng)建ElfReader對象,通過ElfReader對象的Load方法將SO文件裝載到內(nèi)存分配soinfo:調(diào)用soinfo_alloc函數(shù)為SO分配新的soinfo結(jié)構(gòu),并按照裝載結(jié)果更新相應(yīng)的成員變量鏈接:調(diào)用soinfo_link_p_w_picpath完成SO的鏈接通過前面的分析,可以看到,load_library函數(shù)中包含了SO裝載鏈接的主要過程,后文主要通過分析ElfReader類和soinfo_link_p_w_picpath函數(shù),來分別介紹SO的裝載和鏈接過程。2.2裝載在load_library中,首先初始化elf_reader對象,第一個(gè)參數(shù)為SO的名字,第二個(gè)參數(shù)為文件描述符fd:ElfReaderelf_reader(name,fd)之后調(diào)用ElfReader的load方法裝載SO。
...
//ReadtheELFheaderandloadthesegments.
if(!elf_reader.Load(extinfo)){
returnNULL;
}
...ElfReader::Load方法如下:boolElfReader::Load(constAndroid_dlextinfo*extinfo){
returnReadElfHeader()&&
//讀取elfheader
VerifyElfHeader()&&
//驗(yàn)證elfheader
ReadProgramHeader()&&
//讀取programheader
ReserveAddressSpace(extinfo)&&//分配空間
LoadSegments()&&
//按照programheader指示裝載segments
FindPhdr();
//找到裝載后的phdr地址}ElfReader::Load方法首先讀取SO的elfheader,再對elfheader進(jìn)行驗(yàn)證,之后讀取programheader,根據(jù)programheader計(jì)算SO需要的內(nèi)存大小并分配相應(yīng)的空間,緊接著將SO按照以segment為單位裝載到內(nèi)存,最后在裝載到內(nèi)存的SO中找到programheader,方便之后的鏈接過程使用。下面深入ElfReader的這幾個(gè)成員函數(shù)進(jìn)行詳細(xì)介紹。2.2.1read&verifyelfheaderboolElfReader::ReadElfHeader(){
ssize_trc=read(fd_,&header_,sizeof(header_));
if(rc!=sizeof(header_)){
returnfalse;
}
returntrue;}ReadElfHeader使用read直接從SO文件中將elfheader讀取header
中,header_為ElfReader的成員變量,類型為Elf32_Ehdr,通過header可以方便的訪問elfheader中各個(gè)字段,elfheader中包含有programheadertable、sectionheadertable等重要信息。對elfheader的驗(yàn)證包括:magic字節(jié)32/64bit與當(dāng)前平臺是否一致大小端類型:可執(zhí)行文件、SO…版本:一般為1,表示當(dāng)前版本平臺:ARM、x86、amd64…有任何錯(cuò)誤都會導(dǎo)致加載失敗。2.2.2ReadProgramHeaderboolElfReader::ReadProgramHeader(){
phdr_num_=header_.e_phnum;
//programheader數(shù)量
//mmap要求頁對齊
ElfW(Addr)page_min=PAGE_START(header_.e_phoff);
ElfW(Addr)page_max=PAGE_END(header_.e_phoff+(phdr_num_*sizeof(ElfW(Phdr))));
ElfW(Addr)page_offset=PAGE_OFFSET(header_.e_phoff);
phdr_size_=page_max-page_min;
//使用mmap將programheader映射到內(nèi)存
void*mmap_result=mmap(NULL,phdr_size_,PROT_READ,MAP_PRIVATE,fd_,page_min);
phdr_mmap_=mmap_result;
//ElfReader的成員變量phdr_table_指向programheadertable
phdr_table_=reinterpret_cast<ElfW(Phdr)*>(reinterpret_cast<char*>(mmap_result)+page_offset);
returntrue;}將programheader在內(nèi)存中單獨(dú)映射一份,用于解析programheader時(shí)臨時(shí)使用,在SO裝載到內(nèi)存后,便會釋放這塊內(nèi)存,轉(zhuǎn)而使用裝載后的SO中的programheader。2.2.3reservespace&計(jì)算loadsizeboolElfReader::ReserveAddressSpace(constAndroid_dlextinfo*extinfo){
ElfW(Addr)min_vaddr;
//計(jì)算加載SO需要的空間大小
load_size_=phdr_table_get_load_size(phdr_table_,phdr_num_,&min_vaddr);
//min_vaddr一般情況為零,如果不是則表明SO指定了加載基址
uint8_t*addr=reinterpret_cast<uint8_t*>(min_vaddr);
void*start;
intmmap_flags=MAP_PRIVATE|MAP_ANONYMOUS;
start=mmap(addr,load_size_,PROT_NONE,mmap_flags,-1,0);
load_start_=start;
load_bias_=reinterpret_cast<uint8_t*>(start)-addr;
returntrue;}首先調(diào)用phdr_table_get_load_size函數(shù)獲取SO在內(nèi)存中需要的空間load_size,然后使用mmap匿名映射,預(yù)留出相應(yīng)的空間。
關(guān)于loadbias:SO可以指定加載基址,但是SO指定的加載基址可能不是頁對齊的,這種情況會導(dǎo)致實(shí)際映射地址和指定的加載地址有一個(gè)偏差,這個(gè)偏差便是
load_bias_,之后在針對虛擬地址進(jìn)行計(jì)算時(shí)需要使用
load_bias_
修正。普通的SO都不會指定加載基址,這時(shí)min_vaddr=0,則
load_bias_=load_start_,即load_bias_
等于加載基址,下文會將load_bias_
直接稱為基址。
下面深入phdr_table_get_load_size分析一下load_size的計(jì)算:使用成員變量phdr_table遍歷所有的programheader,找到所有類型為PT_LOAD的segment的p_vaddr的最小值,p_vaddr+p_memsz的最大值,分別作為min_vaddr和max_vaddr,在將兩個(gè)值分別對齊到頁首和頁尾,最終使用對齊后的max_vaddr-min_vaddr得到load_size。size_tphdr_table_get_load_size(constElfW(Phdr)*phdr_table,size_tphdr_count,
ElfW(Addr)*out_min_vaddr,
ElfW(Addr)*out_max_vaddr){
ElfW(Addr)min_vaddr=UINTPTR_MAX;
ElfW(Addr)max_vaddr=0;
boolfound_pt_load=false;
for(size_ti=0;i<phdr_count;++i){
constElfW(Phdr)*phdr=&phdr_table[i];
if(phdr->p_type!=PT_LOAD){
continue;
}
found_pt_load=true;
if(phdr->p_vaddr<min_vaddr){
min_vaddr=phdr->p_vaddr;
//記錄最小的虛擬地址
}
if(phdr->p_vaddr+phdr->p_memsz>max_vaddr){
max_vaddr=phdr->p_vaddr+phdr->p_memsz;
//記錄最大的虛擬地址
}
}
if(!found_pt_load){
min_vaddr=0;
}
min_vaddr=PAGE_START(min_vaddr);
//頁對齊
max_vaddr=PAGE_END(max_vaddr);
//頁對齊
if(out_min_vaddr!=NULL){
*out_min_vaddr=min_vaddr;
}
if(out_max_vaddr!=NULL){
*out_max_vaddr=max_vaddr;
}
returnmax_vaddr-min_vaddr;
//load_size=max_vaddr-min_vaddr}2.2.4LoadSegments遍歷programheadertable,找到類型為PT_LOAD的segment:計(jì)算segment在內(nèi)存空間中的起始地址segstart和結(jié)束地址seg_end,seg_start等于虛擬偏移加上基址load_bias,同時(shí)由于mmap的要求,都要對齊到頁邊界得到seg_page_start和seg_page_end。計(jì)算segment在文件中的頁對齊后的起始地址file_page_start和長度file_length。使用mmap將segment映射到內(nèi)存,指定映射地址為seg_page_start,長度為file_length,文件偏移為file_page_start。boolElfReader::LoadSegments(){
for(size_ti=0;i<phdr_num_;++i){
constElfW(Phdr)*phdr=&phdr_table_[i];
if(phdr->p_type!=PT_LOAD){
continue;
}
//Segment在內(nèi)存中的地址.
ElfW(Addr)seg_start=phdr->p_vaddr+load_bias_;
ElfW(Addr)seg_end
=seg_start+phdr->p_memsz;
ElfW(Addr)seg_page_start=PAGE_START(seg_start);
ElfW(Addr)seg_page_end
=PAGE_END(seg_end);
ElfW(Addr)seg_file_end
=seg_start+phdr->p_filesz;
//文件偏移
ElfW(Addr)file_start=phdr->p_offset;
ElfW(Addr)file_end
=file_start+phdr->p_filesz;
ElfW(Addr)file_page_start=PAGE_START(file_start);
ElfW(Addr)file_length=file_end-file_page_start;
if(file_length!=0){
//將文件中的segment映射到內(nèi)存
void*seg_addr=mmap(reinterpret_cast<void*>(seg_page_start),
file_length,
PFLAGS_TO_PROT(phdr->p_flags),
MAP_FIXED|MAP_PRIVATE,
fd_,
file_page_start);
}
//如果segment可寫,并且沒有在頁邊界結(jié)束,那么就將segemntend到頁邊界的內(nèi)存清零。
if((phdr->p_flags&PF_W)!=0&&PAGE_OFFSET(seg_file_end)>0){
memset(reinterpret_cast<void*>(seg_file_end),0,PAGE_SIZE-PAGE_OFFSET(seg_file_end));
}
seg_file_end=PAGE_END(seg_file_end);
//將(內(nèi)存長度-文件長度)對應(yīng)的內(nèi)存進(jìn)行匿名映射
if(seg_page_end>seg_file_end){
void*zeromap=mmap(reinterpret_cast<void*>(seg_file_end),
seg_page_end-seg_file_end,
PFLAGS_TO_PROT(phdr->p_flags),
MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE,
-1,
0);
}
}
returntrue;}2.3分配soinfoload_library在調(diào)用load_segments完成裝載后,接著調(diào)用soinfo_alloc函數(shù)為目標(biāo)SO分配soinfo,soinfo_alloc函數(shù)實(shí)現(xiàn)如下:staticsoinfo*soinfo_alloc(constchar*name,structstat*file_stat){
soinfo*si=g_soinfo_allocator.alloc();
//分配空間,可以簡單理解為malloc
//Initializethenewelement.
memset(si,0,sizeof(soinfo));
strlcpy(si->name,name,sizeof(si->name));
si->flags=FLAG_NEW_SOINFO;
sonex
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 中介押金合同范本
- 2025年漳州貨運(yùn)準(zhǔn)駕證模擬考試
- 醫(yī)院器械采購合同范本
- 加工類協(xié)議合同范本
- 辦公窗簾購銷合同范本
- 村級采購合同范本
- 代銷鋪貨合同范本
- 買賣合同和貨運(yùn)合同范本
- 專利轉(zhuǎn)讓英文合同范例
- 北京不備案施工合同范本
- 專題13《竹里館》課件(共28張ppt)
- 團(tuán)意操作流程詳解課件
- SH/T 0356-1996燃料油
- GB/T 9846.4-2004膠合板第4部分:普通膠合板外觀分等技術(shù)條件
- GB/T 17836-1999通用航空機(jī)場設(shè)備設(shè)施
- GB/T 13012-2008軟磁材料直流磁性能的測量方法
- 2023年全國高中生物聯(lián)賽競賽試題和答案
- 第1課中華優(yōu)秀傳統(tǒng)文化的內(nèi)涵與特點(diǎn)課件(共28張PPT)
- 小學(xué)語文中高學(xué)段單元整體教學(xué)的實(shí)踐研究課題中期報(bào)告
- 《木蘭詩》第二課時(shí)(公開課)課件
- 核電項(xiàng)目人橋吊車抗震計(jì)算書版
評論
0/150
提交評論