AOSP源码定制-内核驱动编写

admin 2026-04-16 04:22:36 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文介绍AOSP内核驱动编写方法,通过编译可加载模块监控系统调用(如openat/read),替代直接修改内核源码。关键步骤包括配置内核选项、编写helloworld测试模块、实现带UID过滤的监控逻辑,并指出模块卸载可能存在系统稳定性问题。文章提供了完整的代码示例和编译指南,适用于安全分析场景。 综合评分: 78 文章分类: 移动安全,内核驱动,渗透测试,二进制安全,逆向分析


cover_image

AOSP源码定制-内核驱动编写

哆啦安全

2024年4月23日 08:55 四川

在小说阅读器读本章

去阅读

以下文章来源于gakki的童养夫 ,作者新垣结衣唯一丈夫

gakki的童养夫 .

emmmmm gakki是我的

AOSP源码定制-内核驱动编写

介绍

有时候为了分析一些壳的检测,需要在内核层面对读写相关的操作进行监控,每次去修改对应的内核源码编译重刷过于耗时耗力,这里就来尝试编写一个内核驱动,载入后监控读写。

前提

  1. 已经同步对应版本的内核源码并编译,这里不赘述。
  2. 真机测试并root。

开启配置

比起直接改源码,编译模块载入模块,不需要反复修改源码并刷入内核,相比较用frida等框架更不容易检测(就是因为frida检测才用这个)。 先为编译配置开启内核可加载、卸载等选项:

1. CONFIG_MODULES=Y
2. CONFIG_STRICT_MEMORY_RWX=N / CONFIG_DEBUG_RODATA=N
3. CONFIG_DEVMEM=Y
4. CONFIG_DEVKMEM=Y
5. CONFIG_KALLSYMS=Y
6. CONFIG_KALLSYMS_ALL=Y
7. CONFIG_HAVE_KPROBES=Y
8. CONFIG_HAVE_KRETPROBES=Y
9. CONFIG_HAVE_FUNCTION_TRACER=Y
10. CONFIG_HAVE_FUNCTION_GRAPH_TRACER=Y
11. CONFIG_TRACING=Y
12. CONFIG_FTRACE=Y

在内核源码目录下执行命令(前面编译过一次,会有导入过系统变量):

1. make menuconfig

然后出现一个图像化的配置页面。

通过”/”,打开搜索页面,查找上面对应的配置所在位置,以CONFIG_DEVKMEM为例,可以看到会给出定义路径。 去对应路径找到这个目录,drivers/char/Kconfig。 找到定义的位置,改成y即可。按照配置改好,重新编译内核,后面就可以开始编写驱动模块了

编译第一个内核驱动

这里我们编译一个内核模块有两种模式,一种是直接编译进内核,另一种是编译成单独的ko文件通过insmod,rmmod命令来加载与卸载。这里我们讲的是单独编译成ko文件。 在内核目录下创建一个modules目录,用于存放编写各类驱动模块。 先来写个helloworld模块进行测试。

简单加个代码测试,驱动代码编写有格式规范:

1. #include <linux/init.h>
2. #include <linux/module.h>
3. #include <linux/sched.h>
4. static int __init hello_init(void){
5. printk(KERN_ALERT "Hello World!\n");
6. return 0;
7. }
8. static void __exit hello_exit(void){
9. printk(KERN_ALERT "Bye\n");
10. }
11. module_init(hello_init);
12. module_exit(hello_exit);

编写Makefile:

1. # 设置内核源码编译的输出目录
2. KERNEL_OUT=/home/fukuyama/sourceCode/msm/out
3. # 设置arm64交叉编译链工具路径
4. TOOLCHAIN=/home/fukuyama/sourceCode/Android8/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin/aarch64-linux-android-
5. # 设置arm32交叉编译链工具路径
6. TOOLCHAIN32=/home/fukuyama/sourceCode/Android8/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androideabi-
7. # 设置模块
8. obj-m := helloworld.o
9. # 编译命令配置
10. all:
11. make ARCH=arm64 CROSS_COMPILE_ARM32=$(TOOLCHAIN32)  CROSS_COMPILE=$(TOOLCHAIN) -C $(KERNEL_OUT) M=$(shell pwd)  modules
12. # 清理编译命令
13. clean:
14. make -C $(KERNEL_OUT) M=$(shell pwd) cleancd

直接make编译: 编译完后,adb 推送到 data/local/tmp目录,然后insmod执行模块查看内核日志输出即可: 可以看到已经有输出了,卸载模块也有输出,证明模块已经生效了。

编写监控模块

比如要监控open和read,我们需要获取到syscalltable的基址。

1. echo 0 > /proc/sys/kernel/kptr_restrict
2. cat /proc/kallsyms

然后编写代码,增加了uid大于10000筛选:

1. #include "linux/kernel.h"
2. #include "linux/init.h"
3. #include "linux/module.h"
4. #include "linux/moduleparam.h"
5. #include "asm/unistd.h"
6. #include "linux/slab.h"
7. #include "linux/sched.h"
8. #include "linux/uaccess.h"
9. #include <linux/syscalls.h>

12. void ** sys_call_table64 = (void**)0xffffffc001000000;

14. #define SURPRESS_WARNING __attribute__((unused))
15. #define LL unsigned long long

17. // int mm_uid = 10067;
18. // module_param(mm_uid, int, 0664);

21. SURPRESS_WARNING int getCurrentPid(void)
22. {
23. int pid = get_current()->pid;
24. return pid;
25. }

27. SURPRESS_WARNING LL isUserPid(void)
28. {
29. const struct cred * m_cred = current_cred();
30. kuid_t uid = m_cred->uid;
31. int m_uid = uid.val;
32. if(m_uid >10000)
33. {
34. return true;
35. }
36. return false;
37. }

39. SURPRESS_WARNING asmlinkage LL (*old_openat64)(int dirfd, const char __user* pathname, int flags, umode_t modex);
40. SURPRESS_WARNING LL new_openat64(int dirfd, const char __user* pathname, int flags, umode_t modex)
41. {
42. const struct cred * m_cred = current_cred();
43. kuid_t uid = m_cred->uid;
44. int m_uid = uid.val;
45. LL ret = -1;
46. ret = old_openat64(dirfd, pathname, flags, modex);
47. if(isUserPid())
48. {
49. char bufname[256] = {0};
50. strncpy_from_user(bufname, pathname, 255);
51. if(strstr("/sdcard/trace.txt",bufname)){

53. }else{
54. printk("myLog::openat64 pathname:[%s] ret:[%llu] current->pid:[%d] current->uid:[%d]\n", bufname,ret , getCurrentPid(),m_uid);
55. }

58. }

60. return ret;
61. }

64. SURPRESS_WARNING asmlinkage LL (*old_read)(unsigned int fd, char __user *buf, size_t count);
65. SURPRESS_WARNING LL new_read(unsigned int fd, char __user *buf, size_t count)
66. {
67. const struct cred * m_cred = current_cred();
68. kuid_t uid = m_cred->uid;
69. int m_uid = uid.val;
70. LL ret = -1;

72. ret = old_read(fd, buf, count);
73. if(isUserPid())
74. {
75. char bufname[256] = {0};
76. strncpy_from_user(bufname, buf, 24);
77. printk("myLog::read fd:[%d] context:[%s] current->pid:[%d] current->uid:[%d]\n", fd,bufname, getCurrentPid(),m_uid);

80. }
81. return ret;
82. }

86. SURPRESS_WARNING int hook_init(void){
87. printk("myLog::hook init success\n");
88. if(sys_call_table64){
89. old_openat64 = (void*)(sys_call_table64[__NR_openat]);
90. printk("myLog::old_openat64 : %p\n", old_openat64);
91. sys_call_table64[__NR_openat] = (void*)new_openat64;

94. old_read = (void*)(sys_call_table64[__NR_read]);
95. printk("myLog::old_read : %p\n", old_read);
96. sys_call_table64[__NR_read] = (void*)new_read;

98. printk("myLog::hook init end\n");
99. }
100. else{
101. printk("mylog::fail to find sys_call_table\n");
102. }
103. return 0;
104. }

107. int __init myInit(void){
108. printk("myLog::hooksyscall Loaded1\n");
109. hook_init();
110. return 0;
111. }

113. void __exit myExit(void){
114. if(sys_call_table64){
115. printk("myLog::cleanup start\n");
116. sys_call_table64[__NR_openat] = (void*)old_openat64;
117. sys_call_table64[__NR_read] = (void*)old_read;
118. printk("myLog::cleanup finish\n");
119. }
120. printk("myLog::hooksyscall Quited\n");
121. }
122. module_init(myInit);
123. module_exit(myExit);

载入后查看效果:已经有监控输出了。 为了对上面的监控更加细化,修改补充一部分uid的限制:

1. ......

3. void ** sys_call_table64 = (void**)0xffffffc001000000;

5. #define SURPRESS_WARNING __attribute__((unused))
6. #define LL unsigned long long

8. int mm_uid = 10067;
9. module_param(mm_uid, int, 0664);

11. ......

13. SURPRESS_WARNING LL isUserPid(void)
14. {
15. const struct cred * m_cred = current_cred();
16. kuid_t uid = m_cred->uid;
17. int m_uid = uid.val;
18. if(m_uid == mm_uid)
19. {
20. return true;
21. }
22. return false;
23. }

在启动模块的时候,增加启动参数,只打印对应uid的app读写监控。

1. insmod helloworld.ko  mm_uid=10067

演示略。 只是这个模块好像卸载的时候会死机重启。

总结

简单学习下内核驱动的编写与加载,简化内核监控,还可以补充定制用于绕过一些反调试。

参考

https://mp.weixin.qq.com/s/m53WOquwa9BcdlfaB_Lyxw


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:哆啦安全 《AOSP源码定制-内核驱动编写》

评论:0   参与:  0