由于小伙伴反映用我的隐私检测插件做检测时,看log要看到头疼,我决定做一个带UI的工具,对隐私检测插件的数据进行接受。最后花了几天时间做好了,隐私插件中也进行了适当修改,数据不再以log形式输出,而是创建一个服务端,由PC端的客户端来主动获取,以下是效果图
现在又有个问题,隐私检测除了用插件来检测,某些场景下,插件用不了,需要用隐私检测rom来做检测。隐私检测rom里面的数据也是以log形式输出的,如果要全部改为网络收发,那要改的点有点多,毕竟这么多桩点。后来想想,决定从logd入手,在logd中做个劫持,筛选,转发给系统服务,然后再写个app从系统服务中获取msg,PC端的客户端与app中的服务端通信,获取数据,下面就讲一下实现过程:
一、修改logd
在 /system/logging/logd/LogListener.cpp 文件中
在HandleData函数中插入下面的函数
void sendLog(uid_t uid, const char* msg, ssize_t size){
if (uid < 10100){
return;
}
if (strstr(msg, "Antiy_log") == NULL){
return;
}
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock == -1) {
return;
}
char name[19] = "@/dev/socket/yooha";
struct sockaddr_un local;
memset(&local, 0, sizeof(local));
strncpy(local.sun_path, name, sizeof(local.sun_path) - 1);
local.sun_path[0] = 0;
local.sun_family = AF_UNIX;
int len = strlen(name) + offsetof(struct sockaddr_un, sun_path);
if (connect(sock, (struct sockaddr *) &local, len) == -1){
close(sock);
return;
}
write(sock, (msg + strlen(msg) + 1), (size - strlen(msg) - 1));
close(sock);
}
创建 /dev/socket/yooha ,/system/logging/logd/logd.rc
service logd /system/bin/logd
socket logd stream 0666 logd logd
socket logdr seqpacket 0666 logd logd
socket logdw dgram+passcred 0222 logd logd
socket yooha stream 0666 system system # add here
file /proc/kmsg r
file /dev/kmsg w
user logd
group logd system package_info readproc
capabilities SYSLOG AUDIT_CONTROL
priority 10
writepid /dev/cpuset/system-background/tasks
二、创建一个系统服务收发MSG
新建 /frameworks/base/core/java/android/app/yooha/ILogSocket.aidl
package android.app.yooha;
import android.app.yooha.ILogListener;
/**
*@hide
*/
interface ILogSocket{
void start();
void addLogListener(ILogListener lis);
void removeLogListener(ILogListener lis);
}
新建 /frameworks/base/core/java/android/app/yooha/LogListener.java
package android.app.yooha;
public interface LogListener {
void onLogChange(String log);
}
新建 /frameworks/base/core/java/android/app/yooha/LogListenerManager.java
package android.app.yooha;
import android.annotation.NonNull;
import android.app.yooha.ILogListener;
import android.app.yooha.LogListener;
import java.util.concurrent.Executor;
import java.lang.Runnable;
/**
*@hide
*/
public class LogListenerManager extends ILogListener.Stub {
private LogListener listener;
private Executor executor;
public LogListenerManager(@NonNull Executor ect, @NonNull LogListener lstn){
listener = lstn;
executor = ect;
}
public void onLogChange(@NonNull String log){
executor.execute(new Runnable(){
@Override
public void run(){
if (listener != null){
listener.onLogChange(log);
}
}
});
}
}
新建 /frameworks/base/core/java/android/app/yooha/ILogListener.aidl
package android.app.yooha;
/**
*@hide
*/
interface ILogListener{
void onLogChange(String log);
}
新建 /frameworks/base/services/core/java/com/android/server/LogSocketService.java
package com.android.server;
import android.app.yooha.ILogSocket;
import android.app.yooha.ILogListener;
import android.content.Context;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import java.lang.JLog;
import java.io.IOException;
import java.io.DataInputStream;
import org.json.JSONObject;
import java.util.ArrayList;
import android.app.yooha.DispatchLogThread;
/**
*@hide
*/
public class LogSocketService extends ILogSocket.Stub{
private Context mContext;
private LocalServerSocket mServer = null;
private boolean bStart = false;
private ArrayList lList = new ArrayList();
private LocalSocket recv = null;
private DataInputStream inFromClient = null;
public Context getContext(){
return mContext;
}
public LogSocketService(Context context) {
mContext = context;
try{
mServer = new LocalServerSocket("/dev/socket/yooha");
}catch (Exception e) {
JLog.showException(e);
}
}
public void start(){
if (bStart){
return;
}
bStart = true;
new Thread() {
public void run() {
JLog.debug("LogSocketService running");
if (mServer != null){
while (true){
try {
try{
recv = mServer.accept();
}catch (Exception e) {
JLog.showException(e);
continue;
}
try{
recv.setReceiveBufferSize(0x1500);
inFromClient = new DataInputStream(recv.getInputStream());
}catch (Exception e) {
JLog.showException(e);
continue;
}
try{
byte[] buffer = new byte[0x1500];
int read = inFromClient.read(buffer);
if (read != -1){
new DispatchLogThread(lList, buffer, read).start();
}
}catch (Exception e) {
JLog.showException(e);
if (inFromClient != null){
inFromClient.close();
}
if (recv != null){
recv.close();
}
}
} catch (Exception e) {
if (mServer != null){
try {
mServer.close();
} catch (IOException ie) {
JLog.showException(ie);
}
}
JLog.showException(e);
}
}
}
}
}.start();
}
public void addLogListener(ILogListener lis){
if(!lList.contains(lis)){
lList.add(lis);
}
}
public void removeLogListener(ILogListener lis){
if(lList.contains(lis)){
lList.remove(lis);
}
}
}
修改 /frameworks/base/Android.bp (忽略lint检查)
metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
"--hide-package com.android.server " +
"--hide-package android.audio.policy.configuration.V7_0 " +
"--error UnhiddenSystemApi " +
"--hide RequiresPermission " +
"--hide CallbackInterface " +
"--hide MissingPermission --hide BroadcastBehavior " +
"--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
"--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " +
"--error NoSettingsProvider " +
"--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " +
"--api-lint-ignore-prefix android.icu. " +
"--api-lint-ignore-prefix java. " +
"--api-lint-ignore-prefix junit. " +
"--api-lint-ignore-prefix android.app.yooha. " + // add start
"--api-lint-ignore-prefix org. "
新建 /frameworks/base/core/java/android/app/yooha/DispatchLogThread.java
package android.app.yooha;
import android.app.yooha.ILogListener;
import android.net.LocalSocket;
import org.json.JSONObject;
import java.io.DataInputStream;
import java.util.Arrays;
import java.lang.JLog;
import java.util.ArrayList;
import java.util.List;
import android.annotation.NonNull;
public class DispatchLogThread extends Thread{
private ArrayList lists = null;
private String log = null;
public DispatchLogThread(@NonNull ArrayList lists, @NonNull byte[] buffer, int size){
try{
this.lists = lists;
this.log = new String(buffer, 0, size - 1);
} catch (Exception e) {
JLog.showException(e);
}
}
public void run() {
try {
for(ILogListener l:lists){
try{
if (l != null){
l.onLogChange(log);
}
} catch (Exception e) {
}
}
} catch (Exception e) {
JLog.showException(e);
}
}
@NonNull
public String byteToString(@NonNull byte[] data, int size){
StringBuffer sb = new StringBuffer();
String tmp = null;
for (int i = 0; i < size; i++){
tmp = Integer.toHexString(0xFF & data[i]);
if (tmp.length() == 1){
tmp = "0" + tmp;
}
sb.append(tmp + " ");
}
return sb.toString();
}
@NonNull
public String byteToString(@NonNull byte[] data){
int index = data.length;
for (int i = 0; i < data.length; i++){
if (data[i] == 0){
index = i;
break;
}
}
return new String(data, 0, index);
}
}
新建 /frameworks/base/core/java/android/app/yooha/LogSocketManager.java
package android.app.yooha;
import android.content.Context;
import android.app.yooha.ILogSocket;
import android.app.yooha.ILogListener;
import android.app.yooha.LogListenerManager;
import java.lang.JLog;
import android.annotation.NonNull;
import java.util.HashMap;
import java.util.concurrent.Executor;
import android.annotation.SystemService;
import android.annotation.CallbackExecutor;
@SystemService(Context.LOG_SOCKET_SERVICE)
public class LogSocketManager {
private final ILogSocket mLogSocket;
private Context mContext;
private final HashMap mListeners = new HashMap();
/**
*@hide
*/
public LogSocketManager(@NonNull Context c, @NonNull ILogSocket f){
mLogSocket = f;
mContext = c;
}
public void start(){
try{
mLogSocket.start();
} catch (Exception ie) {
JLog.showException(ie);
}
}
/**
*@hide
*/
public LogListenerManager getLogListenerManager(@NonNull @CallbackExecutor Executor executor, @NonNull LogListener lis){
return new LogListenerManager(executor, lis);
}
public void addLogListener(@NonNull @CallbackExecutor Executor executor, @NonNull LogListener lis){
try{
if (lis == null){
return;
}
LogListenerManager cb = mListeners.get(lis);
if (cb == null){
LogListenerManager listener = getLogListenerManager(executor, lis);
mLogSocket.addLogListener(listener);
mListeners.put(lis, listener);
}
}catch (Exception ie) {
JLog.showException(ie);
}
}
public void removeLogListener(@NonNull LogListener lis){
try{
if (lis == null){
return;
}
LogListenerManager cb = mListeners.get(lis);
if (cb != null){
mLogSocket.removeLogListener(cb);
mListeners.remove(lis);
}
}catch (Exception ie) {
JLog.showException(ie);
}
}
}
修改 /frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
......
// add start
t.traceBegin("Start LogSocketService");
LogSocketService logsocketservice= new LogSocketService(context);
//logsocketservice.start();
ServiceManager.addService("log_socket", logsocketservice);
t.traceEnd();
// add end
......
}
修改 /frameworks/base/core/java/android/app/SystemServiceRegistry.java
static {
......
// add start
registerService(Context.LOG_SOCKET_SERVICE, LogSocketManager.class,
new CachedServiceFetcher() {
@Override
public LogSocketManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder b = ServiceManager.getService(Context.LOG_SOCKET_SERVICE);
return b == null ? null : new LogSocketManager(ctx, ILogSocket.Stub.asInterface(b));
}});
// add end
}
修改 /frameworks/base/core/java/android/content/Context.java
public static final String LOG_SOCKET_SERVICE = "log_socket"; // add start
三、修改SELINUX
以下文件添加:log_socket u:object_r:log_socket_service:s0
system\sepolicy\private\service_contexts
system\sepolicy\prebuilts\api\26\private\service_contexts
system\sepolicy\prebuilts\api\27\private\service_contexts
system\sepolicy\prebuilts\api\28\private\service_contexts
system\sepolicy\prebuilts\api\29\private\service_contexts
system\sepolicy\prebuilts\api\30\private\service_contexts
system\sepolicy\prebuilts\api\31\private\service_contexts
以下文件添加:type log_socket_service, app_api_service, system_api_service, system_server_service, service_manager_type;
system\sepolicy\public\service.te
system\sepolicy\prebuilts\api\26\public\service.te
system\sepolicy\prebuilts\api\27\public\service.te
system\sepolicy\prebuilts\api\28\public\service.te
system\sepolicy\prebuilts\api\29\public\service.te
system\sepolicy\prebuilts\api\30\public\service.te
system\sepolicy\prebuilts\api\31\public\service.te
以下文件添加: /dev/socket/ayooha u:object_r:log_socket:s0
/system/sepolicy/private/file_contexts
system/sepolicy/prebuilts/api/26/private/file_contexts
system/sepolicy/prebuilts/api/27/private/file_contexts
system/sepolicy/prebuilts/api/28/private/file_contexts
system/sepolicy/prebuilts/api/29/private/file_contexts
system/sepolicy/prebuilts/api/30/private/file_contexts
system/sepolicy/prebuilts/api/31/private/file_contexts
以下文件添加: type log_socket, file_type, coredomain_socket, mlstrustedobject;
/system/sepolicy/public/file.te
system/sepolicy/prebuilts/api/26/public/file.te
system/sepolicy/prebuilts/api/27/public/file.te
system/sepolicy/prebuilts/api/28/public/file.te
system/sepolicy/prebuilts/api/29/public/file.te
system/sepolicy/prebuilts/api/30/public/file.te
system/sepolicy/prebuilts/api/31/public/file.te
以下文件参照logw来添加内容:
/system/sepolicy/private/compat/26.0/26.0.cil
/system/sepolicy/private/compat/27.0/27.0.cil
/system/sepolicy/private/compat/28.0/28.0.cil
/system/sepolicy/private/compat/29.0/29.0.cil
/system/sepolicy/private/compat/30.0/30.0.cil
/system/sepolicy/private/compat/31.0/31.0.cil
system/sepolicy/prebuilts/api/31.0/private/compat/26.0/26.0.cil
system/sepolicy/prebuilts/api/31.0/private/compat/27.0/27.0.cil
system/sepolicy/prebuilts/api/31.0/private/compat/28.0/28.0.cil
system/sepolicy/prebuilts/api/31.0/private/compat/29.0/29.0.cil
system/sepolicy/prebuilts/api/31.0/private/compat/30.0/30.0.cil
以下文件参照logw来添加内容:
/system/sepolicy/prebuilts/api/26.0/nonplat_sepolicy.cil
/system/sepolicy/prebuilts/api/27.0/nonplat_sepolicy.cil
/system/sepolicy/prebuilts/api/28.0/plat_pub_versioned.cil
/system/sepolicy/prebuilts/api/28.0/vender_sepolicy.cil
/system/sepolicy/prebuilts/api/29.0/plat_pub_versioned.cil
/system/sepolicy/prebuilts/api/30.0/plat_pub_versioned.cil
文件/system/sepolicy/prebuilts/api/31.0/public/untrusted_app.te 添加如下内容:
allow untrusted_app log_socket_service:service_manager find;
allow untrusted_app_29 log_socket_service:service_manager find;
allow untrusted_app_27 log_socket_service:service_manager find;
allow untrusted_app_25 log_socket_service:service_manager find;
allow untrusted_app system_server:unix_stream_socket connectto;
allow untrusted_app_29 system_server:unix_stream_socket connectto;
allow untrusted_app_27 system_server:unix_stream_socket connectto;
allow untrusted_app_25 system_server:unix_stream_socket connectto;
文件 /system/sepolicy/public/untrusted_app.te 添加如下内容:
allow untrusted_app log_socket_service:service_manager find;
allow untrusted_app_29 log_socket_service:service_manager find;
allow untrusted_app_27 log_socket_service:service_manager find;
allow untrusted_app_25 log_socket_service:service_manager find;
allow untrusted_app system_server:unix_stream_socket connectto;
allow untrusted_app_29 system_server:unix_stream_socket connectto;
allow untrusted_app_27 system_server:unix_stream_socket connectto;
allow untrusted_app_25 system_server:unix_stream_socket connectto;
以下文件注释掉:neverallow { all_untrusted_apps -mediaprovider } init:unix_stream_socket connectto;
/system/sepolicy/private/app_neverallows.te
/system/sepolicy/prebuilts/api/31.0/private/app_neverallows.te
以下文件添加:allow logd system_server:unix_stream_socket connectto;
./public/logd.te
./prebuilts/api/30.0/public/logd.te
./prebuilts/api/27.0/public/logd.te
./prebuilts/api/29.0/public/logd.te
./prebuilts/api/31.0/public/logd.te
./prebuilts/api/26.0/public/logd.te
./prebuilts/api/28.0/public/logd.te
四、写一个app从系统服务获取msg
注册日志监听器
public void register(String uid){
try{
Object service = this.getSystemService("log_socket");
Class clsLogSocketManager = Class.forName("android.app.yooha.LogSocketManager");
Class clsLogListener = Class.forName("android.app.yooha.LogListener");
Method start = clsLogSocketManager.getDeclaredMethod("start");
Method addLogListener = clsLogSocketManager.getDeclaredMethod("addLogListener", Executor.class, clsLogListener);
start.setAccessible(true);
start.invoke(service);
addLogListener.setAccessible(true);
Log.e("testlog", "register:1");
MyLogListener obj = MyLogListener.getInstance();
Log.e("testlog", String.valueOf(obj));
obj.setUid(uid);
Log.e("testlog", "register:2");
addLogListener.invoke(service, Executors.newSingleThreadExecutor(), MyLogListener.getInstance());
} catch (Throwable th){
Log.e("testlog", "register:" + th.toString());
}
}
接受日志,先解包,再按协议重新组包
@Override
public void onLogChange(String log) {
try{
JSONObject json = new JSONObject(log);
String type = json.getString("Type");
if (type.equals("Antiy-Privacy")){
String Uid = json.getString("Uid");
if (Uid.equals(this.uid)){
String info = json.getString("Info");
String[] infos = info.split("->");
String clz = infos[0].replace(" ", "");
String method = infos[1].substring(0, infos[1].indexOf(":"));
String description = infos[1].substring(infos[1].indexOf(":") + 1);
String stack = json.getString("stackTrace");
String currentTime = sdf.format(new Date());
send(currentTime, clz, method, "", description, "", "", stack);
}
}
} catch (Throwable th){
Log.e("testlog", th.toString());
Log.e("testlog", log);
}
}
public void send(String time, String clz, String method, String total, String descrip, String param, String ret, String stack){
try{
JSONObject json = new JSONObject();
json.put("Time", time);
json.put("Class", clz);
json.put("Method", method);
json.put("Total", total);
json.put("Description", descrip);
json.put("Params", param);
json.put("Return", ret);
json.put("StackTrace", stack);
NanoServer.recvMsg(json.toString());
}catch(Throwable th){
Log.e("testlog", th.toString());
}
}
由服务端来发送msg
public class NanoServer extends EdNanoHTTPD {
public static final LinkedList mMsg = new LinkedList(); //消息队列
public NanoServer() throws IOException {
super(Data.PORT);
start();
}
public static void recvMsg(String msg){
synchronized (mMsg){
mMsg.add(msg);
}
}
@Override
public Response serve(IHTTPSession session) {
String msg = "";
if (Method.GET == session.getMethod()) {
String uri = session.getUri();
if (uri.contains("api/getlog")){
synchronized(mMsg) {
if (!mMsg.isEmpty()){
msg = (String)mMsg.poll();
}
}
}else if (uri.contains("api/connect")){
msg = "connect";
}
}
try {
return newFixedLengthResponse(msg);
} catch (Exception exception) {
return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Internal Server Error!!!");
}
}
}
做一个界面,让用户选择应用,然后传递UID到listener,只筛选执行UID的log
最后测试一下效果: