由于项目要求,需要在应用程序中执行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的权限