本文參考自:Android逆向之旅—基于對(duì)so中的section加密技術(shù)實(shí)現(xiàn)so加固,增加了自己的實(shí)踐過程周崭,以及一些額外的驗(yàn)證和解釋。
本文代碼參見:https://github.com/difcareer/SoEncrypt
Android逆向之旅—基于對(duì)so中的section加密技術(shù)實(shí)現(xiàn)so加固 這篇文章寫得真心好华望,建議先閱讀一下原著丈秩,這里只是自己的實(shí)踐過程(紙上得來終覺淺,絕知此事要躬行)烁巫,和一些更細(xì)節(jié)的解釋罷了署隘。
一. 拆分section
這個(gè)demo的目的是為了將native函數(shù)getString()給保護(hù)起來(實(shí)際應(yīng)用場(chǎng)景就是自己業(yè)務(wù)中的核心代碼)。為了保護(hù)getString()亚隙,用到了gcc的Attributes特性:
__attribute__((section ("xxx")))
上述的文檔中提到磁餐,給變量或者方法增加這個(gè)修飾后,編譯器將把對(duì)應(yīng)的代碼或者數(shù)據(jù)放到你指定的section中阿弃。
我們的demo中做了示例:
__attribute__((section (".encrypt"))) jstring getString(JNIEnv* env) {
static const char* txt __attribute__((section (".encrypt2"))) = "Str from native";
return (*env)->NewStringUTF(env, txt);
};
我們同時(shí)給getString()函數(shù)和txt變量添加了這個(gè)屬性诊霹,分別指定了不同名稱羞延。我們看一下編譯后的so:
可以看到新增了我們自定義的section: encrypt、encrypt2脾还,encrypt中存放getString()的代碼伴箩,因此被映射為可執(zhí)行,encrypt2中存放txt的數(shù)據(jù)鄙漏,因此被映射為可寫嗤谚。
ok,到這里我們已經(jīng)找到拆分核心代碼到單獨(dú)section的方法了泥张。后續(xù)我們只要對(duì)這些section做加密即可保護(hù)核心代碼呵恢。
二. 尋找解密時(shí)機(jī)
假設(shè)我們已經(jīng)加密了這些section,運(yùn)行的時(shí)候總是需要解密還原的媚创,什么時(shí)機(jī)解密最好呢渗钉,當(dāng)然是越早越好,最早可以在load so之后钞钙,執(zhí)行JNI_Onload之前鳄橘,這里也是需要gcc的另外一個(gè)Attributes特性:
__attribute__((constructor (n)))
文檔中指出,constructor (priority)可以在后面指定一個(gè)優(yōu)先級(jí)芒炼,數(shù)字越小瘫怜,優(yōu)先級(jí)越高,越先被執(zhí)行本刽。
關(guān)于這點(diǎn)我們?cè)赿emo中也做了驗(yàn)證:
void init_1() __attribute__((constructor (3)));
void init_getString() __attribute__((constructor (2)));
void init_2() __attribute__((constructor (1)));
我們申明了3個(gè)函數(shù)鲸湃,優(yōu)先級(jí)從低到高,按照規(guī)則子寓,執(zhí)行順序應(yīng)該是:init_2暗挑、init_getString、init_1斜友,我們看一下so:
已經(jīng)按照優(yōu)先級(jí)調(diào)整好了順序炸裆。
我們看下日志:
的確也是這個(gè)順序。
ok鲜屏,這樣我們就可以在這個(gè)特性的修飾下烹看,盡早能做解密邏輯了。
三. 加密邏輯
先說一下加密洛史,作者的加密算法很簡單:字節(jié)取反惯殊。在misc/encrpt.c中,我們可以發(fā)現(xiàn)其核心邏輯是尋找叫做 encrypt 的 section也殖,然后字節(jié)取反寫回靠胜,同時(shí)計(jì)算將一些值計(jì)算了寫入ehdr.e_entry(這個(gè)對(duì)于正常的so是0值)和ehdr.e_shoff(這個(gè)是section表的偏移量,修改這個(gè)值將導(dǎo)致找不到section,后面會(huì)看到加密效果)浪漠,這些值在解密的時(shí)候需要。
demo的misc下有編譯后的腳本encrpt霎褐,需要在linux環(huán)境下執(zhí)行址愿,libencrypt.so是沒有加密前的so,libencrypt2.so是加密后的so冻璃。你可以自行使用beyond compare比較差異响谓。
四. 解密邏輯
回到最重要的解密邏輯了,我們?cè)?code>__attribute__((constructor (n)))修飾的方法init_getString()
中實(shí)現(xiàn)了解密邏輯省艳,其原理是娘纷,通過讀取/proc/pid/maps中的內(nèi)容,找到so被映射到內(nèi)存中的地址跋炕,然后通過ehdr.e_entry和ehdr.e_shoff中的內(nèi)容還原出decrypt section 的地址赖晶,字節(jié)取反恢復(fù),內(nèi)存寫回辐烂。這樣就做到了動(dòng)態(tài)解密了遏插。
五. 加密效果
使用ida打開misc/libencrypt2.so
提示這個(gè)是因?yàn)樾薷牧薳hdr.e_shoff,破壞了第一個(gè)section 類型為SHT_NULL的規(guī)則纠修。
下一步直接解析section 報(bào)錯(cuò)胳嘲。
正常的section都看不到了,看到的都是program sections(上一個(gè)圖的提示)扣草。
而apk可以正常運(yùn)行(加密后的apk為misc/signed.apk):
參考鏈接:
http://www.wjdiankong.cn/android%e9%80%86%e5%90%91%e4%b9%8b%e6%97%85-%e5%9f%ba%e4%ba%8e%e5%af%b9so%e4%b8%ad%e7%9a%84section%e5%8a%a0%e5%af%86%e6%8a%80%e6%9c%af%e5%ae%9e%e7%8e%b0so%e5%8a%a0%e5%9b%ba/
https://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Variable-Attributes.html
https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes