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