Android平臺從上到下,無需ROOT/解鎖/刷機,應用級攔截框架的最後一環 —— SVC系統調用攔截。 ☞ Github ☜ 由於我們虛擬化產品的需求,需要支持在普通的Android手機運行。我們需要搭建覆蓋應用從上到下各層的應用級攔截框架,而Abyss作為系統SVC指令的調用攔截,是我們最底層 ...
Android
平臺從上到下,無需ROOT/解鎖/刷機,應用級攔截框架的最後一環 ——SVC
系統調用攔截。
由於我們虛擬化產品的需求,需要支持在普通的Android
手機運行。我們需要搭建覆蓋應用從上到下各層的應用級攔截框架,而Abyss
作為系統SVC
指令的調用攔截,是我們最底層的終極方案。
01. 說明
tracee: 被ptrace
附加的進程,通常為目標應用進程。
tracer: 用來ptrace
其他進程的進程,在該進程里處理系統調用。
本框架利用Android
的Provider
組件啟動攔截處理的服務進程,進程啟動後創建獨立的一個線程迴圈處理所有攔截的系統調用回調。由於本工程只是演示方案的可行性並列印日誌,所以業務邏輯處理比較簡單,可以根據需要的自行擴展。
若要接入具體業務,可能需要改成多線程的方式進行處理,提升穩定性。不過我們實測多線切換也有一定損耗,性能提升有限,但確實穩定性有提升,防止某個處理耗時導致應用所有進程阻塞。
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_ATTACH
、PTRACE_TRACEME
等做各種不同的處理。同時也處理PTRACE_SYSCALL
、PTRACE_CONT
、PTRACE_SETOPTIONS
、PTRACE_GETEVENTMSG
等各種ptrace
操作。
ptrace
有各種各樣的請求,完整的處理邏輯比較複雜(我們還在消化中)。
03.03 PR_wait4、PR_waitpid
配合PR_ptrace
使用,如果當前的tracee
不是一個tracer
,則不處理直接透傳給系統。或者wait
的第一個參數不為-1
,則去集合里找看等待的這個線程是否存在並且是否是當前處理線程的tracee
,如果不是,則不處理直接透傳給系統。
處理的邏輯如下:
系統調用進入前,將系統調用替換為PR_void
,不實際傳給內核。
退出系統調用後,模擬tracer
里wait
的處理邏輯。主要為基於當前處理的這個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