Remove SELinux context manipulations during injection (#87)

After few tests in various devices, it seems that SELinux context modifications are unnecessary for the injection to work.

We thus remove all related manipulations. Further (partial) reverting of the commit must be justified with SELinux logs:

> adb shell su -c 'cat /proc/kmsg | grep avc'
This commit is contained in:
JingMatrix
2026-01-28 22:14:08 +01:00
parent e7d7b21daa
commit d2492df02e
2 changed files with 25 additions and 53 deletions
+25 -50
View File
@@ -97,10 +97,6 @@ constexpr size_t kMagicLength = 16;
constexpr size_t kMaxPathLength = PATH_MAX;
// Maximum length for file paths.
constexpr const char *kSystemFileContext = "u:object_r:system_file:s0";
// SELinux context for system files,
// used for socket creation and library file context.
constexpr const char *kLibcModule = "libc.so";
// Name of the C standard library.
@@ -217,8 +213,8 @@ private:
* @brief Transfers a file descriptor from the injector process to the remote process.
*
* This function uses Unix domain sockets with SCM_RIGHTS to send a file descriptor.
* It involves setting SELinux contexts, creating local and remote sockets, binding,
* and then coordinating sendmsg/recvmsg calls using ptrace.
* It involves creating local and remote sockets, binding, and then coordinating
* sendmsg/recvmsg calls using ptrace.
*
* @param pid The target process ID.
* @param lib_path The path to the library file being transferred.
@@ -235,29 +231,14 @@ static std::optional<int> transfer_fd_to_remote(int pid, const char *lib_path, s
uintptr_t libc_return_addr) {
LOGD("Attempting to transfer file descriptor for library: %s", lib_path);
// 1. Set SELinux context for socket creation in the injector process.
// This is crucial for Android where SELinux might prevent socket operations.
if (!set_sockcreate_con(constants::kSystemFileContext)) {
LOGE("Failed to set socket creation context.");
return std::nullopt;
}
// 2. Create a local Unix domain socket for FD transfer.
// Create a local Unix domain socket for FD transfer.
UniqueFd local_socket = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (local_socket == -1) {
PLOGE("Failed to create local Unix domain socket.");
return std::nullopt;
}
// 3. Set SELinux context for the library file if possible.
// This might be required for the target process to open/access it later if directly opening by path.
// For FD transfer, this is less critical as the FD's context is inherited, but good practice.
if (setfilecon(lib_path, constants::kSystemFileContext) == -1) {
// Log a warning, but don't fail, as FD transfer might still work.
PLOGE("Failed to set context of library file: %s. This might cause issues.", lib_path);
}
// 4. Open the local library file to get a file descriptor.
// Open the local library file to get a file descriptor.
UniqueFd local_lib_fd = open(lib_path, O_RDONLY | O_CLOEXEC);
if (local_lib_fd == -1) {
PLOGE("Failed to open library file: %s", lib_path);
@@ -273,7 +254,7 @@ static std::optional<int> transfer_fd_to_remote(int pid, const char *lib_path, s
void *errno_addr; // Address of __errno for getting remote errno.
} funcs{};
// 5. Resolve required libc functions in the remote process.
// Resolve required libc functions in the remote process.
funcs.socket_addr = find_func_addr(local_map, remote_map, constants::kLibcModule, "socket");
funcs.bind_addr = find_func_addr(local_map, remote_map, constants::kLibcModule, "bind");
funcs.recvmsg_addr = find_func_addr(local_map, remote_map, constants::kLibcModule, "recvmsg");
@@ -308,7 +289,7 @@ static std::optional<int> transfer_fd_to_remote(int pid, const char *lib_path, s
}
};
// 6. Create a Unix domain socket in the remote process.
// Create a Unix domain socket in the remote process.
std::vector<uintptr_t> args = {AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0};
int remote_fd = static_cast<int>(
remote_call(pid, regs, reinterpret_cast<uintptr_t>(funcs.socket_addr), libc_return_addr, args));
@@ -322,14 +303,14 @@ static std::optional<int> transfer_fd_to_remote(int pid, const char *lib_path, s
}
LOGD("Successfully created remote socket with FD: %d", remote_fd);
// 7. Generate a unique magic string for the abstract Unix domain socket path.
// Generate a unique magic string for the abstract Unix domain socket path.
auto magic = generateMagic(constants::kMagicLength);
struct sockaddr_un sock_addr{.sun_family = AF_UNIX, .sun_path = {0}};
// Abstract Unix domain sockets have sun_path[0] as null, and the name starts from sun_path[1].
memcpy(sock_addr.sun_path + 1, magic.c_str(), magic.size());
socklen_t addr_len = sizeof(sock_addr.sun_family) + 1 + magic.size(); // Length includes null byte and magic.
// 8. Push the sockaddr_un structure to the remote process's stack.
// Push the sockaddr_un structure to the remote process's stack.
auto remote_addr = push_memory(pid, regs, &sock_addr, sizeof(sock_addr));
if (remote_addr == 0) {
LOGE("Failed to push socket address to remote memory.");
@@ -337,7 +318,7 @@ static std::optional<int> transfer_fd_to_remote(int pid, const char *lib_path, s
return std::nullopt;
}
// 9. Bind the remote socket to the abstract Unix domain socket path.
// Bind the remote socket to the abstract Unix domain socket path.
args = {static_cast<uintptr_t>(remote_fd), remote_addr, static_cast<uintptr_t>(addr_len)};
auto bind_result = remote_call(pid, regs, reinterpret_cast<uintptr_t>(funcs.bind_addr), libc_return_addr, args);
if (bind_result == static_cast<uintptr_t>(-1)) {
@@ -351,7 +332,7 @@ static std::optional<int> transfer_fd_to_remote(int pid, const char *lib_path, s
// Prepare control message buffer for SCM_RIGHTS (file descriptor passing).
char cmsgbuf[CMSG_SPACE(sizeof(int))] = {0};
// 10. Push the control message buffer to the remote process's stack.
// Push the control message buffer to the remote process's stack.
auto remote_cmsgbuf = push_memory(pid, regs, &cmsgbuf, sizeof(cmsgbuf));
if (remote_cmsgbuf == 0) {
LOGE("Failed to push control message buffer to remote memory.");
@@ -364,7 +345,7 @@ static std::optional<int> transfer_fd_to_remote(int pid, const char *lib_path, s
msg_hdr.msg_control = reinterpret_cast<void *>(remote_cmsgbuf);
msg_hdr.msg_controllen = sizeof(cmsgbuf);
// 11. Push the msghdr structure to the remote process's stack.
// Push the msghdr structure to the remote process's stack.
auto remote_hdr = push_memory(pid, regs, &msg_hdr, sizeof(msg_hdr));
if (remote_hdr == 0) {
LOGE("Failed to push message header to remote memory.");
@@ -372,7 +353,7 @@ static std::optional<int> transfer_fd_to_remote(int pid, const char *lib_path, s
return std::nullopt;
}
// 12. Initiate the remote recvmsg call. This will block the remote process.
// Initiate the remote recvmsg call. This will block the remote process.
args = {static_cast<uintptr_t>(remote_fd), remote_hdr, MSG_WAITALL};
if (!remote_pre_call(pid, regs, reinterpret_cast<uintptr_t>(funcs.recvmsg_addr), libc_return_addr, args)) {
LOGE("Failed to initiate remote recvmsg call.");
@@ -381,7 +362,7 @@ static std::optional<int> transfer_fd_to_remote(int pid, const char *lib_path, s
}
LOGD("Remote recvmsg initiated, waiting for FD transfer...");
// 13. Prepare the local msghdr for sending the file descriptor.
// Prepare the local msghdr for sending the file descriptor.
// The msg_control and msg_name fields of the local msghdr are set up.
msg_hdr.msg_control = &cmsgbuf; // Use local cmsgbuf for sending.
msg_hdr.msg_name = &sock_addr;
@@ -401,7 +382,7 @@ static std::optional<int> transfer_fd_to_remote(int pid, const char *lib_path, s
*reinterpret_cast<int *>(CMSG_DATA(cmsg)) = local_lib_fd; // The FD to send.
}
// 14. Send the file descriptor from the injector to the remote process.
// Send the file descriptor from the injector to the remote process.
if (sendmsg(local_socket, &msg_hdr, 0) == -1) {
PLOGE("Failed to send file descriptor to remote process.");
// We do not close local_lib_fd here as it might be transferred even if
@@ -412,7 +393,7 @@ static std::optional<int> transfer_fd_to_remote(int pid, const char *lib_path, s
}
LOGD("Local FD %d sent to remote process.", local_lib_fd.operator const int &());
// 15. Complete the remote recvmsg call. This will retrieve the return value.
// Complete the remote recvmsg call. This will retrieve the return value.
auto recvmsg_result =
static_cast<ssize_t>(remote_post_call(pid, regs, libc_return_addr));
if (recvmsg_result == -1) {
@@ -423,7 +404,7 @@ static std::optional<int> transfer_fd_to_remote(int pid, const char *lib_path, s
}
LOGD("Remote recvmsg completed with result: %zd", recvmsg_result);
// 16. Read the control message buffer back from the remote process to extract the FD.
// Read the control message buffer back from the remote process to extract the FD.
if (read_proc(pid, remote_cmsgbuf, &cmsgbuf, sizeof(cmsgbuf)) != sizeof(cmsgbuf)) {
LOGE("Failed to read control message buffer from remote process.");
close_remote(remote_fd);
@@ -444,7 +425,7 @@ static std::optional<int> transfer_fd_to_remote(int pid, const char *lib_path, s
LOGI("Successfully transferred FD %d to remote process, new remote FD: %d", local_lib_fd.operator const int &(),
transferred_fd);
// 17. Close the remote socket.
// Close the remote socket.
close_remote(remote_fd);
return transferred_fd;
@@ -702,9 +683,8 @@ static bool copy_file(const char* src, const char* dst) {
*
* This strategy is used when direct FD passing fails (e.g., due to Seccomp filters).
* 1. Copies the library to a world-readable location (/data/local/tmp).
* 2. Sets permissions and SELinux context to mimic a system library.
* 3. Loads it via standard dlopen().
* 4. Immediately deletes the file to hide tracks.
* 2. Loads it via standard dlopen().
* 3. Immediately deletes the file to hide tracks.
*
* @param pid The target process ID.
* @param regs The target process registers (must be Red-Zone adjusted if x86_64).
@@ -720,7 +700,7 @@ static std::optional<uintptr_t> inject_via_staging(int pid, struct user_regs_str
const char *lib_path, uintptr_t libc_return_addr) {
LOGI("Initiating Staging Fallback mechanism...");
// 1. Generate a random path in /data/local/tmp
// Generate a random path in /data/local/tmp
// /data/local/tmp is chosen because it is traversable by most contexts.
std::string staged_path = "/data/local/tmp/lib" + generateMagic(8) + ".so";
@@ -730,39 +710,34 @@ static std::optional<uintptr_t> inject_via_staging(int pid, struct user_regs_str
LOGD("Staging library to: %s", staged_path.c_str());
// 2. Copy the library
// Copy the library
if (!copy_file(lib_path, staged_path.c_str())) {
LOGE("Failed to copy library during staging.");
return std::nullopt;
}
// 3. Set Permissions to 644 (RW-R--R--)
// Set Permissions to 644 (RW-R--R--)
// This allows the target process (likely running as a specific UID) to read the file.
if (chmod(staged_path.c_str(), 0644) != 0) {
PLOGE("Failed to chmod staged file.");
return std::nullopt;
}
// 4. Set SELinux Context
if (setfilecon(staged_path.c_str(), constants::kSystemFileContext) != 0) {
LOGW("Failed to set SELinux context on staged file. Injection might fail if target is enforcing.");
}
// 5. Resolve 'dlopen' in the remote process
// Resolve 'dlopen' in the remote process
auto dlopen_addr = find_func_addr(local_map, remote_map, constants::kLibdlModule, "dlopen");
if (!dlopen_addr) {
LOGE("Failed to find 'dlopen' in remote process.");
return std::nullopt;
}
// 6. Push the staged path to remote memory
// Push the staged path to remote memory
uintptr_t remote_path_addr = push_string(pid, regs, staged_path.c_str());
if (remote_path_addr == 0) {
LOGE("Failed to push staged path string to remote memory.");
return std::nullopt;
}
// 7. Call dlopen(path, RTLD_NOW)
// Call dlopen(path, RTLD_NOW)
std::vector<uintptr_t> args = {remote_path_addr, RTLD_NOW};
uintptr_t handle = remote_call(pid, regs, reinterpret_cast<uintptr_t>(dlopen_addr),
libc_return_addr, args);
-3
View File
@@ -1,5 +1,2 @@
allow keystore system_file unix_dgram_socket *
allow system_file keystore unix_dgram_socket *
allow keystore system_file file *
allow keystore shell_data_file dir *
allow crash_dump keystore process *