Android 10属性系统原理,检测与定制源码反检测
Android属性是Android系统中重要的设备与运行时信息的来源,也是改机和设备指纹收集绕不开的一个方向,本文就针对我对属性系统的理解,对属性改机的检测与反检测做一个总结.本文代码基于aosp 10.
一.Android属性原理: 属性系统基础: 属性由键值对组成,属性的值有类型,比如string,int,bool等,属性还关联着selinux上下文,因为属性是一种重要的资源,所以由selinux保护,由selinux的策略决定了哪些域可以读取/写哪些属性。
Android属性本质上是进程间通信的一种方式,由init进程创建/dev/__properties__目录,并在此目录下创建和属性相关的文件,然后init进程会调用带着MAP_SHARED标志的mmap()将这些文件映射到vm_area_struct,对属性的初始化和修改会反映到这些文件中去.其他需要读取和修改属性的进程也会调用带着MAP_SHARED标志的mmap()将这些文件映射到自己的vm_area_struct,因此本质上就是共享内存的方式。
在设计上基于安全的考虑,只有init进程可以初始化属性系统,即在/dev/__properties__目录下创建文件,也只有init进程可以修改属性,其他进程想修改属性需要通过/dev/socket/property_service这个socket文件向init进程发起通信请求,而且并不是所有进程都有权限访问这个socket文件,普通的app进程所在的untrusted_app域就是无法访问的,因此普通的app进程无法修改属性。
属性大致分为: ro开头的只读属性,persist开头的持久化属性,ctl开头的控制属性,selinux.restorecon_recursive属性与普通属性。这么区分是因为init进程中对这些属性有不同的处理逻辑,后面会详细描述不同类型属性的差异。
基于效率的考虑,Android属性在属性文件格式的组织上经过了精心的设计,对于频繁的获取某一属性这种场景Android还提供了属性缓存的机制,这个机制也是改机的一个障碍。
进程间通信必然需要某种同步机制,Android中的属性通过包装futex系统调用实现了属性的同步功能。
系统提供了getprop工具可以获取所有属性值,调用的时候加上-Z参数可以获取属性对应的selinux上下文,加上-T参数可以获取属性值对应的类型。
属性核心API: Android属性的实现核心代码放在了bionic libc中,有两个重要的头文件:<sys/system_properties.h>和<sys/_system_properties.h>描述了属性的api,这两个文件都处于路径bionic/libc/include/sys/下,这两个头文件不论是系统代码还是基于ndk开发的代码都是可以包含的,只不过包含<sys/_system_properties.h>文件的时候需要定义"#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_”,不然编译会报错,这是因为<sys/_system_properties.h>文件开头明确了:
#ifndef _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ #error you should #include <sys/system_properties.h> instead #endif 从这里也可以看出系统的开发者不想让你直接包含<sys/_system_properties.h>文件使用里边的接口,而最好使用<sys/system_properties.h>文件中的接口。
<sys/system_properties.h>:
这个文件包含的接口主要是设置属性,遍历属性以及读取属性的值,涵盖了正常使用属性系统的需求,其中有些接口需要prop_info结构体,这个结构体表示了属性值所在的区域.只不过调用接口的人无需关心prop_info结构体里边的细节,只需要调用一个接口获取prop_info指针,再将该指针传递给另外的接口获取更多信息。
以下是接口描述:
int` `__system_property_set(const char``*` `__name, const char``*` `__value); 设置name属性的值为value,它的内部实现是通过/dev/socket/property_service这个socket文件向init进程发起通信请求,因此必须是selinux策略明确同意的进程才有权限发起通信,selinux策略同意设置属性的宏为set_prop,定义在te_macros文件中,这个宏其中之一的作用是允许指定的域connectto init进程域的unix_stream_socket文件.在system_app.te文件中指定了system_app可以设置的一些属性:
set_prop(system_app, bluetooth_audio_hal_prop)
set_prop(system_app, bluetooth_prop)
set_prop(system_app, debug_prop)
set_prop(system_app, system_prop)
表明system_app域中的进程既可以读取u:object_r:bluetooth_audio_hal_prop:s0上下文的属性值,也可以写这些上下文的属性值。
普通的app进程无法调用此函数,会报: type=1400 audit(0.0:901): avc: denied { write } for name="property_service" dev="tmpfs" ino=25226 scontext=u:r:untrusted_app:s0:c107,c256,c512,c768 tcontext=u:object_r:property_socket:s0 tclass=sock_file permissive=0 app=xxx.xxx.xxx
const prop_info* __system_property_find(const char* __name); void __system_property_read_callback(const prop_info* __pi,void (*__callback)(void* __cookie, const char* __name, const char* __value, uint32_t __serial), void* __cookie) 这两个函数是一对,先调用__system_property_find()函数获取对应属性值的区域结构体指针prop_info*,然后调用__system_property_read_callback()以回调的方式接受属性值,属性值输出参数为value,其中cookie参数由调用方任意设置,最终调用callback的时候会将cookie再传递给回调函数。getprop程序获取单个属性值就是通过调用这两个函数实现的。