想做一個(gè)部門的網(wǎng)站怎么做頭條熱點(diǎn)新聞
在某些學(xué)習(xí)或者特殊需求的情況下要對(duì)linux下動(dòng)態(tài)庫(kù)*.so文件內(nèi)部的函數(shù)名進(jìn)行修改。
????????比如一個(gè)函數(shù)ADD(int a,int b);修改為Add(int a,int b);
通過(guò)這篇文章你將了解到在linux下動(dòng)態(tài)庫(kù)函數(shù)名尋址的規(guī)則,截止2024年3月linux動(dòng)態(tài)庫(kù)的尋址規(guī)則已經(jīng)出現(xiàn)多種,這里不會(huì)一一介紹。這篇文章僅提供規(guī)則,并不提供修改函數(shù)名的相關(guān)代碼和執(zhí)行文件。如果有需要請(qǐng)留言溝通。
開(kāi)發(fā)環(huán)境:Ubuntu20、gcc、g++、IDAPro(Windows下安裝即可,版本不限)。
必備知識(shí):ELF文件標(biāo)準(zhǔn)(linux下執(zhí)行文件頭)、IDApro的使用方法。
建議了解:linux 動(dòng)態(tài)庫(kù)加載流程(自行尋找資源,后續(xù)可能給出鏈接)參考文檔(P73):https://paper.seebug.org/papers/Archive/refs/elf/Understanding_ELF.pdf
正文開(kāi)始:
? ? ? ? 編譯簡(jiǎn)單的動(dòng)態(tài)庫(kù)和調(diào)用代碼。
? ? ? ? 以下為測(cè)試代碼:其中僅有兩個(gè)函數(shù)分別是ADD和MINUS的動(dòng)態(tài)庫(kù)代碼;使用main.cpp鏈接api.so對(duì)這兩個(gè)函數(shù)進(jìn)行調(diào)用,并執(zhí)行。最終再修改main.cpp對(duì)ADD的調(diào)用修改為Add,同時(shí)不編譯動(dòng)態(tài)庫(kù),僅對(duì)動(dòng)態(tài)庫(kù)的二進(jìn)制文件進(jìn)行修改的情況下,完成調(diào)用。
注:下列代碼僅為參考,并未規(guī)范處理。編譯命令見(jiàn)main.cpp。
//myAPI.h
//int ADD(int a, int b);
//int MINUS(int a, int b);#ifdef __cplusplus
extern "C" {
#endif// int Add(int a, int b);
int ADD(int a, int b);
int MINUS(int a, int b);#ifdef __cplusplus
}
#endif
//myAPI.cpp
#include "api.h"// int aaa(int a, int b){
int ADD(int a, int b){return a + b;
}
// int aaa(int a, int b){
// return a + b;
// }
int MINUS(int a, int b){// int c = ADD(a,b);int c = b;return a - c;
}
//main.cpp
#include "api.h"
#include <iostream>
// #include <hash.h>
// 使用hash表做編譯,不寫使用ELF GUN hash 或者兩者兼顧// g++ -shared -fPIC -o libapi.so api.cpp -Wl,--hash-style=sysv /// -Wl,--retain-symbols-file=retain-symbols.txt
// 鏈接動(dòng)態(tài)庫(kù)生成執(zhí)行文件// g++ -o main main.cpp api.h -L. -lapi
// 添加動(dòng)態(tài)庫(kù)路徑到環(huán)境變量中// export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/.
int main(){std::cout << "1 + 1 = " << ADD(1, 1) << std::endl;std::cout << "1 - 1 = " << MINUS(1, 1) << std::endl;return 0;
}
通過(guò)上述步驟你將獲得libapi.so和main兩個(gè)可執(zhí)行文件。并且執(zhí)行main時(shí),運(yùn)行正常。
打開(kāi)IDAPro,將libapi.so使用IAD進(jìn)行逆向,能夠查看二進(jìn)制文件即可。
idx name hash_val3 __cxa_finalize 0BEA6495 000000024 _Z5MINUSii 01E5E459 000000015 _ITM_registerTMCloneTable 0B7268A5 000000016 _ITM_deregisterTMCloneTable 012F7225 000000017 _Z3ADDii 0D758CB9 000000028 __gmon_start__ 0F4D007F 00000000ida pro 提供數(shù)據(jù)如下:; ELF Hash Tableelf_hash_nbucket DCD 3elf_hash_nchain DCD 9elf_hash_bucket DCD 8, 6, 7elf_hash_chain DCD 0, 0, 0, 0, 0, 4, 5, 3, 0; ELF Symbol Table0 Elf64_Sym <0>1 Elf64_Sym <byte_300 - byte_300, 3, 0, 7, .init_proc, 0>2 Elf64_Sym <byte_300 - byte_300, 3, 0, 0x12, __dso_handle, 0>3 Elf64_Sym <aCxaFinalize - byte_300, 0x20, 0, 0, dword_0, 0> ; "__cxa_finalize"4 Elf64_Sym <aZ5minusii - byte_300, 0x12, 0, 9, _Z5MINUSii, 0x28> ; "_Z5MINUSii" ...5 Elf64_Sym <aItmRegistertmc - byte_300, 0x20, 0, 0, dword_0, 0> ; "_ITM_registerTMCloneTable"6 Elf64_Sym <aItmDeregistert - byte_300, 0x20, 0, 0, dword_0, 0> ; "_ITM_deregisterTMCloneTable"7 Elf64_Sym <aZ3addii - byte_300, 0x12, 0, 9, _Z3ADDii, 0x20> ; "_Z3ADDii" ...8 Elf64_Sym <aGmonStart - byte_300, 0x20, 0, 0, dword_0, 0> ; "__gmon_start__"; ELF String Tablebyte_300 DCB 0 ; DATA XREF: LOAD:0000000000000240↑o; LOAD:0000000000000258↑o ...aGmonStart DCB "__gmon_start__",0 ; DATA XREF: LOAD:00000000000002E8↑oaItmDeregistert DCB "_ITM_deregisterTMCloneTable",0; DATA XREF: LOAD:00000000000002B8↑oaItmRegistertmc DCB "_ITM_registerTMCloneTable",0; DATA XREF: LOAD:00000000000002A0↑oaCxaFinalize DCB "__cxa_finalize",0 ; DATA XREF: LOAD:0000000000000270↑oaZ3addii DCB "_Z3ADDii",0 ; DATA XREF: LOAD:00000000000002D0↑oaZ5minusii DCB "_Z5MINUSii",0 ; DATA XREF: LOAD:0000000000000288↑oDCB 0, 0, 0, 0, 0, 0, 0
規(guī)則:
? ? ? ? ? ? 規(guī)則:idx是直接讀取 ELF Symbol Table 里面的 內(nèi)容,順序和 ELF String Table 里面的不同
? ? ? ? ? ? ? ? ? ? hash_val 通過(guò)特定的方法計(jì)算得出。
? ? ? ? ? ? ? ? ? ? elf_hash_nbucket 這個(gè)值編譯器根據(jù)方 案計(jì)算出的,一般(總符號(hào)數(shù)/4 + 1) 在附近選擇一個(gè)素?cái)?shù),能 夠使數(shù)據(jù)更加離散。
? ? ? ? ? ? ? ? ? ? elf_hash_nchain ?這個(gè)值是符號(hào)的個(gè)數(shù) 。
? ? ? ? ? ? ? ? ? ? elf_hash_bucket DCD 8, 6, 7
? ? ? ? ? ? ? ? ? ? ? ? 根據(jù)下表計(jì)算出的hash_val從最下面開(kāi)始,第8個(gè) 0 ,接著第6個(gè) 1, 第7個(gè)是2。
? ? ? ? ? ? ? ? ? ? elf_hash_chain ?DCD 0, 0, 0, 0, 0, 4, 5, 3, 0
? ? ? ? ? ? ? ? ? ? ? ? 根據(jù)下表計(jì)算出:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?在計(jì)算的hash=0有 8,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?計(jì)算的hash=1有 6,5,4
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?計(jì)算的hash=2有 7,3
? ? ? ? ? ? ? ? ? ? ? ? ? ? 因此:elf_hash_bucket[0] = 8;elf_hash_bucket[1] = 6;elf_hash_bucket[2] = 7;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? elf_hash_chain[8] = 0
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? elf_hash_chain[6] = 5; elf_hash_chain[5] = 4; elf_hash_chain[4] = 0
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? elf_hash_chain[7] = 3;
? ? ? ? ? ? ? ? 對(duì)橙色部分的內(nèi)容進(jìn)行解釋:
?????????????????hash為0的只有8,結(jié)尾默認(rèn)0,故hash_chain[8]=0。
? ? ? ? ? ? ? ? ?hash為1的有6,5,4 結(jié)尾默認(rèn)0,故hash_chain[6]=5,hash_chain[5]=4,hash_chain[4]=0
? ? ? ? ? ? ? ? ?同理,hash為2的如上。
至此,你已經(jīng)了解了--hash-style=sysv使用傳統(tǒng)ELF編譯動(dòng)態(tài)庫(kù),hash表的生成規(guī)則了,其本質(zhì)是一種快捷的鏈?zhǔn)浇Y(jié)構(gòu)。
使用十六進(jìn)制編輯器,將hash表對(duì)應(yīng)的函數(shù)ADD->Add,然后調(diào)整ELF hash鏈表。
3 __cxa_finalize 0BEA6495 000000024 _Z5MINUSii 01E5E459 000000015 _ITM_registerTMCloneTable 0B7268A5 000000016 _ITM_deregisterTMCloneTable 012F7225 000000017 _Z3Addii 0D77ACB9 000000008 __gmon_start__ 0F4D007F 00000000hash_val = 0: 8,7hash_val = 1: 6,5,4hash_val = 2: 3elf_hash_bucket DCD 8, 6, 3elf_hash_chain DCD 0, 0, 0, 0, 0, 4, 5, 0, 7
注:使用IDA獲取這個(gè)表的時(shí)候,有對(duì)應(yīng)值在二進(jìn)制文件的偏移,可以根據(jù)偏移直接修改。這一過(guò)程修改了hash table 和 string table兩部分,上面首為7的行,已經(jīng)和最開(kāi)始的不一致了,是通過(guò)elf_hash(string)獲得到的。這個(gè)函數(shù)的實(shí)現(xiàn),資源較多。稍后進(jìn)行鏈接給出。
調(diào)整完畢,再將main.cpp和api.h兩個(gè)文件的ADD->Add。如下:
//myAPI.h
//int ADD(int a, int b);
//int MINUS(int a, int b);#ifdef __cplusplus
extern "C" {
#endif// int Add(int a, int b);
int ADD(int a, int b);
int MINUS(int a, int b);#ifdef __cplusplus
}
#endif
//----------------------------------
//main.cpp
#include "api.h"
#include <iostream>
// #include <hash.h>
// 使用hash表做編譯,不寫使用ELF GUN hash 或者兩者兼顧// g++ -shared -fPIC -o libapi.so api.cpp -Wl,--hash-style=sysv /// -Wl,--retain-symbols-file=retain-symbols.txt
// 鏈接動(dòng)態(tài)庫(kù)生成執(zhí)行文件// g++ -o main main.cpp api.h -L. -lapi
// 添加動(dòng)態(tài)庫(kù)路徑到環(huán)境變量中// export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/.
int main(){std::cout << "1 + 1 = " << Add(1, 1) << std::endl;std::cout << "1 - 1 = " << MINUS(1, 1) << std::endl;return 0;
}
此時(shí),使用修改后的libapi.so和上述兩個(gè)文件,編譯,發(fā)現(xiàn)Add可以被正常調(diào)用。
結(jié)語(yǔ):此案例中ADD->Add是函數(shù)名長(zhǎng)度,所以可以避免下面執(zhí)行的代碼不做偏移修改,如果函數(shù)名長(zhǎng)度不一致,可能導(dǎo)致整個(gè)動(dòng)態(tài)庫(kù)文件出現(xiàn)較大的問(wèn)題。如果有興趣,可以查找相關(guān)的資料并提交到GNU,說(shuō)不定你也是對(duì)開(kāi)源社區(qū)做貢獻(xiàn)的小可愛(ài)了。這個(gè)案例對(duì)有傳統(tǒng)ELF hash表有效,其他標(biāo)準(zhǔn)的hash表也是類似的調(diào)整方法,了解其運(yùn)作原理就可以很快出結(jié)果。
參考文檔(P73):https://paper.seebug.org/papers/Archive/refs/elf/Understanding_ELF.pdf