詳解三種 Android 平臺注入技術(shù)
背景
在android系統(tǒng)中,進(jìn)程之間是相互隔離的,兩個進(jìn)程之間是沒辦法直接跨進(jìn)程訪問其他進(jìn)程的空間信息的。那么在android平臺中要對某個app進(jìn)程進(jìn)行內(nèi)存操作,并獲取目標(biāo)進(jìn)程的地址空間內(nèi)信息或者修改目標(biāo)進(jìn)程的地址空間內(nèi)的私有信息,就需要涉及到注入技術(shù)。
通過注入技術(shù)可以將指定so模塊或代碼注入到目標(biāo)進(jìn)程中,只要注入成功后,就可以進(jìn)行訪問和篡改目標(biāo)進(jìn)程空間內(nèi)的信息,包括數(shù)據(jù)和代碼。
Android的注入技術(shù)的應(yīng)用場景主要是進(jìn)行一些非法的操作和實(shí)現(xiàn)如游戲輔助功能軟件、惡意功能軟件。
下面主要進(jìn)行對zygote注入、ptrace注入、修改so文件注入,這三種注入方式進(jìn)行詳細(xì)解析。
zygote注入
zygote是一個在android系統(tǒng)中是非常重要的一個進(jìn)程,因?yàn)樵赼ndroid中絕大部分的應(yīng)用程序進(jìn)程都是由它孵化(fork)出來的,fork是一種進(jìn)程復(fù)用技術(shù)。也就是說在android系統(tǒng)中普通應(yīng)用APP進(jìn)程的父親都是zygote進(jìn)程。
zygote注入目的就是將指定的so模塊注入到指定的APP進(jìn)程中,這個注入過程不是直接向指定進(jìn)程進(jìn)程注入so模塊,而是先將so模塊注入到zygote進(jìn)程。
在so模塊注入到zygote進(jìn)程后,在點(diǎn)擊操作android系統(tǒng)中啟動的應(yīng)用程序APP進(jìn)程,啟動的App進(jìn)程中包括需要注入到指定進(jìn)程的so模塊,太都是由zygote進(jìn)程fork生成,因而在新創(chuàng)建的進(jìn)程中都會包含已注入zygote進(jìn)程的so模塊。
這種的注入是通過間接注入方式完成的,也是一種相對安全的注入so模塊方式。目前xposed框架就是基于zygote注入。
zygote注入so模塊流程
1.通過注入器將要注入的so模塊注入到zygote進(jìn)程;
2.手動啟動要注入so模塊的APP進(jìn)程,由于APP進(jìn)程是通過zygote進(jìn)程fork出來的,所以啟動的APP進(jìn)程都包含zygote進(jìn)程中所有模塊;
3.注入的so模塊劫持被注入APP進(jìn)程的控制權(quán),執(zhí)行注入so模塊的代碼;
4.注入so模塊歸還APP進(jìn)程的控制權(quán),被注入進(jìn)程正常運(yùn)行。
Zygote注入器的實(shí)現(xiàn)流程
(注入器主要是基于ptrace注入shellcode方式的進(jìn)程注入)
- 通過ptrace進(jìn)行附加到zygote進(jìn)程。
- 調(diào)用mmap申請目標(biāo)進(jìn)程空間,用于保存注入的shellcode匯編代碼。
- 執(zhí)行注入shellcode代碼(shellcode代碼是注入目標(biāo)進(jìn)程中并執(zhí)行的匯編代碼)。
- 調(diào)用munmap函數(shù)釋放申請的內(nèi)存。
- 通過ptrace進(jìn)行剝離zygote進(jìn)程。
下面是關(guān)鍵的zygote代碼注入實(shí)現(xiàn)
ptrace注入
ptrace注入實(shí)現(xiàn)上分類:
- 通過利用ptrace函數(shù)將shellcode注入遠(yuǎn)程進(jìn)程的內(nèi)存空間中,然后通過執(zhí)行shellcode加載遠(yuǎn)程進(jìn)程so模塊。
- 通過直接遠(yuǎn)程調(diào)用dlopen、dlsym、dlclose等函數(shù)加載被注入so模塊,并執(zhí)行指定的代碼。
ptrace直接調(diào)用函數(shù)注入流程:
- 通過利用ptrace進(jìn)行附加到要注入的進(jìn)程;
- 保存寄存環(huán)境;
- 遠(yuǎn)程調(diào)用mmap函數(shù)分配內(nèi)存空間;
- 向遠(yuǎn)程進(jìn)程內(nèi)存空間寫入加載模塊名稱和函數(shù)名稱;
- 遠(yuǎn)程調(diào)用dlopen函數(shù)打開注入模塊;
- 遠(yuǎn)程調(diào)用dlsym函數(shù)或需要調(diào)用的函數(shù)地址;
- 遠(yuǎn)程調(diào)用被注入模塊的函數(shù);
- 恢復(fù)寄存器環(huán)境;
- 利用ptrace從遠(yuǎn)程進(jìn)程剝離。
關(guān)鍵的ptrace直接調(diào)用系統(tǒng)函數(shù)實(shí)現(xiàn)
ptrace的shellcode注入原理
shellcode注入就是通過將dlopen/dlsym庫函數(shù)的操作放在shellcode代碼中,注入函數(shù)只是通過對遠(yuǎn)程APP進(jìn)程進(jìn)行內(nèi)存空間申請,接著修改shellcode 代碼中有關(guān)dlopen、dlsymdlclose等函數(shù)使用到的參數(shù)信息,然后將shellcode代碼注入到遠(yuǎn)程APP進(jìn)程申請的空間中,最后通過修改PC寄存器的方式來執(zhí)行shellcode 的代碼。
ptrace注入shellcode的詳細(xì)步驟
1.在shellcode中編寫好dlopen、dlsym等函數(shù)的調(diào)用,來加載so模塊和執(zhí)行函數(shù),但需要將參數(shù)地址、函數(shù)地址、寄存器地址先隨便填充值為我們真實(shí)地址保留;
2.附加到遠(yuǎn)程APP進(jìn)程、保存APP進(jìn)程中寄存器的數(shù)據(jù),為后面恢復(fù)遠(yuǎn)程進(jìn)程的繼續(xù)執(zhí)行準(zhǔn)備;
3.向遠(yuǎn)程APP進(jìn)程申請內(nèi)存空間,選好shellcode存放的具體位置,準(zhǔn)備存放shellcode和參數(shù)數(shù)據(jù);
4.計算本地so模塊函數(shù)對應(yīng)到,遠(yuǎn)程APP進(jìn)程中的so模塊函數(shù)地址,填充到shellcdoe中的參數(shù)中。計算好庫函數(shù)參數(shù)、寄存器存值相對shellcode起始位置的偏移再加上遠(yuǎn)程進(jìn)程中shellcode存放的起始位置,得到的結(jié)果就是遠(yuǎn)程進(jìn)程的內(nèi)存空間中這些參數(shù)存放的位置,將這些地址填充到shellcode的參數(shù)中;
5.設(shè)置寄存器的值來讓執(zhí)行庫函數(shù);
6.恢復(fù)寄存器的值讓遠(yuǎn)程進(jìn)程繼續(xù)正常執(zhí)行。
關(guān)鍵 的ptrace注入shellcode代碼實(shí)現(xiàn)
修改ELF文件注入
在android平臺Native層的可執(zhí)行文件SO文件,它是屬于ELF文件格式,通過修改ELF文件格式可以實(shí)現(xiàn)對so文件的注入。
通過修改ELF二進(jìn)制的可執(zhí)行文件,并在ELF文件中添加自己的代碼,使得可執(zhí)行文件在運(yùn)行時會先執(zhí)行自定義添加的代碼,最后在執(zhí)行ELF文件的原始邏輯。
修改二進(jìn)制ELF文件需要關(guān)注兩個重要的結(jié)構(gòu)體:
ELF Header、Program Header Table
其中ELF Header 它是ELF文件中唯一的,一個固定位置的文件結(jié)構(gòu),它保存著Program Header Table和Section Header Table的位置和大小信息。
Program Header Table 它保存ELF文件的加載過程中各Section的內(nèi)存映射和依賴庫相關(guān)信息,用來告訴android系統(tǒng)中如何創(chuàng)建進(jìn)程映像。
修改ELF文件實(shí)現(xiàn)so文件注入實(shí)現(xiàn)原理為:通過修改 Program Header Table中的依賴庫信息,添加自定義的so文件信息,APP進(jìn)程運(yùn)行加載被該修改過的ELF文件,它也同時會加載并運(yùn)行自定義的so文件。
Program Header Table表項(xiàng)結(jié)構(gòu)
程序頭表項(xiàng)中的類型選項(xiàng)有如下
當(dāng)程序頭表項(xiàng)結(jié)構(gòu)中的類型為PT_DYNAMIC也就是動態(tài)鏈接信息的時候,它是由程序頭表項(xiàng)的偏移(p_offset)和p_filesz(大小)指定的數(shù)據(jù)塊指向.dynamic段。這個.dynamic段包含程序鏈接和加載時的依賴庫信息。
修改ELF文件的注入實(shí)現(xiàn)過程
1.修改.dynamic段指向的字符串表中添加 自定義的so模塊名稱;
2.通過修改Program Header Table中添加PT_LOAD表項(xiàng),新添加的表項(xiàng)將保護(hù)so模塊名稱的字符串表數(shù)據(jù)映射到內(nèi)存中。同時將Program Header Table移動到文件末尾;
3.修改.dynamic段的數(shù)組數(shù)據(jù),使得指向新的字符串表,并指向自定義的so模塊名稱;
4.修改ELF HEADER結(jié)構(gòu)中 Program Header Table的位置信息,并指向新的Program Header Table。
關(guān)鍵ELF文件修改代碼實(shí)現(xiàn)