上一次分析了Cydia Substrate so hook 框架的實現,實際使用中,發現這樣的框架并不能滿足我的一些需求,比如我要知道一個函數內部某處代碼的運行時的寄存器值,用原始的框架就無法做到。
想實現的功能是只要指定一個地址,就可以打印該處代碼執行時的寄存器環境、HOOK的地址以及線程的TID,同時支持多個地址的添加。
先實現一個通用的消息打印函數
void printAllReg(int Reg_R0,int Reg_R1,int Reg_R2,int Reg_R3,int Stack_SP,int Stack_SP2)
{
int *pSP2 = &Stack_SP2;
int Reg_R12 = *(pSP2+1);
int Reg_R11 = *(pSP2+2);
int Reg_R10 = *(pSP2+3);
int Reg_R9 = *(pSP2+4);
int Reg_R8 = *(pSP2+5);
int Reg_R7 = *(pSP2+13);
int Reg_R6 = *(pSP2+12);
int Reg_R5 = *(pSP2+11);
int Reg_R4 = *(pSP2+10);
char *pFormat="Hookaddr:0x%08x tid=%x R0:0x%08x R1:0x%08x R2:0x%08x R3:0x%08x
R4:0x%08xR5:0x%08x R6:0x%08x R7:0x%08x R8:0x%08x R9:0x%08x R10:0x%08x R11:0x%08xR12:0x%08x ";
LOGD(pFormat,(Stack_SP2-1-(int)pSSELibBase),gettid(),Reg_R0,Reg_R1,Reg_R2,Reg_R3,Reg_R4,Reg_R5,Reg_R6,Reg_R7,Reg_R8,Reg_R9,Reg_R10,Reg_R11,Reg_R12);
}
里面具體的參數意義后面再講。
printAllReg要由一個中轉函數來調用,這個函數必須滿足如下要求:
1.調用前后不能改變R0-R12,LR寄存器的值
2.調用完打印函數后,跳到old函數
3.把監控地址入棧,這個要求比較關鍵,不然打印出來不知道是哪處代碼在打印,也就沒有意義了。
根本滿足這三點要求,C語言函數肯定是不行的,只有用arm匯編來實現了
void HookAddr()
{
__asm__(
"PUSH {R0-R7}
"//保存可能會改變的寄存器
"mov r4,r8
"
"PUSH {R4}
"
"mov r4,r9
"
"PUSH {R4}
"
"mov r4,r10
"
"PUSH {R4}
"
"mov r4,r11
"
"PUSH {R4}
"
"mov r4,r12
"
"PUSH {R4}
"//將r8-r12入棧,直接push {R8}這樣的指令是不支持的
"ldr r7,=0x1a2b3001
"//0x1a2b3001是什么意思?
"PUSH {R7}
"
"PUSH {LR}
"//將返回地址入棧,printAllReg執行完后,lr寄存器會變掉
"ldr r7, =printAllReg
"//將兩個地址入棧后,調用打印函數
"BLX r7
"
"POP {R7}
"
"mov lr,r7
"//恢復lr
"POP {R7}
"
"POP {R4}
"
"mov r12,r4
"//r12經過rlx后,會變掉,這里專門恢復一下
"POP {R0-R3}
" //平衡堆棧
"POP {R0-R7}
" //恢復r0-r7
"MOV R0,R0
" //讓下面的bx pc指令4字節對齊
"BX PC
"//轉到arm模式,以進行跳轉
"MOV R0,R0
"//占位
"MOV R0,R0
"//占位,這里后面會被patch掉
"MOV R0,R0
"//占位,這里后面會被patch掉
"MOV R0,R0
"//占位,這里后面會被patch掉
"BX LR
"
);
這段函數經過編譯后,用IDA反匯編的結果如下:
紅框里的指令都是我們需要patch的指令
0x1A2B3001為這值占用了四個字節,這個值是要入棧給printAllReg的,我們在這里寫入HOOK的地址,就能被printAllReg函數所用了。
再回頭來看下printAllReg里int Reg_R12 =*(pSP2+1);這樣指令的作用,從中轉函數我們知道,調用printAllReg時,堆棧里值是這樣排列的LR,HOOK Addr,R12,R11,R10,R9,R8,R7,R6,R5,R4,
Stack_SP2指向的就是HookAddr,向后遞推,就可以把R12-R4的地址讀出來了,當然也可以把改下printAllReg的調用界面,把這些值全放參數里傳過來。
第一個紅框,patch成跳轉指令,0x16e0放把下四個字節內容加載到PC的指令,0x16e4放跳轉地址
使用如下函數進行patch
經過這個函數patch后的中轉函數,如下圖所示
每個hook都會分配一段代碼內存用于保存這樣的中轉函數,以后需要監控一個指定函數時,只需要調用setAddrHook,輸入地址就可以了。限于Cydia Substrate框架的限制,輸入的hook地址指向的前面12個字節最好不要被跳轉所分割。
也可以按此方法對HOOK函數的過濾函數進行通用化改造,實現輸入函數地址即可打印函數調用信息的界面。
文章來源:http://chenjava.blog.51cto.com/374566/1591851
下一篇:Ossim系統常見測試方法