Skip to content

Commit

Permalink
Add native unix socket server.
Browse files Browse the repository at this point in the history
  • Loading branch information
wrlu committed Apr 26, 2024
1 parent c4b8c53 commit 9c0d2b2
Show file tree
Hide file tree
Showing 8 changed files with 421 additions and 78 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ android {
applicationId "com.wrlus.app.sandbox"
minSdk 32
targetSdk 32
versionCode 211
versionName "2.1.1"
versionCode 220
versionName "2.2.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
Expand Down
222 changes: 221 additions & 1 deletion app/src/main/cpp/sandbox.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
#include <jni.h>
#include <string>
#include <unistd.h>
#include <sys/system_properties.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <android/log.h>

#define TAG "sandbox_native"
#define MAX_HEADER_LEN 500
#define FILE_RW_BUF_SIZE 10240
#define BINDER_DATA_RECEIVE_BUF_LEN 1024 * 1024
#define PROPERTY_WATCHED_UID_DEX "sandbox.dex.watched.uid"
#define PROPERTY_WATCHED_UID_BINDER "sandbox.binder.watched.uid"

extern "C"
JNIEXPORT jstring JNICALL
Java_com_wrlus_app_sandbox_config_PropertyManager_get(JNIEnv *env, jclass clazz, jstring key) {
const char *c_key = env->GetStringUTFChars(key, 0);
char value[PROP_VALUE_MAX];
__system_property_get(c_key, value);
env->ReleaseStringUTFChars(key, c_key);
return env->NewStringUTF(value);
}

Expand All @@ -18,4 +31,211 @@ Java_com_wrlus_app_sandbox_config_PropertyManager_set(JNIEnv *env, jclass clazz,
const char *c_key = env->GetStringUTFChars(key, 0);
const char *c_value = env->GetStringUTFChars(value, 0);
__system_property_set(c_key, c_value);
}
env->ReleaseStringUTFChars(key, c_key);
env->ReleaseStringUTFChars(value, c_value);
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_wrlus_app_sandbox_service_BaseHookService_listenNative(JNIEnv *env, jclass clz,
jstring local_socket_name) {
const char *ls_name = env->GetStringUTFChars(local_socket_name, nullptr);
jsize ls_name_len = env->GetStringUTFLength(local_socket_name);

char* ls_name_with_prefix = static_cast<char *>(malloc(ls_name_len + 2));
snprintf(ls_name_with_prefix, ls_name_len + 2, "#%s", ls_name);

struct sockaddr_un server_address;
memset(&server_address, 0, sizeof(server_address));
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, ls_name_with_prefix);
server_address.sun_path[0] = 0;

unlink(ls_name_with_prefix);
free(ls_name_with_prefix);

int server_fd;
if((server_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0)) >= 0) {
if (bind(server_fd, (struct sockaddr*)&server_address,
offsetof(struct sockaddr_un, sun_path) + 1 + ls_name_len) == 0) {
if (listen(server_fd, 5) == 0) {
return server_fd;
}
}
close(server_fd);
}
return -1;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_wrlus_app_sandbox_service_BaseHookService_acceptNative(JNIEnv *env, jclass clz,
jint server_fd) {
if (server_fd < 0) return -1;

struct sockaddr_un client_address;
socklen_t client_address_len = sizeof(client_address);

int client_fd = accept(server_fd, (struct sockaddr*) &client_address,
&client_address_len);
return client_fd;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_wrlus_app_sandbox_service_BaseHookService_closeFdNative(JNIEnv *env, jclass clz,
jint fd) {
if (fd >= 0) close(fd);
}

int readLine(int fd, char *buf, int len) {
if (fd < 0 || buf == nullptr || len < 0) return -1;
char c[1];
int i = 0;
while (recv(fd, c, 1, MSG_WAITALL) > 0) {
if (c[0] != '\n' && i < len - 1) {
buf[i] = c[0];
i++;
} else {
buf[i] = '\0';
break;
}
}

return i;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_wrlus_app_sandbox_entity_BaseData_openStreamNative(JNIEnv *env, jclass clazz,
jint client_fd, jobject base_data) {
char buf[MAX_HEADER_LEN];
if (readLine(client_fd, buf, sizeof(buf)) > 0) {
int uid = atoi(buf);
jmethodID setUidMethod = env->GetMethodID(clazz, "setUid", "(I)V");
env->CallVoidMethod(base_data, setUidMethod, uid);
}
if (readLine(client_fd, buf, sizeof(buf)) > 0) {
int pid = atoi(buf);
jmethodID setPidMethod = env->GetMethodID(clazz, "setPid", "(I)V");
env->CallVoidMethod(base_data, setPidMethod, pid);
}
if (readLine(client_fd, buf, sizeof(buf)) > 0) {
long timestamp = atoi(buf);
jmethodID setTimestampMethod = env->GetMethodID(clazz, "setTimestamp", "(J)V");
env->CallVoidMethod(base_data, setTimestampMethod, static_cast<jlong>(timestamp));
}
}

bool checkWatchedUid(JNIEnv *env, jclass clazz, jobject base_data, const char *property_name) {
char watchedUidStr[PROP_VALUE_MAX];
__system_property_get(property_name, watchedUidStr);
int watchedUid = atoi(watchedUidStr);

jmethodID getUidMethod = env->GetMethodID(clazz, "getUid", "()I");
int uid = env->CallIntMethod(base_data, getUidMethod);
return watchedUid == uid;
}

jobject newDexFileData(JNIEnv *env) {
jclass cls = env->FindClass("com/wrlus/app/sandbox/entity/DexFileData");
jmethodID constructorID = env->GetMethodID(cls, "<init>", "()V");
return env->NewObject(cls, constructorID);
}

extern "C"
JNIEXPORT jobject JNICALL
Java_com_wrlus_app_sandbox_entity_DexFileData_openStreamNative(JNIEnv *env, jclass clazz,
jint client_fd,
jstring dex_save_file) {
if (client_fd < 0) return nullptr;

jobject dexFileData = newDexFileData(env);
if (dexFileData == nullptr) {
return nullptr;
}

Java_com_wrlus_app_sandbox_entity_BaseData_openStreamNative(env, clazz, client_fd,
dexFileData);
if (!checkWatchedUid(env, clazz, dexFileData,
PROPERTY_WATCHED_UID_DEX)) {
return nullptr;
}

jmethodID setDexSaveFileMethod = env->GetMethodID(clazz, "setDexSaveFile",
"(Ljava/lang/String;)V");
env->CallVoidMethod(dexFileData, setDexSaveFileMethod, dex_save_file);

char buf[MAX_HEADER_LEN];
if (readLine(client_fd, buf, sizeof(buf)) > 0) {
jmethodID setOriginDexPathMethod = env->GetMethodID(clazz, "setOriginDexPath",
"(Ljava/lang/String;)V");
jstring originDexPath = env->NewStringUTF(buf);
env->CallVoidMethod(dexFileData, setOriginDexPathMethod, originDexPath);
}

const char *c_dex_file_path = env->GetStringUTFChars(dex_save_file, 0);

FILE *dexFileFd = fopen(c_dex_file_path, "w");
if (dexFileFd == nullptr) {
return nullptr;
}

char fileBuf[FILE_RW_BUF_SIZE];
int recvLen = 0;
while ((recvLen = recv(client_fd, fileBuf,
FILE_RW_BUF_SIZE, MSG_WAITALL)) > 0) {
fwrite(fileBuf, sizeof(char), recvLen, dexFileFd);
}
fclose(dexFileFd);

return dexFileData;
}

jobject newBinderData(JNIEnv *env) {
jclass cls = env->FindClass("com/wrlus/app/sandbox/entity/BinderData");
jmethodID constructorID = env->GetMethodID(cls, "<init>", "()V");
return env->NewObject(cls, constructorID);
}

extern "C"
JNIEXPORT jobject JNICALL
Java_com_wrlus_app_sandbox_entity_BinderData_openStreamNative(JNIEnv *env, jclass clazz,
jint client_fd) {
if (client_fd < 0) return nullptr;

jobject binderData = newBinderData(env);
if (binderData == nullptr) {
return nullptr;
}

Java_com_wrlus_app_sandbox_entity_BaseData_openStreamNative(env, clazz, client_fd,
binderData);
if (!checkWatchedUid(env, clazz, binderData,
PROPERTY_WATCHED_UID_BINDER)) {
return nullptr;
}

char buf[MAX_HEADER_LEN];
if (readLine(client_fd, buf, sizeof(buf)) > 0) {
int code = atoi(buf);
jmethodID setCodeMethod = env->GetMethodID(clazz, "setCode",
"(I)V");
env->CallVoidMethod(binderData, setCodeMethod, code);
}
if (readLine(client_fd, buf, sizeof(buf)) > 0) {
int dataLen = atoi(buf);
if (dataLen > BINDER_DATA_RECEIVE_BUF_LEN) {
return nullptr;
}
void *buffer = malloc(dataLen);
int recvLen = recv(client_fd, buffer, dataLen, MSG_WAITALL);
if (recvLen != dataLen) {
__android_log_print(ANDROID_LOG_WARN, TAG,
"readSize != dataLen, excepted %d, found %d",
dataLen, recvLen);
}
return binderData;
}
return nullptr;
}
15 changes: 15 additions & 0 deletions app/src/main/java/com/wrlus/app/sandbox/entity/BaseData.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
import androidx.room.ColumnInfo;
import androidx.room.PrimaryKey;

import com.wrlus.app.sandbox.utils.StringUtils;

import java.io.IOException;
import java.io.InputStream;

public abstract class BaseData {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
Expand All @@ -15,6 +20,16 @@ public abstract class BaseData {
@ColumnInfo(name = "timestamp")
long timestamp;

static void openStream(InputStream is, BaseData baseData) throws IOException {
if (is == null) return;

baseData.uid = Integer.parseInt(StringUtils.readLine(is));
baseData.pid = Integer.parseInt(StringUtils.readLine(is));
baseData.timestamp = Long.parseLong(StringUtils.readLine(is));
}

public static native void openStreamNative(int client_fd, BaseData baseData);

public long getId() {
return id;
}
Expand Down
58 changes: 39 additions & 19 deletions app/src/main/java/com/wrlus/app/sandbox/entity/BinderData.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import androidx.room.ColumnInfo;
import androidx.room.Entity;

import com.wrlus.app.sandbox.config.PropertyManager;
import com.wrlus.app.sandbox.preference.Debug;
import com.wrlus.app.sandbox.utils.Constant;
import com.wrlus.app.sandbox.utils.StringUtils;
Expand All @@ -24,33 +25,52 @@ public class BinderData extends BaseData {
@ColumnInfo(name = "data")
byte[] data;

public static BinderData openStream(InputStream is) throws IOException {
if (is == null) {
static {
System.loadLibrary("sandbox");
}

public static BinderData openStream(InputStream is) {
if (is == null) return null;

BinderData binderData = new BinderData();
try {
BaseData.openStream(is, binderData);
} catch (IOException e) {
Debug.e(TAG, e);
return null;
}
BinderData binderData = new BinderData();

binderData.uid = Integer.parseInt(StringUtils.readLine(is));
binderData.pid = Integer.parseInt(StringUtils.readLine(is));
binderData.code = Integer.parseInt(StringUtils.readLine(is));
binderData.timestamp = Long.parseLong(StringUtils.readLine(is));
int watchedUid = PropertyManager.getWatchedUid(Constant.FEATURE_BINDER);
if (watchedUid != binderData.uid) {
Debug.w(TAG, "Skip not watched uid = " + binderData.uid);
return null;
}

int dataLen = Integer.parseInt(StringUtils.readLine(is));
if (dataLen > Constant.BINDER_DATA_RECEIVE_BUF_LEN) {
Debug.e(TAG, "dataLen > BINDER_DATA_RECV_BUF_LEN, excepted " + dataLen +
", uid " + binderData.uid + ", pid " + binderData.pid);
try {
binderData.code = Integer.parseInt(StringUtils.readLine(is));

int dataLen = Integer.parseInt(StringUtils.readLine(is));
if (dataLen > Constant.BINDER_DATA_RECEIVE_BUF_LEN) {
Debug.e(TAG, "dataLen > BINDER_DATA_RECV_BUF_LEN, excepted " + dataLen +
", uid " + binderData.uid + ", pid " + binderData.pid);
return null;
}
binderData.data = new byte[dataLen];
int readSize = is.read(binderData.data);
if (readSize != dataLen) {
Debug.w(TAG, "readSize != dataLen, excepted "+ dataLen +
", found " + readSize + ", uid " + binderData.uid + ", pid " + binderData.pid);
}
Debug.d(TAG, "Received " + binderData);
return binderData;
} catch (IOException e) {
Debug.e(TAG, e);
return null;
}
binderData.data = new byte[dataLen];
int readSize = is.read(binderData.data);
if (readSize != dataLen) {
Debug.w(TAG, "readSize != dataLen, excepted "+ dataLen +
", found " + readSize + ", uid " + binderData.uid + ", pid " + binderData.pid);
}
Debug.d(TAG, "Received " + binderData);
return binderData;
}

public static native BinderData openStreamNative(int clientFd);

public String getInterfaceToken() {
return interfaceToken;
}
Expand Down
Loading

0 comments on commit 9c0d2b2

Please sign in to comment.