Android 13 添加native进程LocalSocket,并添加SELinux权限

由于项目要求,需要在应用程序中执行shell命令,获取系统的log信息。这还不简单,直接使用

Process mProcess = Runtime.getRuntime().exec(command);

调试发现只能获取自身的log信息,无法获取外部进程的log,在之前的Android版本是可以正常获取,在Android13无法获取,猜测是权限问题,去代码找结果。 

logcat命令最终执行的是logd,发现Android 13 对logd使用添加一些限制

    // The logd access will be granted when any of the following three
    // conditions is satisfied:
    // 1. the client (native processes) is exempted from user consent check.
    // 2. the client doesn't have log credentials and so can only access its own log data anyway
    // 3. the client (for EventLog API) only wants to access the event log.
    if (clientIsExemptedFromUserConsent(cli) || !clientHasLogCredentials(cli) ||
        only_read_event_logs) {
        reader_list_->AddAndRunThread(std::move(entry));
    } else {
        if (GetLogdBinderServiceStatus(reader_list_)) {
            reader_list_->AddPendingThread(std::move(entry));
        } else {
            // The logd binder service is not available, we are not able to
            // check user consent. So we revoke the privileges.
            entry->Revoke();
            reader_list_->AddAndRunThread(std::move(entry));
        }   
    } 

翻译一下:

1、native进程可以获取所有log

2、非native进程且没有读取log权限的进程只能读取自身log

3、只读取event log的进程可以读取所有进程的event log

所以java层的应用程序想要获取所有log,需要读取log的权限。然后看方法clientHasLogCredentials,根据名字猜测此方法是判断client是否有读取log的权限。

bool clientHasLogCredentials(SocketClient* cli) {
    if (UserIsPrivileged(cli->getUid()) || UserIsPrivileged(cli->getGid())) {
        return true;
    }   

    // Kernel version 4.13 added SO_PEERGROUPS to return the supplemental groups of a peer socket,
    // so try that first then fallback to the above racy checking of /proc/<pid>/status if the
    // kernel is too old.  Per
    // https://source.android.com/devices/architecture/kernel/android-common, the fallback can be
    // removed no earlier than 2024.
    auto supplemental_groups = std::vector<gid_t>(16, -1);
    socklen_t groups_size = supplemental_groups.size() * sizeof(gid_t);

    int result = getsockopt(cli->getSocket(), SOL_SOCKET, SO_PEERGROUPS, supplemental_groups.data(),
                            &groups_size);

    if (result != 0) {
        if (errno != ERANGE) {
            return clientHasLogCredentials(cli->getUid(), cli->getGid(), cli->getPid());
        }   

        supplemental_groups.resize(groups_size / sizeof(gid_t), -1);
        result = getsockopt(cli->getSocket(), SOL_SOCKET, SO_PEERGROUPS, supplemental_groups.data(),
                            &groups_size);

        // There is still some error after resizing supplemental_groups, fallback.
        if (result != 0) {
            return clientHasLogCredentials(cli->getUid(), cli->getGid(), cli->getPid());
        }   
    }   

    supplemental_groups.resize(groups_size / sizeof(gid_t), -1);
            
    for (const auto& gid : supplemental_groups) {
        if (UserIsPrivileged(gid)) {
            
            return true;
        }   
    }   
    return false;
}

如果uid或gid为ROOT, SYSTEM, LOG, 则返回true。

进程不满足以上条件,继续:查看/proc/pid/status,groups里有root组、system组、log组,则返回true。

这里我就不太明白,想要读取所有log就要设置权限“android.permission.READ_LOGS”,此进程要么是log进程,要么是属于log组的进程,此时方法返回true,但是判断是加了一个非,就无法读取所有log,虽然不设置为log组的进程返回false,但是没权限又无法读取,自相矛盾,不知道是不是谷歌不想让应用层读取系统log,有没有懂得老哥告知一下。

现在应用层无法获取,只能通过native进程,思路时native创建一个socketserver,应用层通过socket向server发送shell命令,由socketserver执行shell命令,这样利用native进程可以读取log信息,通过socket通信将结果返回到应用层。

native创建socketserver使用的是android_get_control_socket创建的,使用的是unix系统的socket,应用层通过LocalSocket连接server。

int main() {

    int socket_fd = -1; 
    int receive_fd = -1; 
    int result;

    struct sockaddr addr;
    socklen_t alen;
    alen = sizeof(addr);

    socket_fd = android_get_control_socket(logserver);
    if (socket_fd < 0) {
        LOGD(TAG,"Failed to get socket log_server  %s errno:%d\n", logserver, errno); 
        exit(-1);
    }   
    LOGD(TAG,"android_get_control_socket success\n");


    result = listen(socket_fd, MAX);
    if (result < 0) {
        perror("listen\n");
        LOGD(TAG,"listen error\n");
        exit(-1);
    }   
    LOGD(TAG,"listen success\n");
    while(1) {
        memset(&addr, 0, sizeof(addr));
        receive_fd= accept(socket_fd, &addr, &alen);
        LOGD(TAG,"Accept_fd %d\n",receive_fd);
        if (receive_fd < 0 ) { 
            LOGD(TAG,"receive_fd %d\n",errno);
            perror("accept error");
            exit(-1);
        }   

        fcntl(receive_fd, F_SETFD, FD_CLOEXEC);
        pthread_t id; 
        if(pthread_create(&id, NULL, recever_thread_exe, &receive_fd) )
        {   
            LOGD(TAG,"rece thread create fail\n");
        }   
    }   

    close(socket_fd);
    return 0;
}

代码地址:https://github.com/bug-machine321/native_socketServer

设置SELinux权限

首先新建te文件 detcserver.te

type detcserver, domain;
type detcserver_exec, system_file_type, exec_type, file_type;
typeattribute detcserver coredomain;
typeattribute detcserver mlstrustedsubject;

init_daemon_domain(detcserver)

allow detcserver shell_exec:file rx_file_perms;
allow detcserver logcat_exec:file { getattr open read execute execute_no_trans map};
allow detcserver logdr_socket:sock_file { getattr open read write};
allow detcserver logd:unix_stream_socket { connectto };

typeattribute detcserver coredomain和typeattribute detcserver mlstrustedsubject;  让detcserver越过MLS检查,越过neverallow检查。

file_contexts 添加

/dev/socket/detcserver(/.*)?     u:object_r:detcserver_socket:s0
/system/bin/detcserver           u:object_r:detcserver_exec:s0

file.te添加

type detcserver_socket, file_type, mlstrustedobject;

mlstrustedobject 包含了所有能越过MLS检查的客体type,给创建的socketserver越过neverallow检查

platform.te添加

unix_socket_connect(platform_app, detcserver, detcserver)

给app添加连接socketserver的权限

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值