標籤:
繼續在《一》裡,我把基本思路描寫敘述了一遍,接下為我們先從注入開始入手。
注入
分類我們平時所說的代碼注入,主要靜態和動態兩種方式
- 靜態注入,針對是可運行檔案,比方平時我們改動ELF,DEX檔案等等,相關的協助工具輔助也非常多,比方IDA、JEB、ApkTool等等;
- 動態注入,針對是進程,比方改動進程的寄存器、記憶體值等等;
動態跟靜態最大的差別是,動態不須要修改源檔案,但須要高許可權(一般是root許可權),並且所需的技術含量更高。
本質動態注入技術,本質上就是一種調度技術。想想平時我們調試一個進程時,能夠做哪些功能? 一般有下列幾項:
- 查看變數值
- 改動變數值
- 跟蹤進程跳轉
- 查看進程呼叫堆疊
- 等等
動態注入相比於普通的調試,最大的差別就是動態注入是一個
”自己主動化調試並達到載入自己定義動態連結程式庫“的過程。所謂自己主動化,事實上就是通過代碼實現,在Linux上通過Ptrace就能夠完畢上面全部功能,當然Ptrace功能是比較原始的,平時調試中的功能還須要非常多高層邏輯封裝才幹夠實現。在閱讀以下章節之前,強烈建議閱讀一下man文檔,見這裡。
目的一般而言,我們要對一個進程進行注入,主要有下面幾方面目的:
- 增強目標進程的功能;
- 修複目標進程缺陷;
- 劫持目標進程函數;
- 竊取目標進程資料;
- 篡改目標進程資料;
過程如所看到的,進程A注入到進程B後,通過改動寄存器和記憶體,讓進程B載入自己定義的動態庫a,當a被載入後,a會嘗試載入其它模組,比方載入dex檔案等等,詳細的注入步驟例如以下:
- ATTATCH,指定目標進程,開始調試;
- GETREGS,擷取目標進程的寄存器,儲存現場;
- SETREGS,改動PC等相關寄存器,使其指向mmap;
- POPETEXT,把so path寫入mmap申請的地址空間;
- SETRESG,改動PC等相關寄存器,使其指向dlopen;
- SETREGS,恢複現場;
- DETACH,解除調試,使其恢複;
上述是一個簡化的過程,整個注入的代碼,我已經上傳到github,地址https://github.com/boyliang/Poison當so被dlopen載入到目標進程後,我們須要讓so中的邏輯被運行,比較複雜的做法是相同使用ptrace改動寄存器的辦法,讓目標進程調用dlsym找到我們函數的地址。而比較簡單的做法有兩種,例如以下
- 使用gcc的先行編譯指令__attribute__ ((__constructor__)),作用是讓so被載入後,函數被自己主動運行;
__attribute__ ((__constructor__))void Main() { LOGI(">>>>>>>>>>>>>I am in, I am a bad boy 1!!!!<<<<<<<<<<<<<<"); void* handle = dlopen("libinso.so", RTLD_NOW); void (*setA_func)(int) = (void (*)(int))dlsym(handle, "setA"); if (setA_func) { setA_func(999); }}
- 使用c++全域對象初始化,其建構函式會被自己主動運行;
void Main();static void* _main(void*){Main();return NULL;}class EntryClass {public:EntryClass() {pthread_t tid;pthread_create(&tid, NULL, _main, NULL);pthread_detach(tid);}} boy;
示範範例一以下示範範例一個通過ptrace注入的示範範例,涉及到兩部分代碼,一部分是目標進程代碼記作host,還有一部分是被我們注入的so代碼記作libmyso.so
Host程式碼封裝括三個源檔案,各自是demo1.c,inso.h, inso.c
/* * inso.h * * Created on: 2014年6月24日 * Author: boyliang */__attribute__ ((visibility ("default"))) void setA(int i);__attribute__ ((visibility ("default"))) int getA();
/* * inso.c * * Created on: 2014年6月24日 * Author: boyliang */#include <stdio.h>#include "inso.h"static int gA = 1;void setA(int i){gA = i;}int getA(){return gA;}
/* * demo1.c * * Created on: 2014年6月24日 * Author: boyliang */#include <stdio.h>#include <unistd.h>#include "inso.h"#include "log.h"int main(){LOGI("DEMO1 start.");while(1){LOGI("%d", getA());setA(getA() + 1);sleep(2);}return 0;}libmyso.so代碼
/* * myso.c * * Created on: 2014年6月24日 * Author: boyliang */#include <stdio.h>#include <stddef.h>#include <dlfcn.h>#include <pthread.h>#include <stddef.h>#include "log.h"__attribute__ ((__constructor__))void Main() {LOGI(">>>>>>>>>>>>>I am in, I am a bad boy 1!!!!<<<<<<<<<<<<<<");void* handle = dlopen("libinso.so", RTLD_NOW);void (*setA_func)(int) = (void (*)(int))dlsym(handle, "setA");if (setA_func) {setA_func(999);}}調用注入程式,我將其命名為poison,用法是poison <so_path> <target_pit>。以下是示範範例的輸出顯示:
I/TTT ( 594): DEMO1 start.I/TTT ( 594): 1I/TTT ( 594): 2I/TTT ( 594): 3I/TTT ( 594): 4I/TTT ( 594): 5I/TTT ( 594): 6I/TTT ( 594): 7I/TTT ( 594): >>>>>>>>>>>>>I am in, I am a bad boy 1!!!!<<<<<<<<<<<<<<I/TTT ( 594): 999I/TTT ( 594): 1000I/TTT ( 594): 1001
當運行./poison /data/local/tmp/libmyso.so 594後,輸出中立即出現了特定字串,而且列印的資料一下子變成了999,證明我們注入成功了。示範範例代碼上述示範範例所涉及到代碼,我都放公布到github上了,大家假設想研究代碼,能夠到https://github.com/boyliang/injection_by_ptrace在《三》,我會再介紹一種Android上特有的注入技術,敬請期待。
進擊的Android注入術《二》