为Android应用程序读取/dev下设备而提权(一)

本文深入探讨了Android系统中为应用程序提权以访问特定设备文件的方法。主要介绍了通过修改init.rc脚本或device.c源码实现对/dev目录下设备文件权限调整的技术细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为Android应用程序读取/dev下设备而提权(一)

         倘若应用程序需要对/dev/xxx进行读写操作,就需要提升其权限。提权方法不唯一,需要根据具体需求情况而选择。归根结底,终究都落到chmod 777 /dev/xxx 上,不同的是,chmod操作被执行在何时何地,在此做个分析总结。
        内核启动后会执行/system/init,传说中的系统1号进程,init程序起初的任务是初始化,包括各种mkdir来构建文件系统,得到硬件信息建立设备节点,安装SIGCHLD信号来回收僵尸进程的资源,解析init.rc启动脚本等等,然后init程序变身为property_service来管理系统的权限。我们可以下手的地方有两处: device_init和init.rc



✿ init.rc

   这个方案是大家用的比较多的,在其中添加chmod操作很简单不多说。


✿ device.c

   这个方案用的比较少,先了解下device.c。
   device_init在/system/core/init/device.c中,详细分析如下:

  1. //分别遍历/sys/class /sys/block /sys/devices  
  2. device_init()  
  3. {  
  4.   coldboot(fd,"/sys/class");  
  5.   coldboot(fd,"/sys/block");  
  6.   coldboot(fd,"/sys/devices");  
  7. }  
  8.      
  9. //后面有个递归 /sys下是内核生成的设备,这就相当于udev的作用  
  10. do_coldboot()  
  11. {  
  12.   if(fd>= 0) {  
  13.   write(fd,"add\n", 4);  
  14.   close(fd);  
  15.   handle_device_fd(event_fd);  
  16.   }  
  17. }  
  18.   
  19. //从socket里读出add处理  
  20. handle_device_fd()   
  21. {  
  22.   if(!strcmp(uevent->action,"add")) {  
  23.   make_device(devpath,block, uevent->major, uevent->minor);  
  24.   return;  
  25.   }  
  26. }  
  27.   
  28. //得到设备的相关信息创建设备节点  
  29. make_device()   
  30. {  
  31.   mode= get_device_perm(path,&uid, &gid) | (block ? S_IFBLK : S_IFCHR);  
  32.   dev= (major << 8) | minor;  
  33.   mknod(path,mode, dev);  
  34.   chown(path,uid, gid);  
  35. }  
  36.   
  37. get_device_perm()  
  38. {  
  39.   if(get_device_perm_inner(qemu_perms, path, uid, gid, &perm) == 0) {  
  40.   returnperm;  
  41.   }elseif(get_device_perm_inner(devperms,path, uid, gid, &perm) == 0){  
  42.    returnperm;  
  43.   }else{  
  44.                 …….  
  45. }  
  46.   
  47. //得到devperms结构体的信息  
  48. get_device_perm_inner  
  49. {  
  50.  for(i= 0; perms[i].name; i++) {  
  51.   
  52.  if(perms[i].prefix){  
  53.  if(strncmp(path,perms[i].name, strlen(perms[i].name)))  
  54.  continue;  
  55.  }else{  
  56.  if(strcmp(path,perms[i].name))  
  57.  continue;  
  58.  }  
  59.  *uid= perms[i].uid;  
  60.  *gid= perms[i].gid;  
  61.  *perm= perms[i].perm; //权限位  
  62.  return0;  
  63.  }  
  64. }  
  65.   
  66. 这是devperms的具体内容  
  67. structperms_ {  
  68.    char*name;  
  69.    mode_tperm;  
  70.    unsignedintuid;  
  71.    unsignedintgid;  
  72.    unsignedshortprefix;  
  73. };  
  74. staticstructperms_ devperms[] = {  
  75. {"/dev/null", 0666, AID_ROOT, AID_ROOT, 0 },  
  76. {"/dev/zero", 0666, AID_ROOT, AID_ROOT, 0 },  
  77. {"/dev/full", 0666, AID_ROOT, AID_ROOT, 0 },  
  78. {"/dev/ptmx", 0666, AID_ROOT, AID_ROOT, 0 },  
  79. {"/dev/tty", 0666, AID_ROOT, AID_ROOT, 0 },  
  80. {"/dev/random", 0666, AID_ROOT, AID_ROOT, 0 },  
  81. {"/dev/urandom", 0666, AID_ROOT, AID_ROOT, 0 },  
  82. {"/dev/ashmem", 0666, AID_ROOT, AID_ROOT, 0 },  
  83. {"/dev/binder", 0666, AID_ROOT, AID_ROOT, 0 },  
  84.   
  85. /* logger should be world writable (for logging) but not readable*/  
  86. {"/dev/log/", 0662, AID_ROOT, AID_LOG, 1 },  
  87.   
  88. /*these should not be world writable */  
  89. {"/dev/android_adb", 0660, AID_ADB, AID_ADB, 0 },  
  90. {"/dev/android_adb_enable", 0660, AID_ADB, AID_ADB, 0 },  
  91. {"/dev/ttyMSM0", 0660, AID_BLUETOOTH, AID_BLUETOOTH, 0 },  
  92. {"/dev/alarm", 0664, AID_SYSTEM, AID_RADIO, 0 },  
  93. {"/dev/tty0", 0666, AID_ROOT, AID_SYSTEM, 0 },  
  94. {"/dev/graphics/", 0660, AID_ROOT, AID_GRAPHICS, 1 },  
  95. {"/dev/hw3d", 0660, AID_SYSTEM, AID_GRAPHICS, 0 },  
  96. {"/dev/input/", 0660, AID_ROOT, AID_INPUT, 1 },  
  97. {"/dev/eac", 0660, AID_ROOT, AID_AUDIO, 0 },  
  98. {"/dev/cam", 0660, AID_ROOT, AID_CAMERA, 0 },  
  99. {"/dev/pmem", 0660, AID_SYSTEM, AID_GRAPHICS, 0 },  
  100. {"/dev/pmem_gpu", 0660, AID_SYSTEM, AID_GRAPHICS, 1 },  
  101. {"/dev/pmem_adsp", 0660, AID_SYSTEM, AID_AUDIO, 1 },  
  102. {"/dev/pmem_camera", 0660, AID_SYSTEM, AID_CAMERA, 1 },  
  103. {"/dev/oncrpc/", 0660, AID_ROOT, AID_SYSTEM, 1 },  
  104. {"/dev/adsp/", 0660, AID_SYSTEM, AID_AUDIO, 1 },  
  105. {"/dev/mt9t013", 0660, AID_SYSTEM, AID_SYSTEM, 0 },  
  106. {"/dev/akm8976_daemon",0640, AID_COMPASS, AID_SYSTEM, 0 },  
  107. {"/dev/akm8976_aot", 0640, AID_COMPASS, AID_SYSTEM, 0 },  
  108. {"/dev/akm8976_pffd", 0640, AID_COMPASS, AID_SYSTEM, 0 },  
  109. {"/dev/msm_pcm_out", 0660, AID_SYSTEM, AID_AUDIO, 1 },  
  110. {"/dev/msm_pcm_in", 0660, AID_SYSTEM, AID_AUDIO, 1 },  
  111. {"/dev/msm_pcm_ctl", 0660, AID_SYSTEM, AID_AUDIO, 1 },  
  112. {"/dev/msm_mp3", 0660, AID_SYSTEM, AID_AUDIO, 1 },  
  113. {"/dev/smd0", 0640, AID_RADIO, AID_RADIO, 0 },  
  114. {"/dev/qmi", 0640, AID_RADIO, AID_RADIO, 0 },  
  115. {"/dev/qmi0", 0640, AID_RADIO, AID_RADIO, 0 },  
  116. {"/dev/qmi1", 0640, AID_RADIO, AID_RADIO, 0 },  
  117. {"/dev/qmi2", 0640, AID_RADIO, AID_RADIO, 0 },  
  118. {"/dev/htc-acoustic", 0640, AID_RADIO, AID_RADIO, 0 },  
  119. {NULL, 0, 0, 0, 0 },  
  120. };  

✿ init.c

       init.rc脚本和老版本android中的init.goldfish.rc脚本很早就被parse_config_file()函数解析将脚本内容分为几个段,early-init,init,early-boot,boot,和各个服务。然后在不同的时间点上执行各个段得命令或者开启各种服务。

   
init.c的一段节选:
  
  1. int main(intargc, char**argv)  
  2. {  
  3.   ……  
  4.   mkdir("/dev",0755);   
  5.   mkdir("/proc",0755);  
  6.   mkdir("/sys",0755);  
  7.   mount("tmpfs","/dev""tmpfs", 0, "mode=0755");  
  8.   mkdir("/dev/pts",0755);  
  9.   mkdir("/dev/socket",0755);  
  10.   mount("devpts","/dev/pts""devpts", 0, NULL);  
  11.   mount("proc","/proc""proc", 0, NULL);  
  12.   mount("sysfs","/sys""sysfs", 0, NULL);  
  13.   … …  
  14.   INFO("readingconfig file\n");  
  15.   parse_config_file("/init.rc");   
  16. //调用parse_config解析init.rc脚本  
  17. //经过解析,init.rc的内容就被分为多少个段,被串在action_list链表中。  
  18. //on开头的都是action类型的段,比如init段,init段用一个结构体struct action表示,其中name是init,  
  19. //所有这个段内的命令,都被串在commands链表中。  
  20.   
  21.   action_for_each_trigger("early-init",action_add_queue_tail);   
  22. //遍历action_list链表,查找name是early-init的那个action,将这个节点放在action_queue的尾部。  
  23.   drain_action_queue();  
  24. //将action_queue尾部的节点遍历,然后删除。  
  25. //就相当于遍历name是early-init的action节点内的commands链表。  
  26. //就是在执行init.rc脚本中onearly-init段内的所有命令。  
  27.   
  28. ……  
  29.   INFO("deviceinit\n");  
  30.   device_fd= device_init(); //常见必要的设备节点  
  31.   
  32.   property_init();//init 以后的任务就是proper_service  
  33.   
  34.   action_for_each_trigger("init",action_add_queue_tail); //将init段,加入action_queue  
  35.   drain_action_queue();// 执行init段得命令  
  36.   
  37. … …  
  38. }  

✿ 本节小结

       device_init其实就是linux中的udev的一个简单的替代。把/sys/下的所有内核提供的设备都安排在/dev下创建设备节点。如果要改动/dev/一些设备的权限,可以把chmod 777写在init.rc中,但是要注意写的位置,不能太早执行,不能写在early-init段内,因为那时/dev/下的设备节点还没有被创建。
      在devices.c中修改的方法隐藏的较深不容易被发现,但是如果init.rc内再次修改就可能把之前的修改覆盖掉。


### Android 中打开串口 `/dev/ttyS0` 并获取文件描述符 `fd` 在 Android 系统中,可以通过标准的 POSIX 文件 I/O 函数来访问串口设。以下是实现此功能的具体方法以及相关代码示例。 #### 方法概述 为了在 Android 上成功打开串口 `/dev/ttyS0` 并获得文件描述符 `fd`,需要遵循以下原则: 1. 使用 C/C++ 编程语言中的 `open()` 函数来打开指定的串口设。 2. 设置适当的权限以便应用程序能够读取和写入该设。 3. 配置串口参数(波特率、数据位数等),这通常通过调用 `termios` 结构体及其关联函数完成。 4. 如果目标平台运行的是 SELinux,则可能还需要调整安全策略以允许应用访问特定路径下的设节点。 下面是具体的实现方式及注意事项: --- #### 示例代码 以下是个完整的 C 语言程序片段用于演示如何在 Android NDK 下打开 `/dev/ttyS0` 设并设置基本属性: ```c #include <fcntl.h> /* File Control Definitions */ #include <errno.h> /* Error Number Definitions */ #include <termios.h> /* POSIX Terminal Control Definitions */ #include <unistd.h> /* UNIX Standard Definitions */ int open_serial_port(const char *device_path) { int fd; // 尝试打开串口设 if ((fd = open(device_path, O_RDWR | O_NOCTTY | O_NDELAY)) < 0) { perror("Failed to Open Serial Port"); return -1; } struct termios options; // 获取当前配置项副本 tcgetattr(fd, &options); // 清除旧模式标志 cfmakeraw(&options); // 设置波特率为9600bps (可根据需求修改) cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); // 应用新的选项至端口中 if(tcsetattr(fd,TCSANOW,&options)!=0){ close(fd); perror("Error Setting Attributes on Serial Port"); return -1; } return fd; } // 调用例子 void main(){ const char* device="/dev/ttyS0"; int serial_fd=open_serial_port(device); if(serial_fd>=0){ printf("Serial port %s opened successfully with file descriptor:%d\n",device,serial_fd); // 此处可继续执行其他操作... close(serial_fd); }else{ fprintf(stderr,"Could not open serial port %s.\n",device); } } ``` 以上代码实现了以下几个重要部分的功能: - **打开串口**: 利用了带标志位 `O_RDWR`, `O_NOCTTY`, 和 `O_NDELAY` 的 `open()` 来确保即使没有连接硬件也能正常工作[^3]。 - **初始化串口参数**: 借助于 `struct termios` 类型变量存储所需的各种通信特性,并利用辅助宏如 `cfmakeraw()`, `cfsetispeed()`, 及其对应输出版本设定速率等功能完成了初步定制化过程[^1]。 --- #### 关键点解析 - **权限管理** 在某些情况下,默认安装的应用无法直接访问位于 `/dev/` 目录下的特殊字符设文件。因此,在开发阶段建议授予调试 APK 更高的权限或者手动更改这些资源的所有者与组别关系。例如,可以尝试如下命令赋予适当许可权限给测试账户: ```bash sudo chmod a+rw /dev/ttyS* ``` 同时注意生产环境中应考虑安全性因素而谨慎处理此类变更行为[^2]. - **错误检测机制** 对每个关键步骤都加入了必要的状态检验逻辑以防止单环节失败影响整体流程稳定性。比如当调用 `tcsetattr()` 失败时立即释放已分配资源并通过日志记录具体原因便于后续排查问题所在位置. --- ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值