so文件函數(shù)的加密和解密學(xué)習(xí)筆記

SO文件中函數(shù)的加密和解密

簡(jiǎn)介

原理上來(lái)說(shuō)剃法,找到so文件函數(shù)的位置,對(duì)其二進(jìn)制進(jìn)行一定加密操作后即加密了,解密也是一樣箱歧,找到函數(shù)的位置剧罩,對(duì)其二進(jìn)制進(jìn)行一定的解密操作即可栓拜,只不過(guò)前者是通過(guò)so文件格式,按照一定的索引一步一步找到函數(shù)位置惠昔;后者是在其運(yùn)
行時(shí)幕与,通過(guò)/proc/pid號(hào)/maps文件,找到so文件映射的內(nèi)存虛擬地址镇防,在根據(jù)so文件格式的索引一步一步找到函數(shù)位置啦鸣,再進(jìn)行解密工作;首先来氧,從section角度來(lái)說(shuō):

    + .text 存放函數(shù)的具體實(shí)現(xiàn)
    + .dynstr 存放函數(shù)名字
    + .dynsym 相當(dāng)于指針诫给,存放每個(gè)函數(shù)實(shí)現(xiàn)代碼(在.text)的地址
    + .hash 描述.dynsym如何存儲(chǔ)的,根據(jù)一定的算法可以映射到dynsym

注意:文中的提到的偏移是指在so文件頭開(kāi)始到任意一個(gè)位置的偏移

section結(jié)構(gòu)

.text和.dynstr沒(méi)有固定的結(jié)構(gòu)啦扬,存儲(chǔ)的都是代碼的值中狂,.hash和.dynsym則是有固定的結(jié)構(gòu),后兩者經(jīng)過(guò)一定邏輯運(yùn)算可以定位某個(gè)函數(shù)在前兩者的具體的位置;還有一點(diǎn)扑毡,后面兩個(gè)section從結(jié)構(gòu)視圖(section header)方面找到胃榕,需要從>執(zhí)行視圖(program header)方便的找到,所以這里需要了解program header中的一個(gè)段.dynmaic的結(jié)構(gòu)

.dynmaic segemnet

這個(gè)段可以根據(jù)program header的type類(lèi)型來(lái)確定瞄摊,type為PT_DYNAMIC = 2

d_tag: 描述每個(gè)具體段的類(lèi)型勋又,如.hash .dynsym .dynstr等段,都可以根據(jù)d_tag來(lái)確定
d_val和d_ptr是一個(gè)聯(lián)合體類(lèi)型泉褐,共同占用4個(gè)字節(jié)赐写,一個(gè)類(lèi)型多種解釋;
d_val: 表示d_tag對(duì)應(yīng)段的大小size
d_ptr: 表示d_tag對(duì)應(yīng)段的位置偏移
該段結(jié)構(gòu)如下:

typedef struct dynamic{
  Elf32_Sword d_tag;
  union{
    Elf32_Sword        d_val;
    Elf32_Addr        d_ptr;
  } d_un;
} Elf32_Dyn;

找到這個(gè).dynamic可以找到.hash .dynsym和.dynstr的偏移位置和大小

.hash section

一般來(lái)說(shuō),經(jīng)過(guò)如下操作可以定位到函數(shù)名字和函數(shù)實(shí)現(xiàn)代碼

  1. 函數(shù)字符串名字經(jīng)過(guò)hash函數(shù)得到一個(gè)hash_value的值
  2. 在.hash段中的nbucket膜赃,hash_value = hash_value % nbucket,hash段自身偏移加上hash_valuei + 8后偏移處讀取4字節(jié)值揉忘,記為fun_index;8表示開(kāi)頭的nbucket和nchain占用的字節(jié)
  3. 在.dynsym段中跳座,.dynsym自身偏移 + fun_index * 16,為什么要乘以16,是因?yàn)閐ynsym每個(gè)結(jié)構(gòu)占16個(gè)字節(jié)泣矛,這段偏移值的位置讀取16個(gè)字節(jié)疲眷,轉(zhuǎn)換微dynsym的elf_sym結(jié)構(gòu)
  4. elf_sym結(jié)構(gòu)中有一個(gè)st_name,用.dynstr的偏移 + st_name的值作為偏移您朽,讀取偏移處的值狂丝,然后與我們要加密的函數(shù)名比較换淆,相同就說(shuō)明找到,沒(méi)有的繼續(xù)進(jìn)行以下步驟
  5. 計(jì)算hash的新偏移 funindex_offset = dyn_detail['hash_offset'] + 4 * (2 + nbucket + fun_index)几颜,這句的意思就是要跳過(guò)以下hash結(jié)構(gòu)的bucket[]數(shù)組倍试,在chain中的fun_index位置出重新讀取到新的fun_index_offset,乘4是因
    為每個(gè)數(shù)據(jù)占用4個(gè)字節(jié)蛋哭,讀取此處偏移得到新的fun_index
  6. 重復(fù)3-4-5步驟县习,直到找到和函數(shù)名字相同的st_name為止
hash_section.png

.dynsym section

st_name對(duì)應(yīng)的字符串和函數(shù)名相同后,就可以根據(jù)st_value和st_size進(jìn)行函數(shù)結(jié)構(gòu)加密了

typedef struct elf32_sym{
  Elf32_Word        st_name;            #.dynstr_offset + st_name就是某個(gè)函數(shù)的具體偏移
  Elf32_Addr        st_value;           #st_value就是某個(gè)函數(shù)代碼實(shí)現(xiàn)的偏移
  Elf32_Word        st_size;            #函數(shù)代碼的長(zhǎng)度
  unsigned char        st_info;
  unsigned char        st_other;
  Elf32_Half        st_shndx;
} Elf32_Sym;

腳本

encode_so_function.py

這是我自己寫(xiě)的一個(gè)函數(shù)加密腳本谆趾,輸入函數(shù)名字躁愿,可以自動(dòng)找到函數(shù)對(duì)應(yīng)的地發(fā),然后進(jìn)行加密沪蓬,修改加密只需要改動(dòng)其中的encode_fun(fun_sym)函數(shù)即可彤钟,參數(shù)事找到函數(shù)對(duì)應(yīng)的那個(gè)dynsym結(jié)構(gòu),如上的結(jié)構(gòu)

加密腳本使用方法:

python encode_so_function.py so文件名 函數(shù)名

解密

界面時(shí)跷叉,我們需要先了解兩個(gè)知識(shí)點(diǎn):

attribute機(jī)制

__attribute__(Attribute);

該屬性是GNU C編譯的一種特色機(jī)制样勃,里面的Attribute可以設(shè)置函數(shù)屬性(Function Attribute)、變量屬性和類(lèi)型屬性性芬。我們這里用到函數(shù)屬性constructor峡眶,該屬性定義的函數(shù)將會(huì)在main函數(shù)執(zhí)行之前執(zhí)行,利用這個(gè)特性我們就可以在main之前解密我們的加密的函數(shù)段植锉,最后就可以運(yùn)行了辫樱;attribute的其他用法參考這里,以下是我自己使用的例子:

void init_decode() __attribute__((constructor));

void init_decode()是我自己寫(xiě)得一個(gè)解密函數(shù)俊庇,這個(gè)函數(shù)將會(huì)在main之前執(zhí)行

Linux的/proc/pid進(jìn)程號(hào)/maps文件

因?yàn)槲覀兊膕o文件時(shí)在運(yùn)行的時(shí)候進(jìn)行解密工作的狮暑,而maps文件正是應(yīng)用程序運(yùn)行時(shí)對(duì)各個(gè)段在內(nèi)存中的映射表文件,如讀寫(xiě)數(shù)據(jù)段辉饱、只讀代碼段搬男、堆和棧等區(qū)域在內(nèi)存中如何映射的,該文件都有記錄彭沼,如下:

[root@localhost proc]# cat /proc/1/maps
00110000-00111000 r-xp 00110000 00:00 0          [vdso]
0032b000-00347000 r-xp 00000000 fd:00 852733     /lib/ld-2.8.so
00347000-00348000 r--p 0001c000 fd:00 852733     /lib/ld-2.8.so
00348000-00349000 rw-p 0001d000 fd:00 852733     /lib/ld-2.8.so
0034b000-004ae000 r-xp 00000000 fd:00 852734     /lib/libc-2.8.so
004ae000-004b0000 r--p 00163000 fd:00 852734     /lib/libc-2.8.so
004b0000-004b1000 rw-p 00165000 fd:00 852734     /lib/libc-2.8.so
004b1000-004b4000 rw-p 004b1000 00:00 0
08048000-08067000 r-xp 00000000 fd:00 843075     /sbin/init
08067000-08068000 rw-p 0001e000 fd:00 843075     /sbin/init
08b42000-08b6a000 rw-p 08b42000 00:00 0          [heap]
b8046000-b8048000 rw-p b8046000 00:00 0
bfb4e000-bfb63000 rw-p bffeb000 00:00 0          [stack]

這里只做簡(jiǎn)單介紹缔逛,具體可參考這篇文章,上訴第一列代表映射到內(nèi)存的虛擬地址姓惑,第二列表示該內(nèi)存區(qū)域的權(quán)限褐奴,第三列表示內(nèi)存區(qū)域的偏移,最后一列是被映射的文件于毙,這里有我們熟悉的so庫(kù)敦冬,也有無(wú)名映射如堆棧heao stack等;
我們要做的是唯沮,在解密函數(shù)中脖旱,打開(kāi)讀取這個(gè)文件堪遂,找到第一個(gè)映射文件名為我們的so庫(kù)名,拿到這一行的第一列的虛擬地址作為基地址base萌庆,然后在根據(jù)so文件的格式索引(加密中尋找函數(shù)位置的過(guò)程)溶褪,這個(gè)過(guò)程中只要涉及地址操作的>都要加上這個(gè)base基地址,最終找到我們的函數(shù)進(jìn)行解密即可

例子

解密原理就是我上面的闡述踊兜,例子請(qǐng)參考我的github

C API 詳解

#include <unistd.h>
#include <sys/mmap.h>
int mprotect(const void *start, size_t len, int prot);

mprotect()函數(shù)把自start開(kāi)始的竿滨、長(zhǎng)度為len的內(nèi)存區(qū)的保護(hù)屬性修改為prot指定的值。
prot可以取以下幾個(gè)值捏境,并且可以用“|”將幾個(gè)屬性合起來(lái)使用:
1)PROT_READ:表示內(nèi)存段內(nèi)的內(nèi)容可寫(xiě)于游;
2)PROT_WRITE:表示內(nèi)存段內(nèi)的內(nèi)容可讀;
3)PROT_EXEC:表示內(nèi)存段中的內(nèi)容可執(zhí)行垫言;
4)PROT_NONE:表示內(nèi)存段中的內(nèi)容根本沒(méi)法訪問(wèn)贰剥。
需要指出的是,指定的內(nèi)存區(qū)間必須包含整個(gè)內(nèi)存頁(yè)(4K)筷频。區(qū)間開(kāi)始的地址start必須是一個(gè)內(nèi)存頁(yè)的起始地址蚌成,并且區(qū)間長(zhǎng)度len必須是頁(yè)大小的整數(shù)倍。
如果執(zhí)行成功凛捏,則返回0担忧;如果執(zhí)行失敗,則返回-1坯癣,并且設(shè)置errno變量瓶盛,說(shuō)明具體因?yàn)槭裁丛蛟斐烧{(diào)用失敗。錯(cuò)誤的原因主要有以下幾個(gè):
1)EACCES
該內(nèi)存不能設(shè)置為相應(yīng)權(quán)限示罗。這是可能發(fā)生的惩猫,比如,如果你 mmap(2) 映射一個(gè)文件為只讀的蚜点,接著使用 mprotect() 標(biāo)志為 PROT_WRITE轧房。
2)EINVAL
start 不是一個(gè)有效的指針,指向的不是某個(gè)內(nèi)存頁(yè)的開(kāi)頭绍绘。
3)ENOMEM
內(nèi)核內(nèi)部的結(jié)構(gòu)體無(wú)法分配奶镶。
4)ENOMEM
進(jìn)程的地址空間在區(qū)間 [start, start+len] 范圍內(nèi)是無(wú)效,或者有一個(gè)或多個(gè)內(nèi)存頁(yè)沒(méi)有映射脯倒。
如果調(diào)用進(jìn)程內(nèi)存訪問(wèn)行為侵犯了這些設(shè)置的保護(hù)屬性实辑,內(nèi)核會(huì)為該進(jìn)程產(chǎn)生 SIGSEGV (Segmentation fault,段錯(cuò)誤)信號(hào)藻丢,并且終止該進(jìn)程。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末摄乒,一起剝皮案震驚了整個(gè)濱河市悠反,隨后出現(xiàn)的幾起案子残黑,更是在濱河造成了極大的恐慌,老刑警劉巖斋否,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梨水,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡茵臭,警方通過(guò)查閱死者的電腦和手機(jī)疫诽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)旦委,“玉大人奇徒,你說(shuō)我怎么就攤上這事∮酰” “怎么了摩钙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)查辩。 經(jīng)常有香客問(wèn)我胖笛,道長(zhǎng),這世上最難降的妖魔是什么宜岛? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任长踊,我火速辦了婚禮,結(jié)果婚禮上萍倡,老公的妹妹穿的比我還像新娘身弊。我一直安慰自己,他們只是感情好遣铝,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布佑刷。 她就那樣靜靜地躺著,像睡著了一般酿炸。 火紅的嫁衣襯著肌膚如雪瘫絮。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,158評(píng)論 1 308
  • 那天填硕,我揣著相機(jī)與錄音麦萤,去河邊找鬼。 笑死扁眯,一個(gè)胖子當(dāng)著我的面吹牛壮莹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播姻檀,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼命满,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了绣版?” 一聲冷哼從身側(cè)響起胶台,我...
    開(kāi)封第一講書(shū)人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤歼疮,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后诈唬,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體韩脏,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年铸磅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赡矢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡阅仔,死狀恐怖吹散,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情霎槐,我是刑警寧澤送浊,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站丘跌,受9級(jí)特大地震影響袭景,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜闭树,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一耸棒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧报辱,春花似錦与殃、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至昼接,卻和暖如春爽篷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背慢睡。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工逐工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人漂辐。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓泪喊,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親髓涯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子袒啼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容