rootckeck
rootcheck
1.问题描述
经常会有听说root手机,其实质就是让使用手机的人获得手机系统的最大权限。因为android系统是基于linux内核开发出来的系统,对不同等级的用户会授予不同的权限,其中权限最大的就是root权限。而rootcheck(Root check mechanism to avoid being rooted)就是是一个手机的保护机制,防止用户因为获得过大的权限而对手机造成“破坏”,该机制的主要作用就是检查手机是否被root过。
2.analysis
Root Check功能,用于检测机器是否被用户root,用户root手机有2种方式:
- 通过刷机root手机,即替换掉手机了的某些image文件, 从而增/删/改某些功能或模块。
- 通过root工具,为用户提供root权限,从而使用户可以以root权限去做某些原本不允许操作的操作。
Solution:根据上面用户的2中root方式,所以会通过如下方法来检查用户是否已经root了手机。
- 对image进行处理,主要是指在Uboot(U开头/lk.bin)、Boot image(B开头/boot.img)、System image(Y开头/system.img)这三个image的文件头填写标识字符串,如果用户通过刷机root手机,则该写入的标示会被擦除掉(当然一般是默认用户不知道该标识的写入位置和标示内容的,要是用户都知道确实可以跳过此检测方法的)。
- 添加root检测进程到手机, 如果发现手机用户有root权限,则该进程就会在某个地方写入标记。在检验事只要发现这个标记存在, 则判定手机被root过。
3.solution
- 通过脚本对lk(uboot是早期的lk), boot, system 3个image的头部偏移0x100字节处, 分别写入特定字符. 代码如下:
#!perl -w
my $prj = $ARGV[0];
my $outdir = "out/target/product/$prj";
if($#ARGV == 0) {
&add_magic();
} else {
&usage();
}
sub add_magic {
my $us_offset = 0x100;
my $ls_offset = 0x100;
my $bs_offset = 0x100;
my $ss_offset = 0x100;
my $pattern = "109f10eed3f021e3";
if (-e "$outdir/uboot_$prj.bin") {
open FP, "+<$outdir/uboot_$prj.bin" or die "can't open uboot image!\n";
binmode FP;
seek FP, $us_offset, 0 or die $!;
print FP $pattern;
print "uboot_$prj.bin signed.\n";
close FP;
}
if (-e "$outdir/lk.bin") {
open FP, "+<$outdir/lk.bin" or die "can't open lk image!\n";
binmode FP;
seek FP, $ls_offset, 0 or die $!;
print FP $pattern;
print "lk.bin signed.\n";
close FP;
}
if (-e "$outdir/boot.img") {
open FP, "+<$outdir/boot.img" or die "can't open boot image!\n";
binmode FP;
seek FP, $bs_offset, 0 or die $!;
print FP $pattern;
print "boot.img signed.\n";
close FP;
}
}
sub usage {
print "usage: perl jrd_magic.pl project_name\)n";
exit (0);
}
- 创建一个应用,用来检测用户是否有root权限,相关代码如下所示:
首先,创建一个编译脚本Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := jb.c
LOCAL_CFLAGS += -DDBGMODE=0
LOCAL_MODULE:= forcc
LOCAL_STATIC_LIBRARIES := libcutils
LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)
然后,编写具体的程序代码jb.c如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/inotify.h>
#include <errno.h>
#include <cutils/properties.h>
#define EVENT_NUM 16
#define MAX_BUF_SIZE 1024
#define MAX_PATH_LEN 255
#define MAGIC_SIZE 16
#define MAGIC_ROOTED "109f10eed3f021e3"
#define MAGIC_OFFSET (2*1024*1024)
#define PROINFO_DEV_NODE "/dev/pro_info"
#define FNUM 4
#define WD_NUM 3
static const char * monitored_folders[] = {
"/sbin",
"/system/bin",
"/system/xbin"
};
struct wd_name {
int wd;
char * name;
};
static const char *g_breaker_filename[] = {
"su",
"Su",
"sU",
"SU"
};
struct wd_name wd_array[WD_NUM];
#ifdef DEBUG
char * event_array[] = {
"File was accessed",
"File was modified",
"File attributes were changed",
"writtable file closed",
"Unwrittable file closed",
"File was opened",
"File was moved from X",
"File was moved to Y",
"Subfile was created",
"Subfile was deleted",
"Self was deleted",
"Self was moved",
"",
"Backing fs was unmounted",
"Event queued overflowed",
"File was ignored"
};
#endif
/*将magic number 写入手机某个节点中,或者从某个节点中读取sz个字符到magic中*/
static int rw_pro_info(const size_t offset, const ssize_t sz,const int rw, char* magic)
{
ssize_t count = 0;
int fid;
if(1 == rw) { /*read command*/
fid = open(PROINFO_DEV_NODE, O_RDONLY);
if(fid < 0){
fprintf(stderr, "can not open file %s\n", PROINFO_DEV_NODE);
goto bail;
}
lseek(fid, offset, SEEK_SET);
count = read(fid, magic, sz);
if(count < sz){
fprintf(stderr, "read magic fails\n");
if(fid > 0)
close(fid);
}
} else { /*write command*/
fid = open(PROINFO_DEV_NODE, O_RDWR|O_SYNC);
if(fid < 0){
fprintf(stderr, "can not open file %s\n", PROINFO_DEV_NODE);
goto bail;
}
lseek(fid, offset, SEEK_SET);
count = write(fid, magic, sz);
if(count < sz){
fprintf(stderr, "write magic fails\n");
if(fid > 0)
close(fid);
}
}
return (count == sz ? 1 : 0);
}
/*分别检测"/sbin","/system/bin","/system/xbin"目录下是否有su这样子的文件,如果有就表示存在被root的风险*/
static int check_su_exists()
{
/*concat pathes, access them*/
size_t sz_files = sizeof(g_breaker_filename)/sizeof(char *);
size_t sz_folders = sizeof(monitored_folders)/sizeof(char *);
int i,j;
char *path = (char *)malloc(sizeof(char) * MAX_PATH_LEN);
int fid;
for(i = 0; i < sz_files; ++i){
for(j = 0; j < sz_folders; ++j){
memset(path, 0, MAX_PATH_LEN);
strncpy(path, monitored_folders[j], strlen(monitored_folders[j]));
strncpy(path + strlen(monitored_folders[j]), "/", 1);
strncpy(path + strlen(monitored_folders[j]) + 1, g_breaker_filename[i],
strlen(g_breaker_filename[i])+1);
#ifdef DEBUG
printf("open file %s\n", path);
#else
fid = open(path, O_RDONLY);
if(fid > 0){
close(fid);
return 1;
}
#endif
}
}
return 0;
}
int main(void)
{
int fd;
int wd;
char buffer[1024];
char * offset = NULL;
struct inotify_event * event;
int len, tmp_len;
char strbuf[16];
int i = 0;
char magic[4];
FILE * rfd =NULL;
int rootflag= 0,count;
/*将去指定位置读数据*/
rw_pro_info(MAGIC_OFFSET, MAGIC_SIZE, 1, &magic[0]);
/*如果该位置已经存在了特定标示 便是已经被root过了*/
if(!strncmp(MAGIC_ROOTED, magic, strlen(MAGIC_ROOTED))) {
property_set("persist.su_flag", "1");
//exit(1); /*already rooted, exit*/
rootflag = 1;
goto EXIT;
} else if(check_su_exists()) {
/*如果存在root风险 将magic number 写入手机某个节点中*/
rw_pro_info(MAGIC_OFFSET, MAGIC_SIZE, 0, MAGIC_ROOTED);
property_set("persist.su_flag", "1");
//exit(1);
rootflag = 1;
goto EXIT;
}
fd = inotify_init();
if (fd < 0) {
printf("Fail to initialize inotify.\n");
exit(-1);
}
for (i=0; i<WD_NUM; i++) {
wd_array[i].name = monitored_folders[i];
wd = inotify_add_watch(fd, wd_array[i].name, IN_MOVED_TO | IN_CREATE);
if (wd < 0) {
printf("Can't add watch for %s.\n", wd_array[i].name);
exit(-1);
}
wd_array[i].wd = wd;
}
while(len = read(fd, buffer, MAX_BUF_SIZE)) {
offset = buffer;
//printf("Some event happens, len = %d.\n", len);
event = (struct inotify_event *)buffer;
while (((char *)event - buffer) < len) {
#ifdef DEBUG
if (event->mask & IN_ISDIR) {
memcpy(strbuf, "Direcotory", 11);
}
else {
memcpy(strbuf, "File", 5);
}
printf("Object type: %s\n", strbuf);
for (i=0; i<WD_NUM; i++) {
if (event->wd != wd_array[i].wd) continue;
printf("Object name: %s\n", wd_array[i].name);
break;
}
printf("Event mask: %08X\n", event->mask);
printf("Event name: %s\n", event->name);
for (i=0; i<EVENT_NUM; i++) {
if (event_array[i][0] == '\0') continue;
if (event->mask & (1<<i)) {
printf("Event: %s\n", event_array[i]);
}
}
#endif
for(i=0; i<FNUM; i++){
if(0 == strcmp(event->name, g_breaker_filename[i])){
rw_pro_info(MAGIC_OFFSET, MAGIC_SIZE, 0, MAGIC_ROOTED);
property_set("persist.su_flag", "1");
//exit(1);
rootflag = 1;
goto EXIT;
}
}
tmp_len = sizeof(struct inotify_event) + event->len;
event = (struct inotify_event *)(offset + tmp_len);
offset += tmp_len;
}
}
EXIT:
if(rootflag == 1){
rfd = fopen("/data/rootflag", "w");
if(rfd == NULL){
fprintf(stderr, "can not open file %s\n", "/data/rootflag");
exit(-1);
}
count = fwrite(magic, 1, MAGIC_SIZE,rfd);
if(count < MAGIC_SIZE){
fprintf(stderr, "write magic fails\n");
fclose(rfd);
exit(-1);
}
fclose(rfd);
chmod("/data/rootflag", 0644);
}
return 0;
}
进过上面的修改后,其实这个应用还是不能生效,其原因就是还没为该应用配置selinux权限。其具体配置如下:
在目录device/mediatek/common/sepolicy/full/目录先为forcc应用创建域
# forcc
type forcc, domain;
type forcc_exec, exec_type, file_type;
init_daemon_domain(forcc)
#定义访问规则
allow forcc nvram_device:blk_file { open read write };
allow forcc block_device:dir search;
allow forcc su_exec:file { open read };
allow forcc rootfs:dir read;
allow forcc system_file:dir read;
修改文件mediatek/common/sepolicy/full/file_contexts,定义/system/bin/forcc文件的type,具体如下:
/system/bin/forcc u:object_r:forcc_exec:s0
进过上面的修改,这个应用就基本ok了。
4.总结
这个问题只要理解了原理,其实也不难。就2个点,第一个点,向boot, system 3个image特定位置写标示,主要由pythen完成;第二个点,检测文件标识是否被修改以及判断设备中是否有su进程。在整个开发过程中可能selinux会麻烦点,反正做的时候,访问规则都是一条一条的加,只要小心点也一切ok 。
rootckeck的更多相关文章
随机推荐
- Shell脚本--数值比较组合判断
用于数值比较的无非大于.小于.等于.大于等于.小于等于这几个. 比较格式: [ 数值1 比较符 数值2 ] 注意左边的括号与数值1之间有一个空格,同样,数值2和右边的括号之间也有空格. 数值比较运 ...
- 我写了个IDEA开源插件,vo2dto 一键生成对象转换
让人头疼的对象转换 头炸,po2vo.vo2do.do2dto,一堆对象属性,取出来塞进来.要不是为了 DDD 架构下的各个分层防腐,真想一竿子怼下去. 那上 BeanUtils.copyProper ...
- AcWing429. 奖学金
题目: 某小学最近得到了一笔赞助,打算拿出其中一部分为学习成绩优秀的前5名学生发奖学金. 期末,每个学生都有3门课的成绩:语文.数学.英语. 先按总分从高到低排序,如果两个同学总分相同,再按语文成绩从 ...
- JS(JQuery) 省市区三级联动下拉选择
引入 area.js /* * 全国三级城市联动 js版 */ function Dsy(){ this.Items = {}; } Dsy.prototype.add = function(id,i ...
- JAVA获取昨天、今天、明天等日期
/** * 获取明天的日期字符串 * @return */ public static String tomorrowDateStr(){ Date date=new Date();//取时间 Cal ...
- 聊一下 TS 中的交叉类型
交叉类型不能完全按照传统编程中的 与 来理解. 交叉类型的定义:将多个类型合并为一个类型,包含了所有类型的特性,而且要同时满足要交叉的所有类型. 后半段话不是很好理解,看一下接口类型和联合类型的交叉类 ...
- c++11之 algorithm 算法库新增 minmax_element同时计算最大值和最小值
0.时刻提醒自己 Note: vector的释放 1. minmax_element 功能 寻找范围 [first, last) 中最小和最大的元素. 2. 头文件 #include <algo ...
- 【LeetCode】628. Maximum Product of Three Numbers 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 方法一:排序 日期 题目地址:https://lee ...
- 【LeetCode】355. Design Twitter 解题报告(Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...
- BestCoder Round #66 (div.2)B GTW likes gt
思路:一个O(n)O(n)的做法.我们发现b_1,b_2,...,b_xb1,b2,...,bx都加11就相当于b_{x+1},b_{x+2},...,b_nbx+1,bx+ ...