搞了好幾個星期,終于有點頭緒,歡迎交流。轉(zhuǎn)載請注明出處http://blog.csdn.net/lingfong_cool/article/details/8109290。 android安全可以從三個層面考慮,一次是java虛擬機層,nativec層,還有l(wèi)inux kernel層,本次hook api是屬于nativec層的。
hook api之后就可以使得程序?qū)υ邢到y(tǒng)函數(shù)的調(diào)用改為對我們自己編寫的函數(shù)的調(diào)用,這既可以作為一種攻擊手段,又可以在維持程序運行的情況下更新程序等等。下面談談思路以及實例(以打電話進程com.android.phone為例,項目保密起見,我修改了相關函數(shù)的名稱,但是思路絕對不假,這事我驗證過的)
1.向目標進程注入代碼(注入so,并調(diào)用該so里的一個函數(shù))。首先調(diào)用ptrace函數(shù),調(diào)試com.android.browser進程,在這里我們需要遍歷該進程加載的libc.so,這里有我們需要的dlopen,dlsym等函數(shù)的地址,我們先中斷com.android.phone,修改其寄存器,壓入?yún)?shù)如我們的so路徑,并將之前找到的dlopen地址壓入寄存器,直接操作blx,就可以讓目標進程調(diào)用dlopen加載我們的so,同理dlsym調(diào)用我們的so里的函數(shù)。這個已經(jīng)有大牛寫出來了,地址為http://www./downloads467/sourcecode/comm/android/detail1958833.html
下面就是我做的工作,重定向函數(shù)實現(xiàn)hook。
2.com.android.phone程序打電話等網(wǎng)絡連接時調(diào)用了xxx.so,該so維護了一個got表和rel.plt表。其中rel.plt表存放了外部依賴函數(shù)的地址,而got表里存放的就是本so定義的函數(shù)的地址。在上文被注入的so已經(jīng)和com.android.phone處于一個進程空間,并且可以執(zhí)行一段我們設定的代碼。我們的代碼應該這么做。我們也加載xxx.so,這里不會真正的加載,因為已經(jīng)加載過了,但是我們可以獲得xxx.so的句柄,然后查找到rel.plt表中的dial函數(shù)表項。然后加載我們寫的一個myxxx.so,該so里有我們自己定義的mydial函數(shù),注意兩個函數(shù)的簽名必須一致。同理我們找到mydial函數(shù)加載后的地址,然后將之前xxx.so的dial表項的函數(shù)地址替換為我們的mydial函數(shù)的地址。注意在地址替換時需要先調(diào)用mprotect函數(shù)來突破so內(nèi)存空間的寫保護。
在mydial函數(shù)里,我們copy了dial函數(shù)的全部代碼,但是有一個改變。就是將目標電話號碼修改為我們指定的號碼。
查找函數(shù)地址表項代碼為
//handle為目標so的句柄,name為目標函數(shù)名
void* getaddr(void *handle,const char *name)
{ if(!handle) return; Soinfo *si = (Soinfo*)handle; Elf32_Sym *symtab = si->symtab; const char *strtab = si->strtab; Elf32_Rel *rel = si->plt_rel; unsigned count = si->plt_rel_count; unsigned idx; for(idx=0; idx<count; idx++) //外部依賴函數(shù)在rel_plt中 { unsigned type = ELF32_R_TYPE(rel->r_info); unsigned sym = ELF32_R_SYM(rel->r_info); unsigned reloc = (unsigned)(rel->r_offset + si->base); char *sym_name = (char *)(strtab + symtab[sym].st_name); if(strcmp(sym_name, name)==0) { printf("\"plt_rel\" idx:%2d type:%2d sym:%2d sym_name:%-30s addr:%0x\n",idx,type,sym,sym_name,*((unsigned*)reloc)); return (void *)*((unsigned*)reloc); } rel++; } for(idx=0;idx<si->nchain;idx++) //自定義函數(shù)在symtab中 { unsigned type = ELF32_R_TYPE(symtab[idx].st_info); unsigned sym = ELF32_R_SYM(symtab[idx].st_info); char *sym_name = (char *)(strtab + symtab[idx].st_name); if(strcmp(sym_name, name)==0) { printf("\"got\" idx:%2d sym_name:%-30s st_value:%0x base: %0x\n",idx,sym_name,symtab[idx].st_value,si->base); return (void *)(symtab[idx].st_value+si->base); } }; return NULL; //not found }
至于替換函數(shù)執(zhí)行地址,就是將目標函數(shù)地址修改為之前找到的用于代替目標函數(shù)執(zhí)行的函數(shù)地址。注意got表中時相對so的base的值,需要加減兩個so的base差值。而rel.plt表中則是絕對地址。
從安全的角度入手,我們可以hook關鍵函數(shù),實現(xiàn)權限操作限制。
--------------------------------------------------------------------------------------------------------
防止注入的思路:
1.設置android:debuggable為false,禁止被ptrace;
2.修改mmap、dlopen等函數(shù)的地址,防止被其他進程調(diào)用;
3.動態(tài)監(jiān)測有無加載其他so,有的話就卸載它
|
|
來自: leomuyi > 《API-rewrite》