AOSP源碼定制-內(nèi)核驅(qū)動(dòng)編寫(xiě)
介紹
有時(shí)候?yàn)榱朔治鲆恍さ?a target="_blank">檢測(cè),需要在內(nèi)核層面對(duì)讀寫(xiě)相關(guān)的操作進(jìn)行監(jiān)控,每次去修改對(duì)應(yīng)的內(nèi)核源碼編譯重刷過(guò)于耗時(shí)耗力,這里就來(lái)嘗試編寫(xiě)一個(gè)內(nèi)核驅(qū)動(dòng),載入后監(jiān)控讀寫(xiě)。
前提
已經(jīng)同步對(duì)應(yīng)版本的內(nèi)核源碼并編譯,這里不贅述。
真機(jī)測(cè)試并root。
開(kāi)啟配置
比起直接改源碼,編譯模塊載入模塊,不需要反復(fù)修改源碼并刷入內(nèi)核,相比較用frida等框架更不容易檢測(cè)(就是因?yàn)閒rida檢測(cè)才用這個(gè))。
先為編譯配置開(kāi)啟內(nèi)核可加載、卸載等選項(xiàng):
CONFIG_MODULES=Y
CONFIG_STRICT_MEMORY_RWX=N / CONFIG_DEBUG_RODATA=N
CONFIG_DEVMEM=Y
CONFIG_DEVKMEM=Y
CONFIG_KALLSYMS=Y
CONFIG_KALLSYMS_ALL=Y
CONFIG_HAVE_KPROBES=Y
CONFIG_HAVE_KRETPROBES=Y
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=Y
CONFIG_TRACING=Y
CONFIG_FTRACE=Y
在內(nèi)核源碼目錄下執(zhí)行命令(前面編譯過(guò)一次,會(huì)有導(dǎo)入過(guò)系統(tǒng)變量):
make menuconfig
然后出現(xiàn)一個(gè)圖像化的配置頁(yè)面。
通過(guò)"/",打開(kāi)搜索頁(yè)面,查找上面對(duì)應(yīng)的配置所在位置,以CONFIG_DEVKMEM為例,可以看到會(huì)給出定義路徑。
去對(duì)應(yīng)路徑找到這個(gè)目錄,drivers/char/Kconfig。
找到定義的位置,改成y即可。按照配置改好,重新編譯內(nèi)核,后面就可以開(kāi)始編寫(xiě)驅(qū)動(dòng)模塊了
編譯第一個(gè)內(nèi)核驅(qū)動(dòng)
這里我們編譯一個(gè)內(nèi)核模塊有兩種模式,一種是直接編譯進(jìn)內(nèi)核,另一種是編譯成單獨(dú)的ko文件通過(guò)insmod,rmmod命令來(lái)加載與卸載。這里我們講的是單獨(dú)編譯成ko文件。 在內(nèi)核目錄下創(chuàng)建一個(gè)modules目錄,用于存放編寫(xiě)各類(lèi)驅(qū)動(dòng)模塊。 先來(lái)寫(xiě)個(gè)helloworld模塊進(jìn)行測(cè)試。
簡(jiǎn)單加個(gè)代碼測(cè)試,驅(qū)動(dòng)代碼編寫(xiě)有格式規(guī)范:
#include
#include
#include
static int __init hello_init(void){
printk(KERN_ALERT "Hello World! ");
return 0;
}
static void __exit hello_exit(void){
printk(KERN_ALERT "Bye ");
}
module_init(hello_init);
module_exit(hello_exit);
編寫(xiě)Makefile:
# 設(shè)置內(nèi)核源碼編譯的輸出目錄
KERNEL_OUT=/home/fukuyama/sourceCode/msm/out
# 設(shè)置arm64交叉編譯鏈工具路徑
TOOLCHAIN=/home/fukuyama/sourceCode/Android8/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin/aarch64-linux-android-
# 設(shè)置arm32交叉編譯鏈工具路徑
TOOLCHAIN32=/home/fukuyama/sourceCode/Android8/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androideabi-
# 設(shè)置模塊
obj-m := helloworld.o
# 編譯命令配置
all:
make ARCH=arm64 CROSS_COMPILE_ARM32=$(TOOLCHAIN32) CROSS_COMPILE=$(TOOLCHAIN) -C $(KERNEL_OUT) M=$(shell pwd) modules
# 清理編譯命令
clean:
make -C $(KERNEL_OUT) M=$(shell pwd) cleancd
直接make編譯:
編譯完后,adb 推送到 data/local/tmp目錄,然后insmod執(zhí)行模塊查看內(nèi)核日志輸出即可:
可以看到已經(jīng)有輸出了,卸載模塊也有輸出,證明模塊已經(jīng)生效了。
編寫(xiě)監(jiān)控模塊
比如要監(jiān)控open和read,我們需要獲取到syscalltable的基址。
echo 0 > /proc/sys/kernel/kptr_restrict
cat /proc/kallsyms
然后編寫(xiě)代碼,增加了uid大于10000篩選:
#include "linux/kernel.h"
#include "linux/init.h"
#include "linux/module.h"
#include "linux/moduleparam.h"
#include "asm/unistd.h"
#include "linux/slab.h"
#include "linux/sched.h"
#include "linux/uaccess.h"
#include
void ** sys_call_table64 = (void**)0xffffffc001000000;
#define SURPRESS_WARNING __attribute__((unused))
#define LL unsigned long long
// int mm_uid = 10067;
// module_param(mm_uid, int, 0664);
SURPRESS_WARNING int getCurrentPid(void)
{
int pid = get_current()->pid;
return pid;
}
SURPRESS_WARNING LL isUserPid(void)
{
const struct cred * m_cred = current_cred();
kuid_t uid = m_cred->uid;
int m_uid = uid.val;
if(m_uid >10000)
{
return true;
}
return false;
}
SURPRESS_WARNING asmlinkage LL (*old_openat64)(int dirfd, const char __user* pathname, int flags, umode_t modex);
SURPRESS_WARNING LL new_openat64(int dirfd, const char __user* pathname, int flags, umode_t modex)
{
const struct cred * m_cred = current_cred();
kuid_t uid = m_cred->uid;
int m_uid = uid.val;
LL ret = -1;
ret = old_openat64(dirfd, pathname, flags, modex);
if(isUserPid())
{
char bufname[256] = {0};
strncpy_from_user(bufname, pathname, 255);
if(strstr("/sdcard/trace.txt",bufname)){
}else{
printk("myLog::openat64 pathname:[%s] ret:[%llu] current->pid:[%d] current->uid:[%d] ", bufname,ret , getCurrentPid(),m_uid);
}
}
return ret;
}
SURPRESS_WARNING asmlinkage LL (*old_read)(unsigned int fd, char __user *buf, size_t count);
SURPRESS_WARNING LL new_read(unsigned int fd, char __user *buf, size_t count)
{
const struct cred * m_cred = current_cred();
kuid_t uid = m_cred->uid;
int m_uid = uid.val;
LL ret = -1;
ret = old_read(fd, buf, count);
if(isUserPid())
{
char bufname[256] = {0};
strncpy_from_user(bufname, buf, 24);
printk("myLog::read fd:[%d] context:[%s] current->pid:[%d] current->uid:[%d] ", fd,bufname, getCurrentPid(),m_uid);
}
return ret;
}
SURPRESS_WARNING int hook_init(void){
printk("myLog::hook init success ");
if(sys_call_table64){
old_openat64 = (void*)(sys_call_table64[__NR_openat]);
printk("myLog::old_openat64 : %p ", old_openat64);
sys_call_table64[__NR_openat] = (void*)new_openat64;
old_read = (void*)(sys_call_table64[__NR_read]);
printk("myLog::old_read : %p ", old_read);
sys_call_table64[__NR_read] = (void*)new_read;
printk("myLog::hook init end ");
}
else{
printk("mylog::fail to find sys_call_table ");
}
return 0;
}
int __init myInit(void){
printk("myLog::hooksyscall Loaded1 ");
hook_init();
return 0;
}
void __exit myExit(void){
if(sys_call_table64){
printk("myLog::cleanup start ");
sys_call_table64[__NR_openat] = (void*)old_openat64;
sys_call_table64[__NR_read] = (void*)old_read;
printk("myLog::cleanup finish ");
}
printk("myLog::hooksyscall Quited ");
}
module_init(myInit);
module_exit(myExit);
載入后查看效果:
已經(jīng)有監(jiān)控輸出了。 為了對(duì)上面的監(jiān)控更加細(xì)化,修改補(bǔ)充一部分uid的限制:
......
void ** sys_call_table64 = (void**)0xffffffc001000000;
#define SURPRESS_WARNING __attribute__((unused))
#define LL unsigned long long
int mm_uid = 10067;
module_param(mm_uid, int, 0664);
......
SURPRESS_WARNING LL isUserPid(void)
{
const struct cred * m_cred = current_cred();
kuid_t uid = m_cred->uid;
int m_uid = uid.val;
if(m_uid == mm_uid)
{
return true;
}
return false;
}
在啟動(dòng)模塊的時(shí)候,增加啟動(dòng)參數(shù),只打印對(duì)應(yīng)uid的app讀寫(xiě)監(jiān)控。
insmod helloworld.ko mm_uid=10067
演示略。 只是這個(gè)模塊好像卸載的時(shí)候會(huì)死機(jī)重啟。
總結(jié)
簡(jiǎn)單學(xué)習(xí)下內(nèi)核驅(qū)動(dòng)的編寫(xiě)與加載,簡(jiǎn)化內(nèi)核監(jiān)控,還可以補(bǔ)充定制用于繞過(guò)一些反調(diào)試。
-
Modules
+關(guān)注
關(guān)注
0文章
10瀏覽量
7995 -
AOSP
+關(guān)注
關(guān)注
0文章
16瀏覽量
6195 -
內(nèi)核驅(qū)動(dòng)
+關(guān)注
關(guān)注
0文章
5瀏覽量
2651
原文標(biāo)題:AOSP源碼定制-內(nèi)核驅(qū)動(dòng)編寫(xiě)
文章出處:【微信號(hào):哆啦安全,微信公眾號(hào):哆啦安全】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論