【Abyss】Android 平臺應用級系統呼叫攔截框架

iofomo發表於2024-09-19

Android平臺從上到下,無需ROOT/解鎖/刷機,應用級攔截框架的最後一環 —— SVC系統呼叫攔截。

☞ Github ☜ 

由於我們虛擬化產品的需求,需要支援在普通的Android手機執行。我們需要搭建覆蓋應用從上到下各層的應用級攔截框架,而Abyss作為系統SVC指令的呼叫攔截,是我們最底層的終極方案。

01. 說明

tracee:ptrace附加的程序,通常為目標應用程序。

tracer: 用來ptrace其他程序的程序,在該程序裡處理系統呼叫。

本框架利用AndroidProvider元件啟動攔截處理的服務程序,程序啟動後建立獨立的一個執行緒迴圈處理所有攔截的系統呼叫回撥。由於本工程只是演示方案的可行性並列印日誌,所以業務邏輯處理比較簡單,可以根據需要的自行擴充套件。

若要接入具體業務,可能需要改成多執行緒的方式進行處理,提升穩定性。不過我們實測多線切換也有一定損耗,效能提升有限,但確實穩定性有提升,防止某個處理耗時導致應用所有程序阻塞。

02. 處理流程

應用程序tracee被附加流程如下:

tracer過程如下:

說明: 使用fork()的目的是為了讓工作執行緒去附加。ptrace有嚴格的限制,只有執行附加attach的執行緒才有許可權操作對應tracee的暫存器。

03. 系統呼叫處理

03.01 忽略庫機制

由於業務的需要,為了提升效能,我們需要忽略某些庫中的系統呼叫,如:libc.so

find_libc_exec_maps()中找到libc.so可執行程式碼在maps中的記憶體地址區間,需要處理的系統呼叫:

//enable_syscall_filtering()    
FilteredSysnum internal_sysnums[] = {
    { PR_ptrace,		FILTER_SYSEXIT },
    { PR_wait4,		FILTER_SYSEXIT },
    { PR_waitpid,		FILTER_SYSEXIT },
    { PR_execve,		FILTER_SYSEXIT },
    { PR_execveat,		FILTER_SYSEXIT },
    {PR_readlinkat,   FILTER_SYSEXIT}, //暫時沒有處理
};

set_seccomp_filters針對不同的arch,設定系統呼叫的ebpf。不同架構的ebpf語句會填充到一起,ebpf的組成虛擬碼如下:

for (每一種架構) {
	start_arch_section;
	for (每一個當前架構下的系統呼叫)
    	add_trace_syscall;
   end_arch_section;
}
finalize_program_filter;

start_arch_section;// 架構相關處理的ebpf,包括libc篩選的語句
add_trace_syscall;// 增加匹配要處理系統呼叫的ebpf語句
end_arch_section;// 尾部的ebpf語句(語句含義:匹配到系統呼叫則返回)
finalize_program_filter;// 最後面的ebpf語句,殺死其他異常情況下的執行緒

最終,呼叫如下語句,設定ebpf

status = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &program);

03.02 PR_ptrace

因為一個tracee只能有一個tracer,所以需要處理該系統呼叫,在應用本身使用了ptrace的時候進行模擬。

系統呼叫進入前,將系統呼叫替換為PR_void,不做真正的ptrace,後續模擬。

退出系統呼叫後,針對ptrace的模擬。針對請求是PTRACE_ATTACHPTRACE_TRACEME等做各種不同的處理。同時也處理PTRACE_SYSCALLPTRACE_CONTPTRACE_SETOPTIONSPTRACE_GETEVENTMSG等各種ptrace操作。

ptrace有各種各樣的請求,完整的處理邏輯比較複雜(我們還在消化中)。

03.03 PR_wait4、PR_waitpid

配合PR_ptrace使用,如果當前的tracee不是一個tracer,則不處理直接透傳給系統。或者wait的第一個引數不為-1,則去集合裡找看等待的這個執行緒是否存在並且是否是當前處理執行緒的tracee,如果不是,則不處理直接透傳給系統。

處理的邏輯如下:

系統呼叫進入前,將系統呼叫替換為PR_void,不實際傳給核心。

退出系統呼叫後,模擬tracerwait的處理邏輯。主要為基於當前處理的這個tracer(程式碼裡定義為ptracer),去遍歷它的tracee,看是否有事件需要被處理,如有,則填充好暫存器,喚醒當前正在被處理的這個tracer

03.04 PR_execve、PR_execveat

主要是在USE_LOADER_EXE開啟時,將native程式替換為使用一個固定的loader來載入程式。

03.05 攔截日誌

E INTERCEPT/SYS: vpid 2: got event 7057f
E INTERCEPT: vpid 2,secomp_enabled 0,
E INTERCEPT/SYS: (null) info: vpid 2: sysenter start: openat(0xffffff9c, 0xb4000073c72fcd60, 0x0, 0x0, 0xb4000073c72fcd88, 0xb4000073c72fcde8) = 0xffffff9c [0x7367d45e80, 0]
E INTERCEPT/SYS: vpid 2: open path:/system/fonts/NotoSansMalayalamUI-VF.ttf
E INTERCEPT/SYS: syscall_number:216
E INTERCEPT/SYS: vpid 2,openat: /system/fonts/NotoSansMalayalamUI-VF.ttf
E INTERCEPT/SYS: (null) info: vpid 2: sysenter end: openat(0xffffff9c, 0xb4000073c72fcd60, 0x0, 0x0, 0xb4000073c72fcd88, 0xb4000073c72fcde8) = 0xffffff9c [0x7367d45e80, 0]
E INTERCEPT/SYS: vpid 2: open path:/system/fonts/NotoSansMalayalamUI-VF.ttf
E INTERCEPT/SYS: (null) info: vpid 2: restarted using 7, signal 0, tracee pid 32222,app_pid 32162

E/INTERCEPT/SYS: (null) info: vpid 3: sysenter start: close(0x90, 0x0, 0x7492d0d088, 0x6, 0x73b7b82860, 0x73b7b82880) = 0x90 [0x73633faae0, 0]
E/INTERCEPT/SYS: syscall_number:41
E/INTERCEPT/SYSW: noting to do,sn:41
E/INTERCEPT/SYS: (null) info: vpid 3: sysenter end: close(0x90, 0x0, 0x7492d0d088, 0x6, 0x73b7b82860, 0x73b7b82880) = 0x90 [0x73633faae0, 0]
E/INTERCEPT/SYS: (null) info: vpid 3: restarted using 7, signal 0, tracee pid 32223,app_pid 32162
E/INTERCEPT/SYS: vpid 3: got event 7057f

04. 附

額外模組:

由於本框架會在原應用中增加一個處理程序,並且會trace到應用程序中,因此在實際使用時,還需要對新增程序和trace痕跡進行隱藏,防止與應用檢測模組衝突,支援完整的應用自身trace呼叫的模擬。

這是附加的應用對抗模組,後面會作為單獨文章分享給大家。

參考專案:

https://github.com/proot-me/proot

https://github.com/termux/proot

相關文章