Android P SELinux (三) 权限检查原理与调试
Android P SELinux (一) 基础概念
Android P SELinux (二) 开机初始化与策略文件编译过程
Android P SELinux (三) 权限检查原理与调试
Android P SELinux (四) CTS neverallow处理总结
目录
- 引子
- 一、权限检测原理
- 1、拥有的权限
- 2、需要的权限
- 3、裁决
- 4、其他
- 二、avc denied信息分析
- 三、调试
- 1、快速编译和替换
- 1.1 编译和替换
- 1.2 load_policy
- 2、尽量使用宏
- 3、使用属性
- 4、setools工具
- 四、参考文章
引子
我们在处理SELinux权限问题的时候,avc denied信息是最关键的,那么avc denied 的打印信息要怎么看、里面的内容每一个字段是什么意思?kernel log的avc denied信息是哪里打印出来的?
以前有个想法,我们系统的操作、命令都是有限的,那其实脚本写完之后需要什么权限都是确定的,那可不可能更快的加好te规则呢?
带着上面的一些疑问,接着在代码想找到是怎么检查权限的,下文会简单介绍一些目前了解到的知识:
假设有如下脚本以及te规则
- /vendor/bin/testA.sh
#!/vendor/bin/shecho "hello world" > /data/vendor/test.txt
- /android/device/xxxx/common/sepolicy/file_contexts
/vendor/bin/testA.sh u:object_r:testA_exec:s0
- /android/device/xxxx/common/sepolicy/testA.te
type testA, domain;
type testA_exec, exec_type, vendor_file_type, file_type;init_daemon_domain(testA)allow testA console_device:chr_file { write open getattr ioctl };
allow testA vendor_toolbox_exec:file { execute_no_trans };
allow testA vendor_data_file:dir { add_name };
执行脚本后,会有如下报错信息:
[ 5424.996583@0]- type=1400 audit(1577887433.668:59): avc: denied { write } for pid=4021 comm="testA.sh"
name="vendor" dev="mmcblk0p20" ino=7395 scontext=u:r:testA:s0 tcontext=u:object_r:vendor_data_file:s0 tclass=dir permissive=0
一、权限检测原理
这部分的代码集中在两个部分
- kernel的代码,里面的security/selinux
- /android/external/selinux
一开始,/data/vendor/test.txt是不存在的;testA.sh脚本运行起来之后,它的安全上下文是u:r:testA:s0,type为testA的进程,要拥有对type为vendor_data_file的目录(dir)的写(write)权限,才能创建一个不存在的文件
权限检测其实就是针对进程访问对象的两个权限:拥有的权限和需要的权限,将两者进行计算得出结果,即允许还是拒绝
1、拥有的权限
te规则定义的权限保存在哪里?
testA.te里面它拥有add_name权限,前面的文章有提到te策略文件的编译过程,这个规则最终会生成在二进制策略文件precompiled_sepolicy里面,而policydb这个数据结构是用来组织起所有数据的
其中te规则保存在这个成员里面
/* type enforcement access vectors and transitions */
avtab_t te_avtab;
这里面的数据结构比较复杂,里面主要需要理解几个数据结构:
- 位图ebitmap
- 链表
- 哈希表hashtab
- 符号表symtab
这里面的内容太多了,我只是简单分析了其中te规则一部分代码,有兴趣可以结合参考文章《Linux多安全策略和动态安全策 略框架模块代码分析报告》(见参考文章)来阅读源码深入了解
下面分析的一些结论,如有问题请指出:
testA这个type的进程,对type为vendor_data_file的dir(目录)所拥有的权限是用一个整数u32来表示的:
它的值是:
0x00120010
下面简单看看这个值是怎么生成的?
(allow testA_28_0 vendor_data_file_28_0 (dir (add_name)))
接着看看 /android/external/selinux/libsepol/cil/src/cil_binary.c 里面的__cil_perms_to_datum函数
int __cil_perms_to_datum(struct cil_list *perms, class_datum_t *sepol_class, uint32_t *datum)
{int rc = SEPOL_ERR;char *key = NULL;struct cil_list_item *curr_perm;struct cil_perm *cil_perm;uint32_t data = 0;cil_list_for_each(curr_perm, perms) {cil_perm = curr_perm->data;key = cil_perm->datum.fqn;rc = __perm_str_to_datum(key, sepol_class, &data);if (rc != SEPOL_OK) {goto exit;}}*datum = data;return SEPOL_OK;exit:return rc;
}int __perm_str_to_datum(char *perm_str, class_datum_t *sepol_class, uint32_t *datum)
{int rc;perm_datum_t *sepol_perm;common_datum_t *sepol_common;sepol_perm = hashtab_search(sepol_class->permissions.table, perm_str);if (sepol_perm == NULL) {sepol_common = sepol_class->comdatum;sepol_perm = hashtab_search(sepol_common->permissions.table, perm_str);if (sepol_perm == NULL) {cil_log(CIL_ERR, "Failed to find datum for perm %s\n", perm_str);rc = SEPOL_ERR;goto exit;}}*datum |= 1 << (sepol_perm->s.value - 1);return SEPOL_OK;exit:return rc;
}
datum 里面保存的值就是0x00120010
看最关键的一句话:
*datum |= 1 << (sepol_perm->s.value - 1);
上面的操作其实就是将对应的位置1,代表它拥有的权限,那每一个位对应的权限是什么呢?
这个要看:
/android/system/sepolicy/private/access_vectors
common file
{ioctlreadwritecreategetattrsetattrlockrelabelfromrelabeltoappendmapunlinklinkrenameexecutequotaonmounton
}class dir
inherits file
{add_nameremove_namereparentsearchrmdiropenaudit_accessexecmod
}
sepol_perm->s.value可以理解成是解析过程中某个权限在access_vectors定义的class里面的index值,它会用符号表和对应的字符串关联起来,比如add_name在集合里面的是第18个,对应的值是0x00020000
我们这里的是dir,将其合并下:
{ioctl,read,write,create,getattr,setattr,lock,relabelfrom,relabelto,append,map,unlink,link,rename,execute,quotaon,mounton,add_name,remove_name,reparent,search,rmdir,open,audit_access,execmod
}
上面那句话的操作就是将datum的第18位置成1
那么最后计算出来的0x00120010就是:
翻译过来,是这个规则
allow testA vendor_data_file:dir { search add_name getattr };
奇怪的是,这里怎么会多出来两个权限呢,我都没有加上去
原因是来自这条规则:
这个在domain.te里面
allow domain vendor_data_file:dir { getattr search };
2、需要的权限
要分析操作vendor_data_file:dir所需要的权限,借助打印堆栈信息来分析:
[ 5424.899991@1]d CPU: 1 PID: 4119 Comm: testA.sh Tainted: P O 4.9.113 #14
[ 5424.907561@1]d Hardware name: Generic DT based system
[ 5424.912595@1]d [bc3c7a84+ 16][<c020dc10>] show_stack+0x20/0x24
[ 5424.918447@1]d [bc3c7aa4+ 32][<c05443ec>] dump_stack+0x90/0xac
[ 5424.924311@1]d [bc3c7aec+ 72][<c04d5954>] slow_avc_audit+0x88/0xa8
[ 5424.930515@1]d [bc3c7b34+ 72][<c04da498>] audit_inode_permission+0x80/0x88
[ 5424.937417@1]d [bc3c7bac+ 120][<c04daf20>] selinux_inode_permission+0x2d0/0x314
[ 5424.944664@1]d [bc3c7bcc+ 32][<c04d1f44>] security_inode_permission+0x4c/0x68
[ 5424.951824@1]d [bc3c7bec+ 32][<c03a5bc8>] __inode_permission2+0x50/0xf0
[ 5424.958455@1]d [bc3c7bfc+ 16][<c03a5cb0>] inode_permission2+0x20/0x54
[ 5424.964930@1]d [bc3c7c84+ 136][<c03a9048>] path_openat+0xb30/0x118c
[ 5424.971137@1]d [bc3c7d2c+ 168][<c03aabe4>] do_filp_open+0x7c/0xe0
[ 5424.977179@1]d [bc3c7d7c+ 80][<c0397b34>] do_sys_open+0x124/0x238
[ 5424.983303@1]d [bc3c7d8c+ 16][<c0397c90>] SyS_openat+0x1c/0x20
[ 5424.989165@1]d [00000000+ 0][<c02085c0>] ret_fast_syscall+0x0/0x48用addr2line看看对应的代码调用逻辑
1 show_stack c020dc10
show_stack
kernelcode/arch/arm/kernel/traps.c:2632 dump_stack c05443ec
dump_stack
kernelcode/lib/dump_stack.c:533 slow_avc_audit c04d5954
slow_avc_audit
kernelcode/security/selinux/avc.c:7754 audit_inode_permission c04da498
audit_inode_permission
kernelcode/security/selinux/hooks.c:30055 selinux_inode_permission c04daf20
selinux_inode_permission
kernelcode/security/selinux/hooks.c:30586 security_inode_permission c04d1f44
security_inode_permission
kernelcode/security/security.c:611 (discriminator 5)7 __inode_permission2 c03a5bc8
__inode_permission2
kernelcode/fs/namei.c:4368 inode_permission2 c03a5cb0
inode_permission2
kernelcode/fs/namei.c:4869 path_openat c03a9048
may_o_create
kernelcode/fs/namei.c:301810 do_filp_open c03aabe4
do_filp_open
kernelcode/fs/namei.c:356911 do_sys_open c0397b34
do_sys_open
kernelcode/fs/open.c:107312 SyS_openat c0397c90
SyS_openat
kernelcode/fs/open.c:109313 ret_fast_syscall c02085c0
ret_fast_syscall
kernelcode/arch/arm/kernel/entry-common.S:37
堆栈打印添加在kernel代码里面:
kernelcode$ git diff security/selinux/avc.c
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index e60c79d..79cea20 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -771,7 +771,7 @@ noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,sad.result = result;a->selinux_audit_data = &sad;
-
+ dump_stack();common_lsm_audit(a, avc_audit_pre_callback, avc_audit_post_callback);return 0;}
跟着堆栈简单看一遍代码,不难发现,需要的权限是从这里来的
may_o_create
kernelcode/fs/namei.c:3018
static int may_o_create(const struct path *dir, struct dentry *dentry, umode_t mode)
{struct user_namespace *s_user_ns;int error = security_path_mknod(dir, dentry, mode, 0);if (error)return error;s_user_ns = dir->dentry->d_sb->s_user_ns;if (!kuid_has_mapping(s_user_ns, current_fsuid()) ||!kgid_has_mapping(s_user_ns, current_fsgid()))return -EOVERFLOW;error = inode_permission2(dir->mnt, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);if (error)return error;return security_inode_create(dir->dentry->d_inode, dentry, mode);
}
inode_permission2的第三个参数 MAY_WRITE | MAY_EXEC
会一直传递到 kernelcode/security/selinux/hooks.c
selinux_inode_permission这个函数的第二参数mask
static int selinux_inode_permission(struct inode *inode, int mask)
{const struct cred *cred = current_cred();u32 perms;bool from_access;unsigned flags = mask & MAY_NOT_BLOCK;struct inode_security_struct *isec;u32 sid;struct av_decision avd;int rc, rc2;u32 audited, denied;from_access = mask & MAY_ACCESS;//mask = MAY_WRITE | MAY_EXEC;mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND);/* No permission to check. Existence test. */if (!mask)return 0;validate_creds(cred);if (unlikely(IS_PRIVATE(inode)))return 0;//mask = MAY_WRITE | MAY_EXEC;//perms = DIR__WRITE | DIR__SEARCH//perms = 0x00400004perms = file_mask_to_av(inode->i_mode, mask);sid = cred_sid(cred);isec = inode_security_rcu(inode, flags & MAY_NOT_BLOCK);if (IS_ERR(isec))return PTR_ERR(isec);//主要是获取av_decision信息,里面包含了权限信息,先从avc cache里面获取,如果没有就从policydb里面计算得出rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd);//这个就是审计、裁决的动作了,计算拥有的权限avd和需要的权限perms,得出缺失的权限audited = avc_audit_required(perms, &avd, rc,from_access ? FILE__AUDIT_ACCESS : 0,&denied);//大部分情况下,如果没有权限问题,到这儿就结束了if (likely(!audited))return rc;//再往下就要开始收集信息,打印avc denied信息了rc2 = audit_inode_permission(inode, perms, audited, denied, rc, flags);if (rc2)return rc2;return rc;
}
这里面file_mask_to_av计算出来的perms就是我们现在操作dir所需要的权限
DIR__WRITE 、 DIR__SEARCH的定义是av_permissions.h头文件里面
不过不是这个 /android/external/selinux/libselinux/include/selinux/av_permissions.h
而是编译生成,路径在:/android/out/target/product/xxxxx/obj/KERNEL_OBJ/security/selinux/av_permissions.h
#define DIR__WRITE 0x00000004UL
#define DIR__SEARCH 0x00400000UL
也就是perms = 0x00400004
实际上,这里面这个权限也是每一位代表了一个权限,和上面的不同的是,这个是根据classmap.h里面定义的值来计算的
kernelcode/security/selinux/include/classmap.h
#define COMMON_FILE_SOCK_PERMS "ioctl", "read", "write", "create", \"getattr", "setattr", "lock", "relabelfrom", "relabelto", "append"#define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \"rename", "execute", "quotaon", "mounton", "audit_access", \"open", "execmod"#define COMMON_SOCK_PERMS COMMON_FILE_SOCK_PERMS, "bind", "connect", \"listen", "accept", "getopt", "setopt", "shutdown", "recvfrom", \"sendto", "name_bind"#define COMMON_IPC_PERMS "create", "destroy", "getattr", "setattr", "read", \"write", "associate", "unix_read", "unix_write"#define COMMON_CAP_PERMS "chown", "dac_override", "dac_read_search", \"fowner", "fsetid", "kill", "setgid", "setuid", "setpcap", \"linux_immutable", "net_bind_service", "net_broadcast", \"net_admin", "net_raw", "ipc_lock", "ipc_owner", "sys_module", \"sys_rawio", "sys_chroot", "sys_ptrace", "sys_pacct", "sys_admin", \"sys_boot", "sys_nice", "sys_resource", "sys_time", \"sys_tty_config", "mknod", "lease", "audit_write", \"audit_control", "setfcap"#define COMMON_CAP2_PERMS "mac_override", "mac_admin", "syslog", \"wake_alarm", "block_suspend", "audit_read"/** Note: The name for any socket class should be suffixed by "socket",* and doesn't contain more than one substr of "socket".*/
struct security_class_mapping secclass_map[] = {{ "security",{ "compute_av", "compute_create", "compute_member","check_context", "load_policy", "compute_relabel","compute_user", "setenforce", "setbool", "setsecparam","setcheckreqprot", "read_policy", "validate_trans", NULL } },{ "process",{ "fork", "transition", "sigchld", "sigkill","sigstop", "signull", "signal", "ptrace", "getsched", "setsched","getsession", "getpgid", "setpgid", "getcap", "setcap", "share","getattr", "setexec", "setfscreate", "noatsecure", "siginh","setrlimit", "rlimitinh", "dyntransition", "setcurrent","execmem", "execstack", "execheap", "setkeycreate","setsockcreate", NULL } },{ "system",{ "ipc_info", "syslog_read", "syslog_mod","syslog_console", "module_request", "module_load", NULL } },{ "capability",{ COMMON_CAP_PERMS, NULL } },{ "filesystem",{ "mount", "remount", "unmount", "getattr","relabelfrom", "relabelto", "associate", "quotamod","quotaget", NULL } },{ "file",{ COMMON_FILE_PERMS,"execute_no_trans", "entrypoint", NULL } },{ "dir",{ COMMON_FILE_PERMS, "add_name", "remove_name","reparent", "search", "rmdir", NULL } },{ "fd", { "use", NULL } },{ "lnk_file",{ COMMON_FILE_PERMS, NULL } },{ "chr_file",{ COMMON_FILE_PERMS, NULL } },{ "blk_file",{ COMMON_FILE_PERMS, NULL } },{ "sock_file",{ COMMON_FILE_PERMS, NULL } },{ "fifo_file",{ COMMON_FILE_PERMS, NULL } },{ "socket",{ COMMON_SOCK_PERMS, NULL } },{ "tcp_socket",{ COMMON_SOCK_PERMS,"node_bind", "name_connect",NULL } },{ "udp_socket",{ COMMON_SOCK_PERMS,"node_bind", NULL } },{ "rawip_socket",{ COMMON_SOCK_PERMS,"node_bind", NULL } },{ "node",{ "recvfrom", "sendto", NULL } },{ "netif",{ "ingress", "egress", NULL } },{ "netlink_socket",{ COMMON_SOCK_PERMS, NULL } },{ "packet_socket",{ COMMON_SOCK_PERMS, NULL } },{ "key_socket",{ COMMON_SOCK_PERMS, NULL } },{ "unix_stream_socket",{ COMMON_SOCK_PERMS, "connectto", NULL } },{ "unix_dgram_socket",{ COMMON_SOCK_PERMS, NULL } },{ "sem",{ COMMON_IPC_PERMS, NULL } },{ "msg", { "send", "receive", NULL } },{ "msgq",{ COMMON_IPC_PERMS, "enqueue", NULL } },{ "shm",{ COMMON_IPC_PERMS, "lock", NULL } },{ "ipc",{ COMMON_IPC_PERMS, NULL } },{ "netlink_route_socket",{ COMMON_SOCK_PERMS,"nlmsg_read", "nlmsg_write", NULL } },{ "netlink_tcpdiag_socket",{ COMMON_SOCK_PERMS,"nlmsg_read", "nlmsg_write", NULL } },{ "netlink_nflog_socket",{ COMMON_SOCK_PERMS, NULL } },{ "netlink_xfrm_socket",{ COMMON_SOCK_PERMS,"nlmsg_read", "nlmsg_write", NULL } },{ "netlink_selinux_socket",{ COMMON_SOCK_PERMS, NULL } },{ "netlink_iscsi_socket",{ COMMON_SOCK_PERMS, NULL } },{ "netlink_audit_socket",{ COMMON_SOCK_PERMS,"nlmsg_read", "nlmsg_write", "nlmsg_relay", "nlmsg_readpriv","nlmsg_tty_audit", NULL } },{ "netlink_fib_lookup_socket",{ COMMON_SOCK_PERMS, NULL } },{ "netlink_connector_socket",{ COMMON_SOCK_PERMS, NULL } },{ "netlink_netfilter_socket",{ COMMON_SOCK_PERMS, NULL } },{ "netlink_dnrt_socket",{ COMMON_SOCK_PERMS, NULL } },{ "association",{ "sendto", "recvfrom", "setcontext", "polmatch", NULL } },{ "netlink_kobject_uevent_socket",{ COMMON_SOCK_PERMS, NULL } },{ "netlink_generic_socket",{ COMMON_SOCK_PERMS, NULL } },{ "netlink_scsitransport_socket",{ COMMON_SOCK_PERMS, NULL } },{ "netlink_rdma_socket",{ COMMON_SOCK_PERMS, NULL } },{ "netlink_crypto_socket",{ COMMON_SOCK_PERMS, NULL } },{ "appletalk_socket",{ COMMON_SOCK_PERMS, NULL } },{ "packet",{ "send", "recv", "relabelto", "forward_in", "forward_out", NULL } },{ "key",{ "view", "read", "write", "search", "link", "setattr", "create",NULL } },{ "dccp_socket",{ COMMON_SOCK_PERMS,"node_bind", "name_connect", NULL } },{ "memprotect", { "mmap_zero", NULL } },{ "peer", { "recv", NULL } },{ "capability2",{ COMMON_CAP2_PERMS, NULL } },{ "kernel_service", { "use_as_override", "create_files_as", NULL } },{ "tun_socket",{ COMMON_SOCK_PERMS, "attach_queue", NULL } },{ "binder", { "impersonate", "call", "set_context_mgr", "transfer",NULL } },{ "cap_userns",{ COMMON_CAP_PERMS, NULL } },{ "cap2_userns",{ COMMON_CAP2_PERMS, NULL } },{ "bpf",{"map_create", "map_read", "map_write", "prog_load", "prog_run"} },{ NULL }};
把dir所对应的展开一下:
{ "dir",{ "ioctl", "read", "write", "create", "getattr", "setattr","lock", "relabelfrom", "relabelto", "append", "unlink", "link","rename", "execute", "quotaon", "mounton", "audit_access","open", "execmod","add_name", "remove_name","reparent", "search", "rmdir", NULL }
},
其实这里对应的就是权限:“write"和"search”
也就是说,这一次权限检查,要检查的权限是对type为vendor_data_file的目录(dir)的"write"和"search"权限
其他的其实也是一样的道理,kernel里面的代码规定了进程在访问某些对象的时候所需要的权限
再比如:
mkdir /data/testdir[ 2336.839823@1] type=1400 audit(1230741525.976:909): avc: denied { getattr } for pid=18561 comm="mkdir"
path="/data/testdir" dev="mmcblk0p21" ino=1149 scontext=u:r:testA:s0 tcontext=u:object_r:system_data_file:s0 tclass=dir permissive=0[ 2336.754088@3] [bc6e5b7c+ 16][<c020e3c0>] show_stack+0x20/0x24
[ 2336.759889@3] [bc6e5b9c+ 32][<c05daf0c>] dump_stack+0x90/0xac
[ 2336.765696@3] [bc6e5bf4+ 88][<c055e978>] slow_avc_audit+0x108/0x128
[ 2336.772019@3] [bc6e5c74+ 128][<c055f040>] avc_has_perm+0x13c/0x170
[ 2336.778172@3] [bc6e5c8c+ 24][<c0560334>] inode_has_perm+0x48/0x5c
[ 2336.784326@3] [bc6e5cb4+ 40][<c0562b00>] selinux_inode_getattr+0x6c/0x74
[ 2336.791085@3] [bc6e5cd4+ 32][<c055ab40>] security_inode_getattr+0x4c/0x68
[ 2336.797934@3] [bc6e5cec+ 24][<c03a1ff4>] vfs_getattr+0x20/0x38
[ 2336.803826@3] [bc6e5d24+ 56][<c03a20d4>] vfs_fstatat+0x70/0xb0
[ 2336.809718@3] [bc6e5d8c+ 104][<c03a2804>] SyS_fstatat64+0x24/0x40
[ 2336.815787@3] [00000000+ 0][<c0208680>] ret_fast_syscall+0x0/0x48inode_has_perm
kernelcode/security/selinux/hooks.c:1705selinux_inode_getattr
kernelcode/security/selinux/hooks.c:3089security_inode_getattr
kernelcode/security/security.c:631 (discriminator 5)vfs_getattr
kernelcode/fs/stat.c:70vfs_fstatat
kernelcode/fs/stat.c:110SYSC_fstatat64
kernelcode/fs/stat.c:440
首先检查的需要的权限是:getattr
static int selinux_inode_getattr(const struct path *path)
{return path_has_perm(current_cred(), path, FILE__GETATTR);
}#define FILE__GETATTR 0x00000010UL
3、裁决
前面的分析,我们知道了需要的权限是0x00400004,拥有的权限是0x00120010
那代码是怎么来判断权限是否都ok呢?
先看看权限是怎么从policydb里面读取,然后转换的
inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,u16 tclass, u32 requested,unsigned flags,struct av_decision *avd)
{......node = avc_lookup(ssid, tsid, tclass);if (unlikely(!node))node = avc_compute_av(ssid, tsid, tclass, avd, &xp_node);elsememcpy(avd, &node->ae.avd, sizeof(*avd));......
}
首先从avc cache里面找看看有没有缓存值,这个是为了提高运行效率的,系统里面成千上万条规则,如果每一次判断权限都要从文件里面读取然后计算结果,这将会是一个巨大的工作量
所以采用了内存里面缓存之前计算的结果,如果没有的话那就调用avc_compute_av函数计算一次
接着调用security_compute_av
kernelcode/security/selinux/ss/services.c
void security_compute_av(u32 ssid,u32 tsid,u16 orig_tclass,struct av_decision *avd,struct extended_perms *xperms)
{......context_struct_compute_av(scontext, tcontext, tclass, avd, xperms);map_decision(orig_tclass, avd, policydb.allow_unknown);......
}
函数比较长,这里只看最重要的两句
context_struct_compute_av是从policydb里面读取出来的权限值,在这里就是0x00120010
map_decision会将权限值做一个映射关系,如果认真对比classmap.h和access_vectors里面dir权限的顺序,会发现其实不是完全一样的
static void map_decision(u16 tclass, struct av_decision *avd,int allow_unknown)
{if (tclass < current_mapping_size) {unsigned i, n = current_mapping[tclass].num_perms;u32 result;for (i = 0, result = 0; i < n; i++) {if (avd->allowed & current_mapping[tclass].perms[i])result |= 1<<i;if (allow_unknown && !current_mapping[tclass].perms[i])result |= 1<<i;}avd->allowed = result;......}
}//current_mapping 初始化的时候,是从classmap.h的secclass_map读取的
rc = selinux_set_mapping(&policydb, secclass_map,¤t_mapping,¤t_mapping_size);
这里会将0x00120010转成0x00480010
和上面一样的道理:
0x00480010二进制表示:
0000 0000 0100 1000 0000 0000 0001 0000这里的每一个位代表着不同的权限,1是有权限,0是没有权限
那么0x00480010对应的权限:
和前面的权限是一致的,这里没有搞清楚为什么要有一个映射关系,感觉直接搞成一样的顺序不是更方便吗?
那接下来要比较的权限就是0x00480010(拥有的权限)和0x00400004(需要的权限)
找到0x00400004(需要的权限)为1 但是 0x00480010(拥有的权限)不为 1的那一位,就是缺少权限的
计算方法可以看看函数:
static inline u32 avc_audit_required(u32 requested,struct av_decision *avd,int result,u32 auditdeny,u32 *deniedp)
{u32 denied, audited;//最重要的就是这个计算denied = requested & ~avd->allowed;......
}
那么最后计算的结果就是
0x00400004 & ~0x00480010 = 0x00000004
根据前面的描述,这个权限就是 “write”
所以会看到avc denied信息里面会提示你缺少write权限
[ 5424.996583@0]- type=1400 audit(1577887433.668:59): avc: denied { write } for pid=4021 comm="testA.sh"
name="vendor" dev="mmcblk0p20" ino=7395 scontext=u:r:testA:s0 tcontext=u:object_r:vendor_data_file:s0 tclass=dir permissive=0
上面提到的只是一个简单的例子,其他的权限也可以用类似的方式分析
我们用一张图来总结下:
4、其他
下面聊聊servicemanager里面一些权限的检查,这个是Android才有的
前面的流程可以先看看这篇文章:Binder系列3—启动ServiceManager
我们在这直接看到svcmgr_handler
比较常看见的两个权限问题,find和add
case SVC_MGR_CHECK_SERVICE:s = bio_get_string16(msg, &len);if (s == NULL) {return -1;}handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);if (!handle)break;bio_put_ref(reply, handle);return 0;case SVC_MGR_ADD_SERVICE:s = bio_get_string16(msg, &len);if (s == NULL) {return -1;}handle = bio_get_ref(msg);allow_isolated = bio_get_uint32(msg) ? 1 : 0;dumpsys_priority = bio_get_uint32(msg);if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority,txn->sender_pid))return -1;break;
这里面的调用栈都会跑到kernel里面,和前面的分析都差不多的
do_add_service -> svc_can_register -> check_mac_perms_from_lookup -> check_mac_perms ->
selinux_check_access -> avc_has_perm -> avc_has_perm_noaudit
重点的这里的权限需要哪些,这个是在service_manager.c里面指定的
比如add 和 find :
static int svc_can_find(const uint16_t *name, size_t name_len, pid_t spid, uid_t uid)
{const char *perm = "find";return check_mac_perms_from_lookup(spid, uid, perm, str8(name, name_len)) ? 1 : 0;
}static int svc_can_register(const uint16_t *name, size_t name_len, pid_t spid, uid_t uid)
{const char *perm = "add";if (multiuser_get_app_id(uid) >= AID_APP) {return 0; /* Don't allow apps to register services */}return check_mac_perms_from_lookup(spid, uid, perm, str8(name, name_len)) ? 1 : 0;
}
不过这里的打印不再是kernel log里面的了,而是logcat信息里面,那它的打印是怎么来的?
06-21 13:12:58.631 2832 2832 E SELinux : avc: denied { find } for service=activity pid=4138 uid=0 scontext=u:r:testA:s0 tcontext=u:object_r:activity_service:s0 tclass=service_manager permissive=1
在前面提到的avc_has_perm函数里面,还有一句
rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata, 0);
/android/external/selinux/libselinux/src/avc.c
void avc_audit(security_id_t ssid, security_id_t tsid,security_class_t tclass, access_vector_t requested,struct av_decision *avd, int result, void *a)
{access_vector_t denied, audited;denied = requested & ~avd->allowed;if (denied)audited = denied & avd->auditdeny;else if (!requested || result)audited = denied = requested;elseaudited = requested & avd->auditallow;if (!audited)return;
#if 0if (!check_avc_ratelimit())return;
#endif/* prevent overlapping buffer writes */avc_get_lock(avc_log_lock);snprintf(avc_audit_buf, AVC_AUDIT_BUFSIZE,"%s: %s ", avc_prefix, (denied || !requested) ? "denied" : "granted");avc_dump_av(tclass, audited);log_append(avc_audit_buf, " for ");/* get any extra information printed by the callback */avc_suppl_audit(a, tclass, avc_audit_buf + strlen(avc_audit_buf),AVC_AUDIT_BUFSIZE - strlen(avc_audit_buf));log_append(avc_audit_buf, " ");avc_dump_query(ssid, tsid, tclass);if (denied)log_append(avc_audit_buf, " permissive=%u", result ? 0 : 1);log_append(avc_audit_buf, "\n");avc_log(SELINUX_AVC, "%s", avc_audit_buf);avc_release_lock(avc_log_lock);
}
最后调用的是avc_log打印的,而这个其实这里是回调service_manager.c设置的callback函数
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
cb.func_log = selinux_log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);int selinux_log_callback(int type, const char *fmt, ...)
{va_list ap;int priority;char *strp;switch(type) {case SELINUX_WARNING:priority = ANDROID_LOG_WARN;break;case SELINUX_INFO:priority = ANDROID_LOG_INFO;break;default:priority = ANDROID_LOG_ERROR;break;}va_start(ap, fmt);if (vasprintf(&strp, fmt, ap) != -1) {LOG_PRI(priority, "SELinux", "%s", strp);LOG_EVENT_STRING(AUDITD_LOG_TAG, strp);free(strp);}va_end(ap);return 0;
}
关于Android里面的属性,也有相应的权限检查机制,有兴趣的可以看看相关代码~
二、avc denied信息分析
kernel log中的avc denied来自于common_lsm_audit,还有函数avc_audit_pre_callback、avc_audit_post_callback,把avc denied的每一段信息拼接起来
最后是用printk打印到kernel log里面
kernelcode/security/selinux/avc.c
/* This is the slow part of avc audit with big stack footprint */
noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,u32 requested, u32 audited, u32 denied, int result,struct common_audit_data *a,unsigned flags)
{......common_lsm_audit(a, avc_audit_pre_callback, avc_audit_post_callback);return 0;
}
下面针对几个关键的信息分析下
- { write }
缺失的权限,这个是本文讨论的核心部分
打印来自于函数 avc_dump_av (kernelcode/security/selinux/avc.c)
/*** avc_dump_av - Display an access vector in human-readable form.* @tclass: target security class* @av: access vector*/
static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
{const char **perms;int i, perm;if (av == 0) {audit_log_format(ab, " null");return;}BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map));perms = secclass_map[tclass-1].perms;audit_log_format(ab, " {");i = 0;perm = 1;while (i < (sizeof(av) * 8)) {if ((perm & av) && perms[i]) {audit_log_format(ab, " %s", perms[i]);av &= ~perm;}i++;perm <<= 1;}if (av)audit_log_format(ab, " 0x%x", av);audit_log_format(ab, " }");
}
分析avc_dump_av代码逻辑,其实就是在secclass_map数组里面找到权限对应的字符串,打印出来方便分析
和前面部分提到的内容是相关的
for pid=4021
进程IDcomm=“testA.sh”
进程的名称,但是这个字符串设计的是有长度限制的,所以经常看见一些apk的包名这里会打印不完整
[ 5786.420602@3] type=1400 audit(1624264582.595:426): avc: denied { open } for pid=4686 comm="testA.launcher3" path="/proc/vmstat" dev="proc" ino=4026532002 scontext=u:r:untrusted_app:s0:c46,c256,c512,c768 tcontext=u:object_r:proc_vmstat:s0 tclass=file permissive=1实际上进程名是com.testA.launcher3
scontext=u:r:testA:s0
主体的安全上下文,这里就是脚本所在的进程testA.shtcontext=u:object_r:vendor_data_file:s0
被操作对象的安全上下文,这里是/data/vendor/目录的安全上下文,默认在这个目录下面创建的文件的安全上下文,都会继承这个父目录的安全上下文tclass=dir
种类,dir是目录,file文件,这个也比较好理解
所以后面写te规则的时候,其实就是
allow scontext tcontext:tclass perms;也就是:allow testA vendor_data_file:dir { write };
有个工具叫audit2allow,可以将报错信息自动生成te规则,其实讲实话我并不建议使用
首先,格式有要求,动不动转失败,还有转漏的;
其次,你都不清楚需要的权限是什么,就一顿操作,把所有报错信息都加上去了,可能加的一些权限和问题没有关联,结果还违反neverallow规则,改完以后根本追溯不了
我建议直接看log信息添加权限信息,结合SELinux的知识,添加的权限有理有据,也可以提升自己的能力
三、调试
1、快速编译和替换
1.1 编译和替换
修改的内容 | 编译命令 | 替换方式 |
---|---|---|
*.te |
make precompiled_sepolicy 或者 make selinux_policy |
/vendor/etc/selinux/precompiled_sepolicy 或者 /odm/etc/selinux/precompiled_sepolicy |
property_contexts | 可以直接板子上验证 |
直接到板子上改 /vendor/etc/selinux/vendor_property_contexts、 /system/etc/selinux/plat_property_contexts |
file_contexts | 可以直接板子上验证 |
直接到板子上改 /vendor/etc/selinux/vendor_file_contexts、 /system/etc/selinux/plat_file_contexts |
service_contexts | 可以直接板子上验证 |
直接到板子上改 /vendor/etc/selinux/vendor_hwservice_contexts、 /system/etc/selinux/plat_hwservice_contexts、 /system/etc/selinux/plat_service_contexts |
有很多文章提到在Android 9.0上,编译策略文件要用make sepolicy然后替换/vendor/etc/selinux /system/etc/selinux 两个目录,其实这里是有问题的
1、make sepolicy之后,/vendor/etc/selinux /system/etc/selinux 目录下的产物是不会发生变化,因此替换整个目录是没有作用的(如果lunch的时候target发生变化,那也只是第一次会重新生成,后面再修改te也不会变)
2、system/sepolicy目录下, mma -j编译后,替换是可以的,因为这个相当于把android/system/sepolicy/Android.mk 的所有module都编译了一遍
讲讲由来:
从Android 8.0之后,因为Google project treble的影响,二进制策略文件不再是根目录的 /sepolicy (也就是make sepolicy 编译生成的产物 root/sepolicy)
而是 vendor/etc/selinux/precompiled_sepolicy (或者是odm/etc/selinux/precompiled_sepolicy)
相应的,编译的命令也有变化,修改了*.te,更正确的应该是使用make precompiled_sepolicy
生成 vendor/etc/selinux/precompiled_sepolicy 替换到板子即可 (如果存在odm分区,生成在 odm/etc/selinux/precompiled_sepolicy,替换到板子的路径也是对应的)
那make sepolicy和make precompiled_sepolicy 有什么区别?
编译出来的产物其实内容是一样的,但是生成路径不一样
之所以说有误解,是因为我们没有搞清楚真正要替换的是什么
make selinux_policy 我比较常使用,因为这个编译范围把需要的都编译了,在对应目录下的文件都会更新
这里还需要注意一下,修改/vendor 、 /system下面文件的安全上下文(在/vendor/etc/selinux/vendor_file_contexts、/system/etc/selinux/plat_file_contexts)时,
可能出现修改了没有生效的问题,要注意用restorecon命令恢复一下安全上下文,这些只读分区的文件是一开始编译的时候确定下来的,
所以会发现有些时候没有改成功,最关键的就是出现这些问题的时候,多用ls -Z、getprop -Z看看安全上下文
比如:
console:/ # ls -lZ /system/xbin/testB
-rwxr-xr-x 1 root shell u:object_r:system_file:s0 20380 2009-01-01 00:00 /system/xbin/testB修改了file_contextconsole:/ # restorecon -R /system/xbin/testB
SELinux: Loaded file_contexts
console:/ #
console:/ # ls -lZ /system/xbin/testB
-rwxr-xr-x 1 root shell u:object_r:testB_exec:s0 20380 2009-01-01 00:00 /system/xbin/testB
1.2 load_policy
上面的方式在替换之后需要重启,开机会重新load新的二进制策略文件
还有一个命令也很方便:
load_policy
我们可以直接加载二进制策略文件
load_policy /storage/xxxx/precompiled_sepolicy
不过只在本次开机才有效,重启就没有效果了,所以如果是开机过程的SELinux权限问题,还是得乖乖的替换文件
2、尽量使用宏
对于一些文件和目录的操作,我们可以尽量使用宏
比如:
allow testC vendor_data_file:dir { write add_name create getattr read setattr open remove_name rmdir };
allow testC vendor_data_file:file { create read write open ioctl getattr lock append unlink setattr };
这里脚本的目的是要创建目录和文件
如果按我们现在的方式,等着系统一条一条的报权限问题,那这个过程会发现你得反复添加编译再替换到板子,很浪费时间
但其实可以直接添加下面的权限:
allow testC vendor_data_file:dir create_dir_perms;
allow testC vendor_data_file:file create_file_perms;
create_dir_perms和create_file_perms的定义在/android/system/sepolicy/public/global_macros
如果只是读取,那就直接用r_file_perms、r_dir_perms
使用宏会比较快的解决文件和目录相关的操作,这个也是遇到比较多的一种
还有一个宏定义的地方:
/android/system/sepolicy/public/te_macros
对于属性,通常用的是这两个
get_prop(testC, system_prop)
set_prop(testC, system_prop)
这里是有包含关系的,set_prop宏里面是包含了get_prop的,所以其实写了set_prop就不用写get_prop了
对于binder,这两个用的比较多
binder_use
binder_call
遇到一些权限问题的时候,可以先看看te_macros里面的,搜一搜,看看注释,会有很大帮助的
当然,还可以自定义宏,可以少写很多重复的规则,比如下面这个:
define(`testD_common_file_dir_r_perms', `
allow testD $1:file r_file_perms;
allow testD $1:dir r_dir_perms;
')然后使用:
testD_common_file_dir_r_perms(init)
testD_common_file_dir_r_perms(kernel)
testD_common_file_dir_r_perms(logd)
testD_common_file_dir_r_perms(vold)
testD_common_file_dir_r_perms(audioserver)
testD_common_file_dir_r_perms(lmkd)
testD_common_file_dir_r_perms(tee)
testD_common_file_dir_r_perms(surfaceflinger)
testD_common_file_dir_r_perms(hdmicecd)
testD_common_file_dir_r_perms(hwservicemanager)
testD_common_file_dir_r_perms(netd)
testD_common_file_dir_r_perms(sdcardfs)
testD_common_file_dir_r_perms(servicemanager)
3、使用属性
如果出现下面这种一直报的同一类型和权限的
比如service_manager { find }
allow testD activity_service:service_manager { find };
allow testD package_service:service_manager { find };
allow testD window_service:service_manager { find };
allow testD meminfo_service:service_manager { find };
allow testD cpuinfo_service:service_manager { find };
allow testD mount_service:service_manager { find };
allow testD surfaceflinger_service:service_manager { find };
allow testD audioserver_service:service_manager { find };
allow testD secure_element_service:service_manager { find };
allow testD contexthub_service:service_manager { find };
allow testD netd_listener_service:service_manager { find };
allow testD connmetrics_service:service_manager { find };
allow testD bluetooth_manager_service:service_manager { find };
allow testD imms_service:service_manager { find };
allow testD cameraproxy_service:service_manager { find };
allow testD slice_service:service_manager { find };
allow testD media_projection_service:service_manager { find };
allow testD crossprofileapps_service:service_manager { find };
allow testD launcherapps_service:service_manager { find };
allow testD shortcut_service:service_manager { find };
allow testD media_router_service:service_manager { find };
allow testD hdmi_control_service:service_manager { find };
allow testD media_session_service:service_manager { find };
allow testD restrictions_service:service_manager { find };
allow testD graphicsstats_service:service_manager { find };
allow testD dreams_service:service_manager { find };
allow testD commontime_management_service:service_manager { find };
allow testD network_time_update_service:service_manager { find };
allow testD diskstats_service:service_manager { find };
allow testD voiceinteraction_service:service_manager { find };
allow testD appwidget_service:service_manager { find };
allow testD backup_service:service_manager { find };
allow testD trust_service:service_manager { find };
allow testD voiceinteraction_service:service_manager { find };
allow testD jobscheduler_service:service_manager { find };
allow testD hardware_properties_service:service_manager { find };
allow testD serial_service:service_manager { find };
allow testD usb_service:service_manager { find };
allow testD DockObserver_service:service_manager { find };
allow testD audio_service:service_manager { find };
allow testD wallpaper_service:service_manager { find };
allow testD search_service:service_manager { find };
allow testD country_detector_service:service_manager { find };
allow testD location_service:service_manager { find };
allow testD devicestoragemonitor_service:service_manager { find };
allow testD notification_service:service_manager { find };
allow testD updatelock_service:service_manager { find };
allow testD system_update_service:service_manager { find };
allow testD servicediscovery_service:service_manager { find };
allow testD connectivity_service:service_manager { find };
allow testD ethernet_service:service_manager { find };
allow testD wifip2p_service:service_manager { find };
allow testD wifiscanner_service:service_manager { find };
allow testD wifi_service:service_manager { find };
allow testD netpolicy_service:service_manager { find };
allow testD netstats_service:service_manager { find };
allow testD network_score_service:service_manager { find };
allow testD textclassification_service:service_manager { find };
allow testD textservices_service:service_manager { find };
allow testD ipsec_service:service_manager { find };
allow testD network_management_service:service_manager { find };
allow testD clipboard_service:service_manager { find };
allow testD statusbar_service:service_manager { find };
allow testD device_policy_service:service_manager { find };
allow testD deviceidle_service:service_manager { find };
allow testD uimode_service:service_manager { find };
allow testD lock_settings_service:service_manager { find };
allow testD storagestats_service:service_manager { find };
allow testD accessibility_service:service_manager { find };
allow testD input_method_service:service_manager { find };
allow testD pinner_service:service_manager { find };
allow testD network_watchlist_service:service_manager { find };
allow testD vr_manager_service:service_manager { find };
allow testD input_service:service_manager { find };
可以找到这些type的共同点,也就是attribute
上面的可以简化为下面的,减去的哪些type是因为违反了neverallow规则的
allow testD { service_manager_type -stats_service -incident_service -vold_service -netd_service -installd_service -dumpstate_service }:service_manager { find };
4、setools工具
这个工具挺好用的,可以分析二进制策略文件,比如用于某些情况下判断权限是否加上了
GitHub上有:setools3 工具
$ sesearch -A precompiled_sepolicy | grep kernel
allow kernel app_data_file:file read;
allow kernel asec_image_file:file read;
allow kernel binder_device:chr_file { append getattr ioctl lock map open read write };
allow kernel debugfs_mali:dir search;
allow kernel device:blk_file { create getattr setattr unlink };
allow kernel device:chr_file { create getattr setattr unlink };
allow kernel device:dir { add_name append create getattr ioctl lock map open read remove_name rmdir search write };
allow kernel file_contexts_file:file { getattr ioctl lock map open read };
allow kernel hwbinder_device:chr_file { append getattr ioctl lock map open read write };
allow kernel init:process { rlimitinh share siginh transition };
allow kernel init_exec:file { execute getattr map open read relabelto };
allow kernel kernel:cap_userns { sys_boot sys_nice sys_resource };
allow kernel kernel:capability { mknod sys_boot sys_nice sys_resource };
allow kernel kernel:dir { getattr ioctl lock open read search };
allow kernel kernel:fd use;
allow kernel kernel:fifo_file { append getattr ioctl lock map open read write };
allow kernel kernel:file { append getattr ioctl lock map open read write };
allow kernel kernel:lnk_file { getattr ioctl lock map open read };
allow kernel kernel:process { fork getattr getcap getpgid getsched getsession setcap setpgid setrlimit setsched sigchld sigkill signal signull sigstop };
allow kernel kernel:security setcheckreqprot;
......
四、参考文章
《Linux强制访问控制机制模块代码分析报告》
- https://www.sohu.com/a/135967369_467784
- https://www.sohu.com/a/136011893_467784
- https://www.sohu.com/a/136288365_467784
- https://www.sohu.com/a/136482060_467784
- https://www.sohu.com/a/136639046_467784
- http://www.safebase.cn/article-231024-1.html
- https://www.sohu.com/a/137347468_467784
- https://www.sohu.com/a/137512129_467784
- https://www.sohu.com/a/137888523_467784
- https://www.sohu.com/a/137888531_467784
- https://www.sohu.com/a/138185447_467784
《Linux多安全策略和动态安全策 略框架模块代码分析报告》
- https://www.sohu.com/a/138580970_467784
- http://www.safebase.cn/article-231245-1.html
- https://www.sohu.com/a/138804853_467784
- https://www.sohu.com/a/138976820_467784
- http://www.voidcn.com/article/p-sfquuwtm-wk.html
- https://www.sohu.com/a/139413489_467784
- https://www.sohu.com/a/139709740_467784
- https://www.sohu.com/a/139973785_467784
- http://www.voidcn.com/article/p-xhablags-wk.html
- http://www.voidcn.com/article/p-uwdtubpm-wk.html
- https://www.sohu.com/a/140884205_467784
- http://www.voidcn.com/article/p-tkroeamc-wk.html
- https://www.sohu.com/a/141414625_467784
- https://www.sohu.com/a/141709117_467784
- https://m.sohu.com/a/142034082_467784/
- http://www.voidcn.com/article/p-ksphumbr-wk.html
- https://www.sohu.com/a/142438688_467784
- http://www.voidcn.com/article/p-blaxfzwg-wk.html
- http://www.voidcn.com/article/p-gfifderx-wk.html
- https://m.sohu.com/a/143323077_467784/
- http://www.voidcn.com/article/p-eoijaouz-wk.html
- https://www.sohu.com/a/143971000_467784
- http://www.voidcn.com/article/p-zfzucopl-wk.html
fs_use_xattr
https://blog.csdn.net/keheinash/article/details/78507063
https://blog.csdn.net/l173864930/article/details/17194899
其他
- Linux 编程中的API函数和系统调用的关系
- Linux 中open系统调用实现原理
- linux syscall 详解
- Linux 进程安全上下文 struct cred
- linux cred管理
- openVswitch(OVS)源代码分析之工作流程(哈希桶结构体的解释)
- openVswitch(OVS)源代码的分析技巧(哈希桶结构体为例)
- 神奇的Selinux Restore Rule
Android P SELinux (三) 权限检查原理与调试相关推荐
- Android 修改 SELinux avc 权限的方法
系统版本:Android 11.0 平 台:RK3568 在 Android 系统的开发及适配过程中,我们常常需要对 SELinux avc 权限进行修改,以下是我对 SEL ...
- 探索 Sa-Token (三) 权限认证原理
前言:前一篇文章我们做了权限认证,看着就用一个注解就是实现权限认证,那么他的底层原理怎么实现的呢?家人们,不要着急,让我慢慢道来. # SaCheckPermission 注解 package cn. ...
- Android P SELinux (二) 开机初始化与策略文件编译过程
Android P SELinux (一) 基础概念 Android P SELinux (二) 开机初始化与策略文件编译过程 Android P SELinux (三) 权限检查原理与调试 Andr ...
- Android P SELinux (四) CTS neverallow处理总结
Android P SELinux (一) 基础概念 Android P SELinux (二) 开机初始化与策略文件编译过程 Android P SELinux (三) 权限检查原理与调试 Andr ...
- 2022-07-20 Android 11 SELinux avc 修改sys目录下面某个节点的权限
一.我这里有/sys/devices/platform/thermal-camera-control/powerenable 这样一个节点,用命令ls -Z 查看该文件的域. 二.我现在在一个普通ap ...
- 【Android 热修复】热修复原理 ( Dex 文件拷贝后续操作 | 外部存储空间权限申请 | 执行效果验证 | 源码资源 )
文章目录 一.Dex 文件准备 二.外部存储空间权限申请 1.清单文件申请权限 2.动态申请权限 三.文件拷贝 1.文件拷贝 2.执行效果 四. 源码资源 一.Dex 文件准备 在 [Android ...
- Android权限管理原理(含6.0)
前言 Android系统在MarshMallow之前,权限都是在安装的时候授予的,虽然在4.3时,Google就试图在源码里面引入AppOpsManager来达到动态控制权限的目的,但由于不太成熟,在 ...
- Android应用.三星i9000系列(4).SuperOneClick获取Root权限的原理
Android应用.三星i9000系列(4).SuperOneClick获取Root权限的原理 草木瓜 20110408 一.前言 经过笔者自己测试与分析,得出结论:所以Android手机获取Root ...
- Android 操作系统 获取Root权限 原理解析
android root权限破解分析 许多机友新购来的Android机器没有破解过Root权限,无法使用一些需要高权限的软件,以及进行一些高权限 的操作,其实破解手机Root权限是比较简单 及安全的, ...
最新文章
- Python接口自动化测试框架(基础篇)-- 常用数据类型Number
- linux安装tree命令
- php 提交网页 传值 获取编辑框的值
- 总结】Android辅助功能(一)-AccessibilityEvent的分发
- C++words search单词搜索的算法实现(附完整源码)
- 11g crsctl start/stop crs 和 crsctl start/stop cluster 的关系
- Android启动(Booting)
- python新版下载安装_各种版本的Python下载安装教程
- android studio gradle 学习,学习Android Studio里的Gradle
- 防止Linux库so中的接口冲突
- Modbus协议栈开发笔记之二:Modbus消息帧的生成
- 北京师范大学c语言题库,北京师范大学C语言题库.doc
- 远程桌面管理工具源码
- 掌握茼蒿施肥方式,想不高产都难。
- 3、身份证、数字、日期、手机号码等等的验证判断
- 若依分离版在windows上部署(1)
- 【C# 练习】最少需要准备多少张人民币,才能在给每个人发工资的时候都不用找零呢,人民币一共有100元、50元、10元、5元、2元和1元六种
- Vue实现导航栏切换
- 2----Android手机小白知识全集!! 刚入手
- 目标检测 TP\FP\FN\TN如何理解?FN和TN无意义