1. 程式人生 > >android 反編譯 反除錯入門資料

android 反編譯 反除錯入門資料

0x01 反編譯出錯

1.插入無效指令是部分逆向工具崩潰

原理:大部分逆向工具都是線性讀取位元組碼並解析, 如dex2jar,baksmali,apktool等,當遇到無效位元組碼時,就會引起反編譯工具解析失敗。
例如:新版的dex2jar 遇到這種情況任然沒法轉化成jar,在新版本的baksmail和apktool已修復此問題。


010editor檢視,紅色框中就是加入的陷阱類,繞過方法很簡單,只要將這個三個類刪除,重編譯即可

2.利用apk包本質上是zip/jar包進行保護

a.偽加密

在Android4.2.x前的一種保護方 式,通過APK(壓縮檔案)進行偽加密,其修改原理是修改連續4位位元組標記為“P K 01 02”後的第5位元組(ps:一般在檔案末尾有多處),奇數表示加密偶數不加密,這種保護方式一般只會出現在一些cm裡,因為:
1.對系統不相容;
2.偽加密 處理後的apk市場也無法對其進行安全檢測,部分市場會拒絕這類APK上傳市場


b. 檔名長度操作

Android平臺對檔名的長度是沒有限制的,但是作業系統要求不能大於255;於是可以構建超長字元的類名達到反編譯錯誤目的。

3.axml檔案保護

目的:防止/檢測二次打包

android在解析AXMl檔案時,是用過屬性的資源id,而不是資源名,當android系統遇到非法資源id時,並不會做解析,說以可以對axml檔案解析,新增無用的屬性。但是對於破解者來說,一般會在java層增加log資訊,然後打包apk, 此時如apktool之類的逆向工具,就會無法解析無效屬性,或進入trap類(檢測二次打包)

0x02 執行環境檢測

環境檢測主要包括

  1. ▏執行在除錯狀態下
  2. ▏系統程式碼被hook
  3. ▏以及執行環境在模擬器中

目的:為了保護關鍵程式碼被逆向分析,一般放在應用程式初始化過程中,如init_array,或jni_onload函式裡進行檢查程式碼執行。

1.除錯檢測

對偵錯程式的檢測(ida,gdb,strace, ltrace等除錯工具)

a.父程序檢測

b.當前執行程序檢測

例如對android_server程序檢測。針對這種檢測只需將android_server改名就可繞過

  1. pid_t GetPidByName(constcharchar *as_name) {  
  2.         DIR *pdir = NULL;  
  3.         structdirent *pde = NULL
    ;  
  4.         FILEFILE *pf = NULL;  
  5.         char buff[128];  
  6.         pid_t pid;  
  7.         char szName[128];  
  8.         // 遍歷/proc目錄下所有pid目錄  
  9.         pdir = opendir("/proc");  
  10.         if (!pdir) {  
  11.                 perror("open /proc fail.\n");  
  12.                 return -1;  
  13.         }  
  14.         while ((pde = readdir(pdir))) {  
  15.                 if ((pde->d_name[0] < '0') || (pde->d_name[0] > '9')) {  
  16.                         continue;  
  17.                 }  
  18.                 sprintf(buff, "/proc/%s/status", pde->d_name);  
  19.                 pf = fopen(buff, "r");  
  20.                 if (pf) {  
  21.                         fgets(buff, sizeof(buff), pf);  
  22.                         fclose(pf);  
  23.                         sscanf(buff, "%*s %s", szName);  
  24.                         pid = atoi(pde->d_name);  
  25.                         if (strcmp(szName, as_name) == 0) {  
  26.                                 closedir(pdir);  
  27.                                 return pid;  
  28.                         }  
  29.                 }  
  30.         }  
  31.         closedir(pdir);  
  32.         return0;  
  33. }  

c.讀取程序狀態(/proc/pid/status)

State屬性值T 表示除錯狀態,TracerPid 屬性值正在除錯此程序的pid,在非除錯情況下State為S或R, TracerPid等於0

d.讀取 /proc/%d/wchan

下圖中第一個紅色框值為非除錯狀態值,第二個紅色框值為除錯狀態:

  1. staticvoid get_process_status(pid_t pid,constchar* info,charchar *outline)  
  2. {  
  3.       FILEFILE *fp;  
  4.       char filename;  
  5.       char line = {0};  
  6.       snprintf( filename, sizeof(filename), "/proc/%d/status", pid );  
  7.       fp = fopen( filename, "r" );  
  8.       if ( fp != NULL )  
  9.       {  
  10.                 while ( fgets( line, sizeof(line), fp ) )  
  11.                 {  
  12.                         if ( strstr( line, info ) )  
  13.                               strcpy(outline,line);  
  14.                 }  
  15.                 fclose( fp ) ;  
  16.       }  
  17.       return ;  
  18. }  
  19. staticint getProcessStatus(int pid)  
  20. {  
  21.       char readline = {0};  
  22.       int result = STATUS_ELSE;  
  23.       get_process_status(pid,"State",readline);  
  24.       if(strstr(readline,"R"))        
  25.                 result = STATUS_RUNNING;  
  26.       elseif(strstr(readline,"S"))  
  27.                 result = STATUS_SLEEPING;  
  28.       elseif(strstr(readline,"T"))  
  29.                 result = STATUS_TRACING;  
  30.       return result;  
  31. }  
  32. staticint getTracerPid(int pid)  
  33. {  
  34.       char readline = {0};  
  35.       int result = INVALID_PID;  
  36.       get_process_status(pid,"TracerPid",readline);  
  37.       charchar *pidnum = strstr(readline,":");  
  38.       result = atoi(pidnum + 1);  
  39.       return result;  
  40. }  
  41. staticint getWchanStatus(int pid)  
  42. {  
  43.       FILEFILE *fp= NULL;  
  44.       char filename;  
  45.       char wchaninfo = {0};  
  46.       int result = WCHAN_ELSE;  
  47.       char cmd = {0};  
  48.       sprintf(cmd,"cat /proc/%d/wchan",pid);  
  49.       LOGANTI("cmd= %s",cmd);  
  50.       FILEFILE *ptr;         if((ptr=popen(cmd, "r")) != NULL)  
  51.       {  
  52.                 if(fgets(wchaninfo, 128, ptr) != NULL)  
  53.                 {  
  54.                         LOGANTI("wchaninfo= %s",wchaninfo);  
  55.                 }  
  56.       }  
  57.       if(strncasecmp(wchaninfo,"sys_epoll\0",strlen("sys_epoll\0")) == 0)  
  58.                 result = WCHAN_RUNNING;  
  59.       elseif(strncasecmp(wchaninfo,"ptrace_stop\0",strlen("ptrace_stop\0")) == 0)  
  60.                 result = WCHAN_TRACING;  
  61.       return result;  
  62. }  

e. ptrace 自身或者fork子程序相互ptrace

  1. ptrace me  
  2. if (ptrace(PTRACE_TRACEME, 010) < 0) {  
  3. printf("DEBUGGING... Bye\n");  
  4. return1;  
  5. }  
  6. void anti_ptrace(void)  
  7. {  
  8.     pid_t child;  
  9.     child = fork();  
  10.     if (child)  
  11.       wait(NULL);  
  12.     else {  
  13.       pid_t parent = getppid();  
  14.       if (ptrace(PTRACE_ATTACH, parent, 00) < 0)  
  15.             while(1);  
  16.       sleep(1);  
  17.       ptrace(PTRACE_DETACH, parent, 00);  
  18.       exit(0);  
  19.     }  
  20. }  

f. 防止dump

利用Inotify機制,對/proc/pid/mem和/proc/pid/pagemap檔案進行監視。inotify API提供了監視檔案系統的事件機制,可用於監視個體檔案,或者監控目錄。具體原理可參考:http://man7.org/linux/man- pages/man7/inotify.7.html
虛擬碼:
  1. void __fastcall anitInotify(int flag)  
  2. {  
  3.       MemorPagemap = flag;  
  4.       charchar *pagemap = "/proc/%d/pagemap";  
  5.       charchar *mem = "/proc/%d/mem";  
  6.       pagemap_addr = (charchar *)malloc(0x100u);  
  7.       mem_addr = (charchar *)malloc(0x100u);  
  8.       ret = sprintf(pagemap_addr, &pagemap, pid_);  
  9.       ret = sprintf(mem_addr, &mem, pid_);  
  10.       if ( !MemorPagemap )  
  11.       {  
  12.                 ret = pthread_create(&th, 0, (voidvoid *(*)(voidvoid *)) inotity_func, mem_addr);  
  13.                 if ( ret >= 0 )  
  14.                    ret = pthread_detach(th);  
  15.       }  
  16.       if ( MemorPagemap == 1 )  
  17.       {  
  18.                 ret = pthread_create(&newthread, 0, (voidvoid *(*)(voidvoid *)) inotity_func, pagemap_addr);  
  19.                 if(ret > 0)  
  20.                   ret = pthread_detach(th);  
  21.       }  
  22. }  
  23. void __fastcall __noreturn inotity_func(constcharchar *inotity_file)  
  24. {  
  25.       constcharchar *name; // [email protected]
  26.       signedint fd; // [email protected]
  27.       bool flag; // [email protected]
  28.       bool ret; // [email protected]
  29.       ssize_t length; // [email protected]
  30.       ssize_t i; // [email protected]
  31.       fd_set readfds; // @2
  32.       char event; // @1
  33.       name = inotity_file;  
  34.       memset(buffer, 00x400u);  
  35.       fd = inotify_init();  
  36.       inotify_add_watch(fd, name, 0xFFFu);  
  37.       while ( 1 )  
  38.       {  
  39.                 do
  40.                 {  
  41.                         memset(&readfds, 00x80u);  
  42.                 }  
  43.                 while ( select(fd + 1, &readfds, 000) <= 0 );  
  44.                 length = read(fd, event, 0x400u);  
  45.                 flag = length == 0;  
  46.                 ret = length < 0;  
  47.                 if ( length >= 0 )  
  48.                 {  
  49.                         if ( !ret && !flag )  
  50.                       {  
  51.                               i = 0;  
  52.                               do
  53.                               {  
  54.                                         inotity_kill((int)&event);  
  55.                                         i += *(_DWORD *)&event + 16;  
  56.                               }  
  57.                               while ( length > i );  
  58.                         }  
  59.                 }  
  60.                 else
  61.                 {  
  62.                         while ( *(_DWORD *)_errno() == 4 )  
  63.                         {  
  64.                               length = read(fd, buffer, 0x400u);  
  65.                               flag = length == 0;  
  66.                               ret = length < 0;  
  67.                               if ( length >= 0 )  
  68.                         }  
  69.                 }  
  70.       }  
  71. }  

g. 對read做hook

因為一般的記憶體dump都會呼叫到read函式,所以對read做記憶體hook,檢測read資料是否在自己需要保護的空間來阻止dump

h. 設定單步除錯陷阱

  1. int handler()  
  2. {  
  3.     return bsd_signal(50);  
  4. }  
  5. int set_SIGTRAP()  
  6. {  
  7.     int result;  
  8.     bsd_signal(5, (int)handler);  
  9.     result = raise(5);  
  10.     return result;  
  11. }  

2. 模擬器檢測

使用者層行為和資料檢測,模擬器特有屬性值,以及模擬器體系結構特徵
  1. ▏電池狀態和電流,模擬器預設電話號碼檢測,檢測裝置IDS 是不是“000000000000000”, 檢測imsi id是不是“310260000000000“,手機運營商等
  2. ▏API Demo,Dev tool一般模擬器上才有的應用,檢測安裝應用,簡訊箱,通訊錄,相簿等
  3. ▏讀取/system/build.prop檔案
  4. ▏呼叫__system_property_get,或反射呼叫Systemproperty.get獲取系統屬性值
  5. ▏通過執行shell命令檢測模擬器,如getprop
  6. ▏檢查模擬器特有檔案如 /dev/socket/qemud","/dev/qemu_pipe","/sysrtem/bin/qemud",/dev/qemu_pipe,/dev/qemu_trace等
  7. ▏模擬器cpu資訊值差異,如hardware,Revision等
  8. ▏系統屬性值等(android.os.build)
  9. ▏基於Qemu二進位制翻譯技術(ps:真機具有真正的物理CPU,在執行一段指令的時候只能一條一條的去執行指令(編譯器沒有對指令進行優化的前提下)。模擬器沒有真正的物理CPU,所以,他在執行一段指令的時候,這段指令已經被人為的優化掉)http://www.dexlabs.org/blog/btdetect
  10. ▏通過觀察低級別的快取行為。檢測方法: 預設情況下,Android模擬器提供了Android SDK是基於QEMU,模擬器不具備分裂快取。而在真實裝置上存在兩個不同的快取,一個用於資料訪問,一個用於指令。https://bluebox.com/technical/android-emulator-detection-by-observing-low-level-caching-behavior/

0x03防app執行環境被hook

hook程式碼肯定是在app自身模組載入之前執行的,那麼在app的maps表裡會首先載入hook框架的dex,我們只要對此dex做簡單的校驗,就會檢測到app被注入了。
編碼思路:

遍歷maps表,檢視子串是否存在“[email protected]”的字串,若存在獲取該模組的startAddr和endAddr, 然後檢驗此odex的頭部是否為真正的dex檔案。

0x4抗靜態分析(ida F5 以及執行流程圖)

1.arm 指令插花

2.通過棧修改程式呼叫過程

STDFD儲存暫存器值到棧上,LDMFD將棧上資料賦值到暫存器中,這個過程修改了函式返回的地址。

0x05如何繞過app環境檢測

1.除錯過程中修改程式碼

hook檢測點(重定向函數出關鍵檔案操作,函式返回值修改等),修改原始碼(改變欄位屬性主要針對模擬器模擬,修改函式返回值,例如繞過簽名校驗等)
eg:
ida patch 執行緒退出函式: patch 地址48D9668A處的函式呼叫

a.在資料視窗定位到機器碼位置

b.F2 編輯機器碼 00 00 0A EF (movs R0, R0)

c.F2儲存修改

2.fopen函式相關的檢測

由於/proc/pid/status,/proc/pid/wchan,/proc/pid/mem等都是針對檔案狀態的檢測,入口點函式一般都為fopen, 我們可以事先攔截fopen,檢視app是否左右這方面的防護。
以某某app為例:如下圖此app fopen了這些檔案,我們就能猜測這是對除錯檢測。

繞過方法:在指定目錄下(/data/local/tmp)新建一檔案alimolisec, hook fopen函式檢測到檔名子串有/proc/self時,就重定位開啟alimolisec檔案
  1. FILEFILE * MyOpen( constcharchar * filename, constcharchar * mode ) {  
  2.       FILEFILE *file = NULL;  
  3.       if (strstr(filename, "/proc/self")){  
  4.                 LOGI("fileName:%s", filename);  
  5.                 file = oldFopen("/data/local/tmp/hone", mode );  
  6.       }else
  7.                 file = oldFopen( filename, mode );  
  8.       return file;  
  9. }  

0x06 CM中的那些殼子

加固手法:

1.不替換源classes.dex,也沒有做任何加密的處理,對classes.dex中的Activity,service, receiver
等的oncreate,onReceive,加密替換,殼首先拿到執行權,在自身so裡完成對源dex還原。
2.不替換源classes.dex,也沒有做任何加密的處理, 修改原Dex的Class_Data,將
MyContentProvider,Application,Activity,service類的入口函式 onCreate方法標記為native方
法,但是原始位元組碼仍然未加密儲存在dex檔案中。
3.對classes.dex整包加密,使用殼載入器記憶體解密classes.dex,並替換原始成源classes.dex
4.對classes.dex整包加密,並將原dex拆分成兩部分,在記憶體中分兩塊區域儲存。

脫殼手法:

1.對部分整包加密的可以通過執行時memory dump,部分通過攔截dvmDexFileOpenPartial函
數即可獲取完整dex。(dex連續)
2.對於修改了dex的Class_data和classes.dex做了拆分的,可以通過找到dex對應的pDvmDex

結構,重建dex(dex不連續或不完整的)

0x07 android平臺常用的hook框架

▏cydia substrate。原理:框架注入zygote程序,採用inline hook( 修改目標函式前N位元組,跳轉到自定義函式入口,備份目標函式前N個位元組,跳轉回目標函式)
▏Xposed。原理:替換app_process,將需要hook的java函式替換成JNI函式,所有需要HOOK的函式首先由xposedCallHandler處 理,xposedCallHandler負責呼叫註冊的beforeHookMethod和afterHookedMethod

▏adbi。原理:利用ptrace()函式attach到一個程序上,然後在其呼叫序列中插入一個呼叫dlopen()函式的步驟,將一個實現預先備好的.so檔案載入到要hook的程序中,最終由這個載入的.so檔案在初始化化函式中hook指定的函式。

參考文件

http://man7.org/linux/man-pages/man7/inotify.7.html
http://www.dexlabs.org/blog/btdetect
https://bluebox.com/technical/android-emulator-detection-by-observing-low-level-caching-behavior/
https://github.com/crmulliner/adbi
http://www.cydiasubstrate.com/

相關推薦

android 編譯 除錯入門資料

0x01 反編譯出錯 1.插入無效指令是部分逆向工具崩潰 原理:大部分逆向工具都是線性讀取位元組碼並解析, 如dex2jar,baksmali,apktool等,當遇到無效位元組碼時,就會引起反編譯工具解析失敗。例如:新版的dex2jar 遇到這種情況任然沒法轉化成jar,

.net破解一(編譯,混淆-剝殼)

http://www.cnblogs.com/jio92/p/de4dot.html     大家好,前段時間做資料分析,需要解析對方資料,而資料檔案是對方公司內部的生成方式,完全不知道它是怎麼生成的. 不過還好能拿到客戶端(正好是C#開發)所以第一件事就是用Refl

android 系統編譯要求,官方資料

This section describes how to set up your local work environment to build the Android source files. You will need to use Linux or Mac OS. Building unde

ANDROID編譯工具APKTOOL原始碼除錯過程

APKTOOL是目前反編譯ANDROID apk的主要工具,可以反編譯出APK裡的資原始檔和smali程式碼,而有些APK開發者為了防止自己的程式碼被反編譯,針對APKTOOL工具加固程式碼,使得APKTOOL無法執行正常,比如這篇文章介紹的Android應用資

自己收集的資料 僅供學習使用——————Android編譯(經驗證)

在學習Android開發的過程你,你往往會去借鑑別人的應用是怎麼開發的,那些漂亮的動畫和精緻的佈局可能會讓你愛不釋手,作為一個開發者,你可能會很想知道這些效果介面是怎麼去實現的,這時,你便可以對改應用的APK進行反編譯檢視。下面是我參考了一些文章後簡單的教程詳解。 (注

android編譯odex文件

lis 目錄 trac 依賴 andro xheditor span sys 放置 關於android的反編譯工具,相信大家並不陌生 如APK-TOOL,dex2jar APK-TOOL 用於反編譯出布局文件 下載地址http://code.google.co

Android Cocos2dx引擎 prv.ccz/plist/so等優化緩存文件,手把手ida教你逆向project編譯apk庫等文件

反編譯 packer ng- ace yaffs2 ces spa 即將 放棄 前段時間在 Android play 上看到一個非常牛逼的 3D 動態天氣預報,效果真的非常炫。二話不說動手 dex2jar.bat/apktool 發現這並沒 有什麽卵用,在核心的地方看見

Android Apk的編譯和加密

打不開 源文件 資源 left 結果 blog 9.png 規則 找到   這幾天在上海出差,忙裏偷閑學習了一下Apk的反編譯工具的基本使用。下面就簡單介紹一下如何將我們從網上下載的Apk文件進行反編譯得到我們想要獲得的資源文件和源碼。 一、Apk文件組成   Androi

Android對apk源代碼的改動--編譯+源代碼改動+又一次打包+簽名【附HelloWorld的改動實例】

str 技術 命令行 文章 color 不同的 pop ogl 方法 最近遇到了須要改動apk源代碼的問題,於是上網查了下相關資料。編寫了HelloWorld進行改動看看可行性,經過實驗證明此方案可行,而且後來也成功用這種方法對目標apk進行了改動,僅僅只是

Android APK編譯就這麽簡單 詳解(附圖)

雙擊 整合 cmd 進行 自我 nts clas 以及 思路 在學習Android開發的過程你,你往往會去借鑒別人的應用是怎麽開發的,那些漂亮的動畫和精致的布局可能會讓你愛不釋手,作為一個開發者,你可能會很想知道這些效果界面是怎麽去實現的,這時,你便可以對改應用的APK進行

Android APK編譯 apktool使用教程

文件夾 ossh ng- 右鍵 xmu shape func prev pos 2017年棋牌遊戲突然就火了,正所謂春江水暖鴨先知本猿處在軟件行業中就能清晰的感受到市場的變化,最近老家那邊也是玩的風生水起,於是最近閑暇時光想到反編譯下這些棋牌軟件,看看代碼實現的思路

AndroidAndroid如何對APK編譯

輸入 details href 樣式 class 圖片 logs apk反編譯 com 本文筆者粗略的介紹如何利用一些工具,對Android進行反編譯,從而得到源碼,希望對你有所幫助,筆者的android環境為4.4.2。 1.準備資源. 在開始之前,需要準備三項工具:

Android APK編譯

exe 下載 最新 free size 輸出 布局文件 最新版本 style 1.官方最新版本下載地址 http://java.decompiler.free.fr/?q=jdgui 2.反編譯流程: ①dex2jar (將apk反編譯成java源碼(classe

android編譯

adbc nsh cef vfs weixin 反編譯 target get blank 轉自這位老鐵:https://www.jianshu.com/p/f5fadbc7d2f8 https://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2M

Android編譯和二次打包

犯錯 解壓 就會 field har stat 生成 https make 參考:APK反編譯 一、工具介紹: 1、解壓工具 2、JDK 3.apktool: aapt.exe,apktool.bat,apktool.jar;三個在同一目錄結合使用,用來反編譯apk

編譯網易雲音樂Android App

起因 找到工作之後 鹹魚好長時間了,不該在繼續鹹魚了 ,該繼續學習啦。 之前就想看看 網易雲音樂 內部使用了那些開源框架,今天有時間就分析一下吧、 ClassyShark 先推薦一款(反編譯) 應用—ClassyShark,谷歌開源的 下載地址:https://github.

解開編譯android o的框架程式碼

華為mate10使用了最新的androido,並進行了預編譯,然後baksmali現在還不支援androido,導致反編譯困難,於是自己動手diy   1、        adb pull /syste

Android 編譯&回編譯

1.下載ShakaApktool工具 1.從github上下載ShakaApktool GitHub 2.為了方便,已在csdn上傳資源ShakaApktool 2.刪除old框架 window環境: 進入目錄,C:\Users\使用者名稱\apktool\framew

Python爬蟲:mac環境apktool編譯Android安卓程式碼

所需工具 Java環境 apktool:反編譯APK檔案,得到classes.dex檔案 dex2jar:將反編譯後的classes.dex檔案轉化為.jar檔案 jd-gui:用於將.jar檔案轉換成java程式碼 以上所需的工具打包下載: 連結

android編譯工具

apktool 命令 https://ibotpeaches.github.io/Apktool/install/ apktool d testapp.apk d2j-dex2jar -f beautifulcamera.apk dex2jar https://git