編輯:關於Android編程
0x00
為了避免我們的so文件被動態分析,我們通常在so中加入一些反調試代碼,常見的Android native反調試方法有以下幾種。
1、直接調用ptrace(PTRACE_TRACEME, 0, 0, 0),參考Android Native反調試。
2、根據上面說的/proc/$pid/status中TracerPid行顯示調試程序的pid的原理, 可以寫一個方法檢查下這個值, 如果!=0就退出程序。參考Android Native反調試,用JNI實現APK的反調試。
3、檢查代碼執行的間隔時間,參考Android應用方法隱藏及反調試技術淺析的0×03反調試初探。
4、檢測手機上的一些硬件信息,判斷是否在調試器中,參考Android應用方法隱藏及反調試技術淺析的0×03反調試初探。
0x01
那麼我們如何過掉這些反調試呢?
我們以阿裡比賽第二題為例,參考安卓動態調試七種武器之孔雀翎 – Ida Pro。
我們講解兩種方式:
1、Ida Patch so
2、Ida動態修改內存數據和寄存器數值
我們首先講解Ida Patch so,有幾處都可以patch。我們從易到難依次講解。
第一處:
我們在JNI_ONLOAD下斷點,如下圖:
依次單步執行到BLX R7
我們發現當執行完這步後,我們的ida就退出了,說明反調試代碼是從這個入口進入執行的。那麼我們只要把這個入口給NOP掉,就可以繞過反調試了。
Patch so就是修改so中的二進制代碼,然後再重新簽名生成新的apk。Patch so,需要修改的本地so中的代碼,而不是內存中的,所以我們需要通過上圖內存中指令地址減去so在內存中的基地址來獲取這條指令在本地so文件中的偏移。那麼so在內存中的基地址怎麼獲取呢?按Crtl+s。
我們看到libcrackme.so的基地址是AB732000,用BLX R7的地址AB733C58減去AB732000,等於1C58。
然後我們雙開ida,在另一個ida中打開libcrackme.so,按G,然後輸入1C58,果然我們調到了BLX R7的位置,如下圖:
下面就要把這行代碼NOP,可以修改為00 00 00 00,也可以修改為00 00 A0 E1。
修改後點擊右鍵,applay change。然後重新簽名生成apk,再次運行apk,ida調試時就沒有反調試的干擾了。
第二處:
我們按F7進入BLX R7的內部執行,如下圖:
是創建了一個線程去執行反調試,這裡有個小技巧,如果我們想回到剛才的函數BLX R7,怎麼辦呢?選擇寄存器LR,然後點擊右鍵選擇Jump即可,同理選擇PC是跳到當前的位置。
這個線程執行的函數體是sub_AB7336A4,如下圖:
sub_AB7336A4函數體如下:
這個方法循環執行sub_AB73330C。我們進入sub_AB73330C,懷疑這裡就是真正檢查是否處於調試狀態的地方,但是代碼經過了嚴重的混淆,所以找不到反調試的代碼。
那怎麼辦呢?在0x00中我們談到了常見的反調試代碼,最常見的是第二種方式,第二種方式檢查的過程中會調用fopen,所以我們在libc的fopen方法下斷點,來是哪個函數調用的fopen,基本上就可以斷定這個函數是反調試代碼。
首先我們需要找到fopen的位置,按Alt+T,然後輸入fopen關鍵字,如下圖:
找到fopen後,代碼是這樣的:
此時按P,就可以變成代碼形式。如下圖,在fopen處下斷點。
點擊F9,繼續運行,我們看到程序停在fopen處,此時LR就是剛剛我們談到的sub_AB73330C,如下圖:
所以我們可以確定sub_AB73330C就是進行反調試的代碼。我們可以看到這個函數是被sub_AB7336A4調用的。
點擊右側的CODE XREF:sub_AB7336A4就能進入到調用sub_AB73330C的地方。在sub_AB7336A4函數上按F5,就能看到對應的C語言代碼。如下:
可見程序是在sub_AB73330C循環檢測是否被反調試的。
此時我們可以用和第一處一樣的方式,找到本地so中對應的方法,然後Patch so,Nop掉對應的方法,然後重新簽名,重新運行。
第三處:
其實第三處和第二處原理是一樣的,只不過這裡不使用Nop了,sub_AB73330C開始和結束的匯編代碼如下:
開始時:
結束時:
所以我們可以把AB733310的代碼修改為AB73363C處的代碼,不執行任何操作,直接返回。
0x02
Ida動態修改內存數據和寄存器數值
我們看到反調試方法第二點,代碼如下:
void be_attached_check() { try { const int bufsize = 1024; char filename[bufsize]; char line[bufsize]; int pid = getpid(); sprintf(filename, "/proc/%d/status", pid); FILE* fd = fopen(filename, "r"); if (fd != nullptr) { while (fgets(line, bufsize, fd)) { if (strncmp(line, "TracerPid", 9) == 0) { int statue = atoi(&line[10]); LOGD("%s", line); if (statue != 0) { LOGD("be attached !! kill %d", pid); fclose(fd); int ret = kill(pid, SIGKILL); } break; } } fclose(fd); } else { LOGD("open %s fail...", filename); } } catch (...) { } }我們發現該程序會用fopen ()打開/proc/[pid]/status這個文件,隨後會用fgets()和strcmp()來比較,於是我們在strcmp()處下個斷點,然後讓hex view的數據與R0同步。每次點擊繼續,我們都會看到strstr傳入的參數。當傳入的參數變為TracerPid:XXXX的時候我們停一下。因為在正常情況下,TracerPid的值應該是0。但是當被調試的時候就會變成調試器的pid。
我們在strcmp下斷點:
程序會在此處斷下,當我們發現R0地址中的內容為TracerPid:XXXX時,我們停一下,如下圖:
R0的裡面存的地址是AB731B25,裡面的內容為,如下圖:
我們可以通過修改內存值的方式來過掉這一次反調試。
把TracerPid改為0,如下圖:
然後點擊Apply changes。這樣就可以過掉這次反調試。我們在前面也看到了,程序是在一個循環中進行反調試檢查,所以這樣的方試只是過了其中一次反調試。
不推薦使用這種方式,最好使用Patch So的方式。
最近在做一個Android位圖陰影處理的效果,今天把思路總結一下:分析如下:Android sdk提供了BlurMaskFilter工具類可以給圖片添加陰影效果,代碼如下
AMS對startActivity請求處理及返回過程根據上一章的分析了解了調用startActivity(),終於把數據和要開啟Activity的請求發送到了AMS了,接
任務棧簡單入門??最近又把兩本進階書看了一遍,但總感覺好記性不如爛筆頭,所以還是決定通過博客記錄一下,我們將分兩篇來全面深入地記錄Activity 啟動模式與任務棧的內容
Github地址:https://github.com/coder-pig/AndroidStudio-Eat-Guide1.必須記住的快捷鍵:Ctrl+Shift+A