前一陣子在研究so加固,發現其中涉及自實現的Linker加載so的技術,而我對此知之什少,因此只好先來學習下Linker的加載流程。
本文參考AOSP源碼和r0ysue大佬的文章 ( 不知為何文中給出的那個demo我一直跑不起來 )來實現一個簡單的自實現Linker Demo。
環境:Pixel1XL
、AOSP - Oreo - 8.1.0_r81
Linker在加載so時大致可以分成五步:
利用open
+mmap
來將待加載的so文件映射到內存空間,存放在start_addr_
中。然後調用Read
函數來獲取ehdr、phdr等信息。
Read
函數實現如下,調用ReadElfHeader
和ReadProgramHeaders
來讀取ehdr和phdr。
AOSP源碼的Read
中還會讀取Section Headers和Dynamic節,一開始我也有實現這部份的邏輯,但後來發現讀取後的信息根本沒有被用到,因此就把這部份給刪了。
ReadElfHeader
的實現如下,直接通過memcpy
來賦值。
ReadProgramHeaders
的實現直接copy源碼就可以,本質上還是內存映射的過程。
調用Load
來載入so。
Load
的實現如下:
ReserveAddressSpace
用於生成一片新的內存空間,之後的操作基本上都是在這片內存空間進行。LoadSegments
、FindPhdr
用於將待加載so的對應信息填充到此內存空間。
最後要修正so,將當前so修正為待加載的so,這部份放到後面來解析。
ReserveAddressSpace
的具體實現如下,先計算出load_size_
後mmap
一片內存,在我這個demo中min_vaddr
是0
,因此load_start_ == load_bias_
,load_bias_
代表的就是這片內存,而這片內存是用來存放待加載的so。
LoadSegments
的具體實現如下,遍歷Program Header Table將所有type為PT_LOAD
的段加載進內存,源碼中是采用mmap
來映射,但我嘗試後發現會有權限問題,因而采用memcpy
的方案。
FindPhdr
的具體實現如下,簡單來說就是將Phdr信息填充進load_bias_
那片內存。
Load
函數最後是在對soinfo的修正,將當前so( 加載器 )修正為待加載的so。AOSP源碼中的si_
是通過特定方法new出來的全新soinfo,而我看大多數文章都是獲取當前so作為si_
,然後修正其中的信息。
本來是想嘗試按AOSP源碼那樣new一個soinfo看看結果有什麼不同,但最終被soinfo結構的複雜性勸退了。
修正so的第一步是要獲取當前so的soinfo對象,從這篇文章 發現find_containing_library
這個函數,似乎可以一步到位直接獲取soinfo對象。該函數位於linker64
中,將它拉入IDA,能直接搜尋到該函數,這意味著能夠「借用」這個函數。
想要「借用」linker64
裡的find_containing_library
,需要知道linker64
在內存的基址和find_containing_library
的函數偏移( 相對基址的偏移 ),前者可以通過遍歷/proc/self/maps
來取得,而後者的獲取有以下兩種思路:
成功獲取find_containing_library
地址後,強轉成FunctionPtr
函數指針後即可調用,參數為當前so的地址( 同樣是遍歷maps取得的 ),最終會返回當前so的soinfo對象。
get_export_func
的實現如下,主要依賴於elf的文件結構,可以參考下我之前寫的文章 ,大致原理如下:
成功獲取si_
後要修改其對應屬性。在這裡我遇到一個很玄學的問題,就是一開始不知為什麼死活修改不了si_
的屬性,一改就會報內存讀寫的錯,即使mprotect
賦予可讀可寫權限也無用,嘗試了各種方法都無用,在這卡了我好幾天,直到某次重啟手機後就突然好了???
soinfo結構體定義在bionic/linker/linker_soinfo.h
中。
將它copy到本地後會有很多報錯,一開始我是將那些沒有用到又報紅的直接刪掉,但後來發現這樣做會間接導致最後發生「android linker java.lang.unsatisfiedlinkerror: no implementation found for XXX
」的錯誤( 這個錯誤我排查了很久很久,最終才發現是soinfo結構的問題,果然細節決定成敗…… )。
正確的做法是必須要保留所有的成員變量( 即使該變量用不到也要留下來占位 ),函數由於不占空間可以隨便刪掉。
預鏈接,主要是在遍歷.dynamic
節獲取各種動態信息並保存在修正後的soinfo中。
prelink_image
的具體實現太長( 基本上是copy源碼的 )就不展示了,比較大的改動是在DT_NEEDED
時手動保存對應的依賴庫,之後重定向時會用到。
link_image
裡處理重定向信息。
link_image
的實現如下,android_relocs_
的重定向我沒有處理( 嘗試處理過,但有點問題就刪了 ),好像問題不大?
之後調用relocate
對rela_
和plt_rela_
的內容進行重定向。
relocate
函數的實現如下,在重定位時最需要確定的就是目標函數的真實地址。
這裡采用一種偷懶的方式,直接遍歷所有依賴庫( 之前保存在myneed
中 ),調用dlopen
+dlsym
查找對應函數地址,找到的結果會保存在sym_addr
中,後續再根據type
來決定重定位的方式;而如果遍歷完所有依賴庫都沒有找到,則嘗試從symtab_[sym].st_value
裡獲取。
調用soinfo的構建函數:.init
和.init_array
內所有函數
原版Linker在調用.init
和.init_array
時傳入的是0, nullptr, nullptr
,我這裡與其保持一致。
項目地址:https://github.com/ngiokweng/ng1ok-linker
隨便寫一個so作為待加載的so( 名為libdemo1.so
),內容如下,將它push到/data/local/tmp
。
Demo的用例如下,實例化MyLoader
,調用run
函數加載指定路徑的so。
Java層的onCreate
如下,在test
之後調用待加載so裡的demo1Func
函數。
輸出如下,大功告成~
前前後後弄了兩、三周的時間,最終總算是弄好了這一個小Demo。自知該Demo仍有很多不足之處( 如無法捕獲try…catch ),而且只經過簡單的測試,定然存在諸多的BUG,歡迎各位大佬的指正!!有任何也問題歡迎評論,或者私聊我/找我聊聊天都可以!!!
int
fd;
struct
stat sb;
fd = open(path, O_RDONLY);
fstat(fd, &sb);
start_addr_ =
static_cast
<
void
**>(mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0));
if
(!Read(path, fd, 0, sb.st_size)){
LOGD(
"Read so failed"
);
munmap(start_addr_, sb.st_size);
close(fd);
}
int
fd;
struct
stat sb;
fd = open(path, O_RDONLY);
fstat(fd, &sb);
start_addr_ =
static_cast
<
void
**>(mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0));
if
(!Read(path, fd, 0, sb.st_size)){
LOGD(
"Read so failed"
);
munmap(start_addr_, sb.st_size);
close(fd);
}
bool
MyLoader::Read(
const
char
* name,
int
fd, off64_t file_offset, off64_t file_size) {
bool
res =
false
;
name_ = name;
fd_ = fd;
file_offset_ = file_offset;
file_size_ = file_size;
if
(ReadElfHeader() &&
ReadProgramHeaders()) {
res =
true
;
}
return
res;
}
bool
MyLoader::Read(
const
char
* name,
int
fd, off64_t file_offset, off64_t file_size) {
bool
res =
false
;
name_ = name;
fd_ = fd;
file_offset_ = file_offset;
file_size_ = file_size;
if
(ReadElfHeader() &&
ReadProgramHeaders()) {
res =
true
;
}
return
res;
}
bool
MyLoader::ReadElfHeader() {
return
memcpy
(&(header_),start_addr_,
sizeof
(header_));
}
bool
MyLoader::ReadElfHeader() {
return
memcpy
(&(header_),start_addr_,
sizeof
(header_));
}
bool
MyLoader::ReadProgramHeaders() {
phdr_num_ = header_.e_phnum;
size_t
size = phdr_num_ *
sizeof
(ElfW(Phdr));
void
* data = Utils::getMapData(fd_, file_offset_, header_.e_phoff, size);
if
(data == nullptr) {
LOGE(
"ProgramHeader mmap failed"
);
return
false
;
}
phdr_table_ =
static_cast
<ElfW(Phdr)*>(data);
return
true
;
}
void
* Utils::getMapData(
int
fd, off64_t base_offset,
size_t
elf_offset,
size_t
size) {
off64_t offset;
safe_add(&offset, base_offset, elf_offset);
off64_t page_min = page_start(offset);
off64_t end_offset;
safe_add(&end_offset, offset, size);
safe_add(&end_offset, end_offset, page_offset(offset));
size_t
map_size =
static_cast
<
size_t
>(end_offset - page_min);
uint8_t* map_start =
static_cast
<uint8_t*>(
mmap64(nullptr, map_size, PROT_READ, MAP_PRIVATE, fd, page_min));
if
(map_start == MAP_FAILED) {
return
nullptr;
}
return
map_start + page_offset(offset);
}
bool
MyLoader::ReadProgramHeaders() {
phdr_num_ = header_.e_phnum;
size_t
size = phdr_num_ *
sizeof
(ElfW(Phdr));
void
* data = Utils::getMapData(fd_, file_offset_, header_.e_phoff, size);
if
(data == nullptr) {
LOGE(
"ProgramHeader mmap failed"
);
return
false
;
}
phdr_table_ =
static_cast
<ElfW(Phdr)*>(data);
return
true
;
}
void
* Utils::getMapData(
int
fd, off64_t base_offset,
size_t
elf_offset,
size_t
size) {
off64_t offset;
safe_add(&offset, base_offset, elf_offset);
off64_t page_min = page_start(offset);
off64_t end_offset;
safe_add(&end_offset, offset, size);
safe_add(&end_offset, end_offset, page_offset(offset));
size_t
map_size =
static_cast
<
size_t
>(end_offset - page_min);
uint8_t* map_start =
static_cast
<uint8_t*>(
mmap64(nullptr, map_size, PROT_READ, MAP_PRIVATE, fd, page_min));
if
(map_start == MAP_FAILED) {
return
nullptr;
}
return
map_start + page_offset(offset);
}
if
(!Load()) {
LOGD(
"Load so failed"
);
munmap(start_addr_, sb.st_size);
close(fd);
}
if
(!Load()) {
LOGD(
"Load so failed"
);
munmap(start_addr_, sb.st_size);
close(fd);
}
bool
MyLoader::Load() {
bool
res =
false
;
if
(ReserveAddressSpace() &&
LoadSegments() &&
FindPhdr()) {
LOGD(
"Load Done........."
);
res =
true
;
}
si_ = Utils::get_soinfo(
"libnglinker.so"
);
if
(!si_) {
LOGE(
"si_ return nullptr"
);
return
false
;
}
LOGD(
"si_ -> base: %lx"
, si_->base);
mprotect((
void
*) PAGE_START(
reinterpret_cast
<ElfW(Addr)>(si_)), 0x1000, PROT_READ | PROT_WRITE);
si_->base = load_start();
si_->size = load_size();
si_->load_bias = load_bias();
si_->phnum = phdr_count();
si_->phdr = loaded_phdr();
return
res;
}
bool
MyLoader::Load() {
bool
res =
false
;
if
(ReserveAddressSpace() &&
LoadSegments() &&
FindPhdr()) {
LOGD(
"Load Done........."
);
res =
true
;
}
si_ = Utils::get_soinfo(
"libnglinker.so"
);
if
(!si_) {
LOGE(
"si_ return nullptr"
);
return
false
;
}
LOGD(
"si_ -> base: %lx"
, si_->base);
mprotect((
void
*) PAGE_START(
reinterpret_cast
<ElfW(Addr)>(si_)), 0x1000, PROT_READ | PROT_WRITE);
si_->base = load_start();
si_->size = load_size();
si_->load_bias = load_bias();
si_->phnum = phdr_count();
si_->phdr = loaded_phdr();
return
res;
}
bool
MyLoader::ReserveAddressSpace() {
ElfW(Addr) min_vaddr;
load_size_ = phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr);
LOGD(
"load_size_: %x"
, load_size_);
if
(load_size_ == 0) {
LOGE(
"\"%s\" has no loadable segments"
, name_.c_str());
return
false
;
}
uint8_t* addr =
reinterpret_cast
<uint8_t*>(min_vaddr);
void
* start;
void
* mmap_hint = nullptr;
start = mmap(mmap_hint, load_size_, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
load_start_ = start;
load_bias_ =
reinterpret_cast
<uint8_t*>(start) - addr;
return
true
;
}
bool
MyLoader::ReserveAddressSpace() {
ElfW(Addr) min_vaddr;
load_size_ = phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr);
LOGD(
"load_size_: %x"
, load_size_);
if
(load_size_ == 0) {
LOGE(
"\"%s\" has no loadable segments"
, name_.c_str());
return
false
;
}
uint8_t* addr =
reinterpret_cast
<uint8_t*>(min_vaddr);
void
* start;
void
* mmap_hint = nullptr;
start = mmap(mmap_hint, load_size_, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
load_start_ = start;
load_bias_ =
reinterpret_cast
<uint8_t*>(start) - addr;
return
true
;
}
bool
MyLoader::LoadSegments() {
for
(
size_t
i = 0; i < phdr_num_; ++i) {
const
ElfW(Phdr)* phdr = &phdr_table_[i];
if
(phdr->p_type != PT_LOAD) {
continue
;
}
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_size_ <= 0) {
LOGE(
"\"%s\" invalid file size: %"
, name_.c_str(), file_size_);
return
false
;
}
if
(file_end >
static_cast
<
size_t
>(file_size_)) {
LOGE(
"invalid ELF file"
);
return
false
;
}
if
(file_length != 0) {
mprotect(
reinterpret_cast
<
void
*>(seg_page_start), seg_page_end - seg_page_start, PROT_WRITE);
void
* c = (
char
*)start_addr_ + file_page_start;
void
* res =
memcpy
(
reinterpret_cast
<
void
*>(seg_page_start), c, file_length);
LOGD(
"[LoadSeg] %s seg_page_start: %lx c : %lx"
,
strerror
(
errno
), seg_page_start, c);
}
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);
if
(seg_page_end > seg_file_end) {
size_t
zeromap_size = seg_page_end - seg_file_end;
void
* zeromap = mmap(
reinterpret_cast
<
void
*>(seg_file_end),
zeromap_size,
PFLAGS_TO_PROT(phdr->p_flags),
MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE,
-1,
0);
if
(zeromap == MAP_FAILED) {
LOGE(
"couldn't zero fill \"%s\" gap: %s"
, name_.c_str(),
strerror
(
errno
));
return
false
;
}
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, zeromap, zeromap_size,
".bss"
);
}
}
return
true
;
}
bool
MyLoader::LoadSegments() {
for
(
size_t
i = 0; i < phdr_num_; ++i) {
const
ElfW(Phdr)* phdr = &phdr_table_[i];
if
(phdr->p_type != PT_LOAD) {
continue
;
}
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_size_ <= 0) {
LOGE(
"\"%s\" invalid file size: %"
, name_.c_str(), file_size_);
return
false
;
}
if
(file_end >
static_cast
<
size_t
>(file_size_)) {
LOGE(
"invalid ELF file"
);
return
false
;
}
if
(file_length != 0) {
mprotect(
reinterpret_cast
<
void
*>(seg_page_start), seg_page_end - seg_page_start, PROT_WRITE);
void
* c = (
char
*)start_addr_ + file_page_start;
void
* res =
memcpy
(
reinterpret_cast
<
void
*>(seg_page_start), c, file_length);
LOGD(
"[LoadSeg] %s seg_page_start: %lx c : %lx"
,
strerror
(
errno
), seg_page_start, c);
}
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);
if
(seg_page_end > seg_file_end) {
size_t
zeromap_size = seg_page_end - seg_file_end;
void
* zeromap = mmap(
reinterpret_cast
<
void
*>(seg_file_end),
zeromap_size,
PFLAGS_TO_PROT(phdr->p_flags),
MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE,
-1,
0);
if
(zeromap == MAP_FAILED) {
LOGE(
"couldn't zero fill \"%s\" gap: %s"
, name_.c_str(),
strerror
(
errno
));
return
false
;
}
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, zeromap, zeromap_size,
".bss"
);
}
}
return
true
;
}
bool
MyLoader::FindPhdr() {
const
ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_;
for
(
const
ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
if
(phdr->p_type == PT_PHDR) {
return
CheckPhdr(load_bias_ + phdr->p_vaddr);
}
}
for
(
const
ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
if
(phdr->p_type == PT_LOAD) {
if
(phdr->p_offset == 0) {
ElfW(Addr) elf_addr = load_bias_ + phdr->p_vaddr;
const
ElfW(Ehdr)* ehdr =
reinterpret_cast
<
const
ElfW(Ehdr)*>(elf_addr);
ElfW(Addr) offset = ehdr->e_phoff;
return
CheckPhdr(
reinterpret_cast
<ElfW(Addr)>(ehdr) + offset);
}
break
;
}
}
LOGE(
"can't find loaded phdr for \"%s\""
, name_.c_str());
return
false
;
}
bool
MyLoader::CheckPhdr(ElfW(Addr) loaded) {
const
ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_;
ElfW(Addr) loaded_end = loaded + (phdr_num_ *
sizeof
(ElfW(Phdr)));
for
(
const
ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
if
(phdr->p_type != PT_LOAD) {
continue
;
}
ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
ElfW(Addr) seg_end = phdr->p_filesz + seg_start;
if
(seg_start <= loaded && loaded_end <= seg_end) {
loaded_phdr_ =
reinterpret_cast
<
const
ElfW(Phdr)*>(loaded);
return
true
;
}
}
LOGE(
"\"%s\" loaded phdr %p not in loadable segment"
,
name_.c_str(),
reinterpret_cast
<
void
*>(loaded));
return
false
;
}
bool
MyLoader::FindPhdr() {
const
ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_;
for
(
const
ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
if
(phdr->p_type == PT_PHDR) {
return
CheckPhdr(load_bias_ + phdr->p_vaddr);
}
}
for
(
const
ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
if
(phdr->p_type == PT_LOAD) {
if
(phdr->p_offset == 0) {
ElfW(Addr) elf_addr = load_bias_ + phdr->p_vaddr;
const
ElfW(Ehdr)* ehdr =
reinterpret_cast
<
const
ElfW(Ehdr)*>(elf_addr);
ElfW(Addr) offset = ehdr->e_phoff;
return
CheckPhdr(
reinterpret_cast
<ElfW(Addr)>(ehdr) + offset);
}
break
;
}
}
LOGE(
"can't find loaded phdr for \"%s\""
, name_.c_str());
return
false
;
}
bool
MyLoader::CheckPhdr(ElfW(Addr) loaded) {
const
ElfW(Phdr)* phdr_limit = phdr_table_ + phdr_num_;
ElfW(Addr) loaded_end = loaded + (phdr_num_ *
sizeof
(ElfW(Phdr)));
for
(
const
ElfW(Phdr)* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
if
(phdr->p_type != PT_LOAD) {
continue
;
}
ElfW(Addr) seg_start = phdr->p_vaddr + load_bias_;
ElfW(Addr) seg_end = phdr->p_filesz + seg_start;
if
(seg_start <= loaded && loaded_end <= seg_end) {
loaded_phdr_ =
reinterpret_cast
<
const
ElfW(Phdr)*>(loaded);
return
true
;
}
}
LOGE(
"\"%s\" loaded phdr %p not in loadable segment"
,
name_.c_str(),
reinterpret_cast
<
void
*>(loaded));
return
false
;
}
soinfo* Utils::get_soinfo(
const
char
* so_name) {
typedef
soinfo* (*FunctionPtr)(ElfW(Addr));
char
line[1024];
ElfW(Addr) linker_base = 0;
ElfW(Addr) so_addr = 0;
FILE
*fp=
fopen
(
"/proc/self/maps"
,
"r"
);
while
(
fgets
(line,
sizeof
(line), fp)) {
if
(
strstr
(line,
"linker64"
) && !linker_base) {
char
* addr =
strtok
(line,
"-"
);
linker_base = strtoull(addr, NULL, 16);
}
else
if
(
strstr
(line, so_name) && !so_addr) {
char
* addr =
strtok
(line,
"-"
);
so_addr = strtoull(addr, NULL, 16);
}
if
(linker_base && so_addr)
break
;
}
ElfW(Addr) func_offset = Utils::get_export_func(
"/system/bin/linker64"
,
"find_containing_library"
);
if
(!func_offset) {
LOGE(
"func_offset == 0? check it ---> get_soinfo"
);
return
nullptr;
}
ElfW(Addr) find_containing_library_addr =
static_cast
<ElfW(Addr)>(linker_base + func_offset);
FunctionPtr find_containing_library =
reinterpret_cast
<FunctionPtr>(find_containing_library_addr);
return
find_containing_library(so_addr);
}
soinfo* Utils::get_soinfo(
const
char
* so_name) {
typedef
soinfo* (*FunctionPtr)(ElfW(Addr));
char
line[1024];
ElfW(Addr) linker_base = 0;
ElfW(Addr) so_addr = 0;
FILE
*fp=
fopen
(
"/proc/self/maps"
,
"r"
);
while
(
fgets
(line,
sizeof
(line), fp)) {
if
(
strstr
(line,
"linker64"
) && !linker_base) {
char
* addr =
strtok
(line,
"-"
);
linker_base = strtoull(addr, NULL, 16);
}
else
if
(
strstr
(line, so_name) && !so_addr) {
char
* addr =
strtok
(line,
"-"
);
so_addr = strtoull(addr, NULL, 16);
}
if
(linker_base && so_addr)
break
;
}
ElfW(Addr) func_offset = Utils::get_export_func(
"/system/bin/linker64"
,
"find_containing_library"
);
if
(!func_offset) {
LOGE(
"func_offset == 0? check it ---> get_soinfo"
);
return
nullptr;
}
ElfW(Addr) find_containing_library_addr =
static_cast
<ElfW(Addr)>(linker_base + func_offset);
FunctionPtr find_containing_library =
reinterpret_cast
<FunctionPtr>(find_containing_library_addr);
return
find_containing_library(so_addr);
}
ElfW(Addr) Utils::get_export_func(
char
* path,
char
* func_name) {
struct
stat sb;
int
fd = open(path, O_RDONLY);
fstat(fd, &sb);
void
* base = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
ElfW(Ehdr) header;
memcpy
(&(header), base,
sizeof
(header));
size_t
size = header.e_shnum *
sizeof
(ElfW(Shdr));
void
* tmp = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
LOGD(
"error: %s"
,
strerror
(
errno
));
ElfW(Shdr)* shdr_table;
memcpy
(tmp, (
void
*)((ElfW(Off))base + header.e_shoff), size);
shdr_table =
static_cast
<ElfW(Shdr)*>(tmp);
char
* shstrtab =
reinterpret_cast
<
char
*>(shdr_table[header.e_shstrndx].sh_offset + (ElfW(Off))base);
void
* symtab = nullptr;
char
* strtab = nullptr;
uint32_t symtab_size = 0;
for
(
size_t
i = 0; i < header.e_shnum; ++i) {
const
ElfW(Shdr) *shdr = &shdr_table[i];
char
* section_name = shstrtab + shdr->sh_name;
if
(!
strcmp
(section_name,
".symtab"
)) {
symtab =
reinterpret_cast
<
void
*>(shdr->sh_offset + (ElfW(Off))base);
symtab_size = shdr->sh_size;
}
if
(!
strcmp
(section_name,
".strtab"
)) {
strtab =
reinterpret_cast
<
char
*>(shdr->sh_offset + (ElfW(Off))base);
}
if
(strtab && symtab)
break
;
}
ElfW(Sym)* sym_table;
tmp = mmap(nullptr, symtab_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
memcpy
(tmp, symtab, symtab_size);
sym_table =
static_cast
<ElfW(Sym)*>(tmp);
int
sym_num = symtab_size /
sizeof
(ElfW(Sym));
for
(
int
i = 0; i < sym_num; i++) {
const
ElfW(Sym) *sym = &sym_table[i];
char
* sym_name = strtab + sym->st_name;
if
(
strstr
(sym_name, func_name)) {
return
sym->st_value;
}
}
return
0;
}
ElfW(Addr) Utils::get_export_func(
char
* path,
char
* func_name) {
struct
stat sb;
int
fd = open(path, O_RDONLY);
fstat(fd, &sb);
void
* base = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
ElfW(Ehdr) header;
memcpy
(&(header), base,
sizeof
(header));
size_t
size = header.e_shnum *
sizeof
(ElfW(Shdr));
void
* tmp = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
LOGD(
"error: %s"
,
strerror
(
errno
));
ElfW(Shdr)* shdr_table;
memcpy
(tmp, (
void
*)((ElfW(Off))base + header.e_shoff), size);
shdr_table =
static_cast
<ElfW(Shdr)*>(tmp);
char
* shstrtab =
reinterpret_cast
<
char
*>(shdr_table[header.e_shstrndx].sh_offset + (ElfW(Off))base);
void
* symtab = nullptr;
char
* strtab = nullptr;
uint32_t symtab_size = 0;
[注意]APP应用上架合规检测服务,协助应用顺利上架!