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

admin 2024年4月23日07:16:53评论7 views字数 4892阅读16分18秒阅读模式

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

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

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

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

编译第一个内核驱动

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

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

  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 "Byen");
  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编译: AOSP源码定制-内核驱动编写编译完后,adb 推送到 data/local/tmp目录,然后insmod执行模块查看内核日志输出即可:
AOSP源码定制-内核驱动编写可以看到已经有输出了,卸载模块也有输出,证明模块已经生效了。

编写监控模块

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

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

AOSP源码定制-内核驱动编写然后编写代码,增加了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>
  10. void ** sys_call_table64 = (void**)0xffffffc001000000;
  11. #define SURPRESS_WARNING __attribute__((unused))
  12. #define LL unsigned long long
  13. // int mm_uid = 10067;
  14. // module_param(mm_uid, int, 0664);
  15. SURPRESS_WARNING int getCurrentPid(void)
  16. {
  17. int pid = get_current()->pid;
  18. return pid;
  19. }
  20. SURPRESS_WARNING LL isUserPid(void)
  21. {
  22. const struct cred * m_cred = current_cred();
  23. kuid_t uid = m_cred->uid;
  24. int m_uid = uid.val;
  25. if(m_uid >10000)
  26. {
  27. return true;
  28. }
  29. return false;
  30. }
  31. SURPRESS_WARNING asmlinkage LL (*old_openat64)(int dirfd, const char __user* pathname, int flags, umode_t modex);
  32. SURPRESS_WARNING LL new_openat64(int dirfd, const char __user* pathname, int flags, umode_t modex)
  33. {
  34. const struct cred * m_cred = current_cred();
  35. kuid_t uid = m_cred->uid;
  36. int m_uid = uid.val;
  37. LL ret = -1;
  38. ret = old_openat64(dirfd, pathname, flags, modex);
  39. if(isUserPid())
  40. {
  41. char bufname[256] = {0};
  42. strncpy_from_user(bufname, pathname, 255);
  43. if(strstr("/sdcard/trace.txt",bufname)){
  44. }else{
  45. printk("myLog::openat64 pathname:[%s] ret:[%llu] current->pid:[%d] current->uid:[%d]n", bufname,ret , getCurrentPid(),m_uid);
  46. }
  47. }
  48. return ret;
  49. }
  50. SURPRESS_WARNING asmlinkage LL (*old_read)(unsigned int fd, char __user *buf, size_t count);
  51. SURPRESS_WARNING LL new_read(unsigned int fd, char __user *buf, size_t count)
  52. {
  53. const struct cred * m_cred = current_cred();
  54. kuid_t uid = m_cred->uid;
  55. int m_uid = uid.val;
  56. LL ret = -1;
  57. ret = old_read(fd, buf, count);
  58. if(isUserPid())
  59. {
  60. char bufname[256] = {0};
  61. strncpy_from_user(bufname, buf, 24);
  62. printk("myLog::read fd:[%d] context:[%s] current->pid:[%d] current->uid:[%d]n", fd,bufname, getCurrentPid(),m_uid);
  63. }
  64. return ret;
  65. }
  66. SURPRESS_WARNING int hook_init(void){
  67. printk("myLog::hook init successn");
  68. if(sys_call_table64){
  69. old_openat64 = (void*)(sys_call_table64[__NR_openat]);
  70. printk("myLog::old_openat64 : %pn", old_openat64);
  71. sys_call_table64[__NR_openat] = (void*)new_openat64;
  72. old_read = (void*)(sys_call_table64[__NR_read]);
  73. printk("myLog::old_read : %pn", old_read);
  74. sys_call_table64[__NR_read] = (void*)new_read;
  75. printk("myLog::hook init endn");
  76. }
  77. else{
  78. printk("mylog::fail to find sys_call_tablen");
  79. }
  80. return 0;
  81. }
  82. int __init myInit(void){
  83. printk("myLog::hooksyscall Loaded1n");
  84. hook_init();
  85. return 0;
  86. }
  87. void __exit myExit(void){
  88. if(sys_call_table64){
  89. printk("myLog::cleanup startn");
  90. sys_call_table64[__NR_openat] = (void*)old_openat64;
  91. sys_call_table64[__NR_read] = (void*)old_read;
  92. printk("myLog::cleanup finishn");
  93. }
  94. printk("myLog::hooksyscall Quitedn");
  95. }
  96. module_init(myInit);
  97. module_exit(myExit);

载入后查看效果:AOSP源码定制-内核驱动编写已经有监控输出了。
为了对上面的监控更加细化,修改补充一部分uid的限制:

  1. ......
  2. void ** sys_call_table64 = (void**)0xffffffc001000000;
  3. #define SURPRESS_WARNING __attribute__((unused))
  4. #define LL unsigned long long
  5. int mm_uid = 10067;
  6. module_param(mm_uid, int, 0664);
  7. ......
  8. SURPRESS_WARNING LL isUserPid(void)
  9. {
  10. const struct cred * m_cred = current_cred();
  11. kuid_t uid = m_cred->uid;
  12. int m_uid = uid.val;
  13. if(m_uid == mm_uid)
  14. {
  15. return true;
  16. }
  17. return false;
  18. }

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

  1. insmod helloworld.ko mm_uid=10067

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

总结

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

参考

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

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年4月23日07:16:53
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   AOSP源码定制-内核驱动编写https://cn-sec.com/archives/2680408.html

发表评论

匿名网友 填写信息