編輯:關於Android編程
通過取系統時間,檢測關鍵代碼執行耗時,檢測單步調試,類似函數有:time,gettimeofday,clock_gettime.
也可以直接使用匯編指令RDTSC讀取,但測試ARM64有兼容問題。
time_t t1, t2; time (&t1); /* Parts of Important Codes */ time (&t2); if (t2 - t1 > 2) { puts ("debugged"); }
進程的狀態信息能通過 procfs 系統反饋給用戶空間,調試會使進程狀態發生變化:
char file [PATH_LEN]; char line [LINE_LEN]; snprintf (file, PATH_LEN-1, "/proc/%d/status", pid); FILE *fp = fopen (file, "r"); while (fgets (line, LINE_LEN-1, fp)) { if (strncmp (line, "TracerPid:", 10) == 0) { if (0 != atoi (&line[10])) { /* encrypt random .TEXT code */ } break; } } fclose (fp);
類似可以檢測的接口還有:
/proc/pid/status /proc/pid/task/pid/status /proc/pid/stat /proc/pid/task/pid/stat /proc/pid/wchan /proc/pid/task/pid/wchan
ARM程序下斷點,調試器完成兩件事:
當命中斷點時,系統產生SIGTRAP信號,調試器收到信號後完成下面操作:
這時當控制權回到被調試程序時,正好執行斷點位置指令。這就是 ARM 平台斷點的基本原理。
可以看到,斷點是通過處理 SIGTRAP 信號來實現的,假如我們自己注冊 SIGTRAP 的信號處理函數,並在程序中主動執行中斷指令觸發中斷。
在中斷處理函數中,NOP 掉斷點指令,程序可正常執行。但在調試狀態下,調試器遇到斷點指令時,會去恢復原先指令,由於不是調試器下的斷點,所以恢復會失敗,而調試器會繼續第2步操作,回退PC寄存器,程序會在此處無限循環。
斷點會替換內存中原有指令,因此通過檢測內存中的斷點指令,可以檢測調試:
#include <stdlib.h> #include <stdio.h> #include <elf.h> #include <string.h> #include <unistd.h> #include <dlfcn.h> void checkBreakPoint (); unsigned long getLibAddr (const char *lib); #define LOG_TAG "ANTIDBG_DEMO" #include <android log.h=""> #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) int main () { dlopen ("./libdemo.so", RTLD_NOW); sleep (60); checkBreakPoint (); return 0; } unsigned long getLibAddr (const char *lib) { puts ("Enter getLibAddr"); unsigned long addr = 0; char lineBuf[256]; snprintf (lineBuf, 256-1, "/proc/%d/maps", getpid ()); FILE *fp = fopen (lineBuf, "r"); if (fp == NULL) { perror ("fopen failed"); goto bail; } while (fgets (lineBuf, sizeof(lineBuf), fp)) { if (strstr (lineBuf, lib)) { char *temp = strtok (lineBuf, "-"); addr = strtoul (temp, NULL, 16); break; } } bail: fclose(fp); return addr; } void checkBreakPoint () { int i, j; unsigned int base, offset, pheader; Elf32_Ehdr *elfhdr; Elf32_Phdr *ph_t; base = getLibAddr ("libdemo.so"); if (base == 0) { LOGI ("getLibAddr failed"); return; } elfhdr = (Elf32_Ehdr *) base; pheader = base + elfhdr->e_phoff; for (i = 0; i < elfhdr->e_phnum; i++) { ph_t = (Elf32_Phdr*)(pheader + i * sizeof(Elf32_Phdr)); // traverse program header if ( !(ph_t->p_flags & 1) ) continue; offset = base + ph_t->p_vaddr; offset += sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr) * elfhdr->e_phnum; char *p = (char*)offset; for (j = 0; j < ph_t->p_memsz; j++) { if(*p == 0x01 && *(p+1) == 0xde) { LOGI ("Find thumb bpt %p", p); } else if (*p == 0xf0 && *(p+1) == 0xf7 && *(p+2) == 0x00 && *(p+3) == 0xa0) { LOGI ("Find thumb2 bpt %p", p); } else if (*p == 0x01 && *(p+1) == 0x00 && *(p+2) == 0x9f && *(p+3) == 0xef) { LOGI ("Find arm bpt %p", p); } p++; } } }
inotify 是一個內核用於通知用戶態文件系統變化的機制,當文件被訪問,修改,刪除等時用戶態可以快速感知。
1.使用 inotify_init() 初始化一個 inotify 實例並返回文件描述符,每個文件描述符都關聯了一個事件隊列:
int fd = inotify_init ();
2.拿到這個文件描述符後下一步就告訴內核,哪些文件發生哪些事件時你得通知我,通過函數 inotify_add_watch 實現:
int wd = inotify_add_watch (fd, path, mask);
第一個參數即 inotify_init 返回的文件描述符,path 表示關注的目標路徑,可以是文件目錄等。mask 表示關注的事件的掩碼,如 IN_ACCESS 代表訪問,IN_MODIFY 代表修改等
相應的,可以通過 inotify_rm_watch 來刪除一個watch:
int ret = inotify_rm_watch (fd, wd);
這樣,每當監視的文件發生變化時,內核便給 fd 關聯的事件隊列裡面塞一個文件事件。文件事件用一個 inotify_event 結構表示,可以通過 read 來讀取:
struct inotify_event { __s32 wd; /* watch descriptor */ __u32 mask; /* watch mask */ __u32 cookie; /* cookie to synchronize two events */ __u32 len; /* length (including nulls) of name */ char name[0]; /* stub for possible name */ };
size_t len = read (fd, buf, LEN);
通過監視/proc/pid/maps文件的打開事件,可防針對 360 加固的 dump 脫殼
文件變化與事件觸發非必然聯系,例如/proc/pid/status中的TracerPid值在被調試時是變化的,但其變化沒有事件發生,原因未知可能與 inofity 的內核實現有關
ptrace() 是 Linux 的一個系統調用,也是 Linux 下 gdb 等調試器實現的基礎。它提供了 Linux 下一個進程跟蹤另一個進程寄存器、內存等的能力。
由於 ptrace() 到一個線程後,任何信號都將導致線程STOP 並將控制權交由調用者 ,因此如果利用每個線程只能有一個ptrace跟蹤 來防止附加的話,需要處理好這個關系:
while (waitpid (g_childPid, &stat, 0) ) { if (WIFEXITED (stat) || WIFSIGNALED(stat)) { XXX_DEBUG_LOG ("waitpid : child died\n"); exit (11); } ptrace (PTRACE_CONT, g_childPid, NULL, NULL); }
一基礎知識 android的事件處理分為3步。 1)public booleandispatchTouchEvent(MotionEvent ev) 這個方法用來分發
23種GOF設計模式一般分為三大類:創建型模式、結構型模式、行為模式。創建型模式抽象了實例化過程,它們幫助一個系統獨立於如何創建、組合和表示它的那些對象。一個類創建型模式
概述本篇是繼上一篇Android 源碼解析View的touch事件分發機制之後的,關於ViewGroup事件分發機制的學習。同樣的,將采用案例結合源碼的方式來進行分析。前
在Android開發中,我們不可避免的會做到注冊功能,而現在的注冊大多數都是用手機去注冊的,那麼注冊的時候都會要求用獲取驗證碼的方式去驗證,我們接下來就來實戰一下自定義獲