Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 53 additions & 61 deletions dex2oat/src/main/cpp/dex2oat.cpp
Original file line number Diff line number Diff line change
@@ -1,29 +1,9 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2022 LSPosed Contributors
*/

//
// Created by Nullptr on 2022/4/1.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>

// POSIX/Linux headers
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
Expand All @@ -36,79 +16,84 @@
#define LP_SELECT(lp32, lp64) lp32
#endif

#define ID_VEC(is64, is_debug) (((is64) << 1) | (is_debug))
constexpr int id_vec(bool is64, bool is_debug) { return ((is64 << 1) | is_debug); }

// An anonymous namespace is used to limit the scope of helper functions and constants
// to this file, which is the C++ equivalent of the `static` keyword for free functions.
namespace {

// The abstract socket name for communication.
const char kSockName[] = "5291374ceda0aef7c5d86cd2a4f6a3ac\0";

static ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags) {
int rec = recvmsg(sockfd, msg, flags);
ssize_t xrecvmsg(int sockfd, struct msghdr* msg, int flags) {
ssize_t rec = recvmsg(sockfd, msg, flags);
if (rec < 0) {
PLOGE("recvmsg");
}
return rec;
}

static void *recv_fds(int sockfd, char *cmsgbuf, size_t bufsz, int cnt) {
struct iovec iov = {
.iov_base = &cnt,
.iov_len = sizeof(cnt),
};
void* recv_fds(int sockfd, char* cmsgbuf, size_t bufsz, int cnt) {
struct iovec iov = {.iov_base = &cnt, .iov_len = sizeof(cnt)};
struct msghdr msg = {
.msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsgbuf, .msg_controllen = bufsz};

xrecvmsg(sockfd, &msg, MSG_WAITALL);
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);

if (msg.msg_controllen != bufsz || cmsg == NULL ||
if (msg.msg_controllen != bufsz || cmsg == nullptr ||
cmsg->cmsg_len != CMSG_LEN(sizeof(int) * cnt) || cmsg->cmsg_level != SOL_SOCKET ||
cmsg->cmsg_type != SCM_RIGHTS) {
return NULL;
return nullptr;
}

return CMSG_DATA(cmsg);
}

static int recv_fd(int sockfd) {
int recv_fd(int sockfd) {
char cmsgbuf[CMSG_SPACE(sizeof(int))];

void *data = recv_fds(sockfd, cmsgbuf, sizeof(cmsgbuf), 1);
if (data == NULL) return -1;
void* data = recv_fds(sockfd, cmsgbuf, sizeof(cmsgbuf), 1);
if (data == nullptr) return -1;

int result;
memcpy(&result, data, sizeof(int));
return result;
}

static int read_int(int fd) {
int read_int(int fd) {
int val;
if (read(fd, &val, sizeof(val)) != sizeof(val)) return -1;
return val;
}

static void write_int(int fd, int val) {
void write_int(int fd, int val) {
if (fd < 0) return;
write(fd, &val, sizeof(val));
}

int main(int argc, char **argv) {
} // namespace

int main(int argc, char** argv) {
LOGD("dex2oat wrapper ppid=%d", getppid());
struct sockaddr_un sock = {};

struct sockaddr_un sock{};
sock.sun_family = AF_UNIX;
strlcpy(sock.sun_path + 1, kSockName, sizeof(sock.sun_path) - 1);

int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
size_t len = sizeof(sa_family_t) + strlen(sock.sun_path + 1) + 1;
if (connect(sock_fd, (struct sockaddr *)&sock, len)) {
if (connect(sock_fd, reinterpret_cast<struct sockaddr*>(&sock), len)) {
PLOGE("failed to connect to %s", sock.sun_path + 1);
return 1;
}
write_int(sock_fd, ID_VEC(LP_SELECT(0, 1), strstr(argv[0], "dex2oatd") != NULL));
write_int(sock_fd, id_vec(LP_SELECT(0, 1), strstr(argv[0], "dex2oatd") != nullptr));
int stock_fd = recv_fd(sock_fd);
read_int(sock_fd);
close(sock_fd);

sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (connect(sock_fd, (struct sockaddr *)&sock, len)) {
if (connect(sock_fd, reinterpret_cast<struct sockaddr*>(&sock), len)) {
PLOGE("failed to connect to %s", sock.sun_path + 1);
return 1;
}
Expand All @@ -122,27 +107,34 @@ int main(int argc, char **argv) {
}
LOGD("sock: %s %d", sock.sun_path + 1, stock_fd);

const char *new_argv[argc + 2];
for (int i = 0; i < argc; i++) new_argv[i] = argv[i];
new_argv[argc] = "--inline-max-code-units=0";
new_argv[argc + 1] = NULL;
// Use std::vector for safer and more flexible argument list management.
std::vector<const char*> new_argv;
for (int i = 0; i < argc; ++i) {
new_argv.push_back(argv[i]);
}
new_argv.push_back("--inline-max-code-units=0");
new_argv.push_back(nullptr); // execve requires a null-terminated array.

if (getenv("LD_LIBRARY_PATH") == NULL) {
char const *libenv = LP_SELECT(
if (getenv("LD_LIBRARY_PATH") == nullptr) {
const char* libenv = LP_SELECT(
"LD_LIBRARY_PATH=/apex/com.android.art/lib:/apex/com.android.os.statsd/lib",
"LD_LIBRARY_PATH=/apex/com.android.art/lib64:/apex/com.android.os.statsd/lib64");
putenv((char *)libenv);
// The putenv function is a legacy C API that requires a char*.
// A const_cast is necessary here.
putenv(const_cast<char*>(libenv));
}

// Set LD_PRELOAD to load liboat_hook.so
const int STRING_BUFFER = 50;
char env_str[STRING_BUFFER];
snprintf(env_str, STRING_BUFFER, "LD_PRELOAD=/proc/%d/fd/%d", getpid(), hooker_fd);
putenv(env_str);
LOGD("Set env %s", env_str);
// Use std::string for safe and easy string construction.
std::string ld_preload_env =
"LD_PRELOAD=/proc/" + std::to_string(getpid()) + "/fd/" + std::to_string(hooker_fd);
// putenv(const_cast<char*>(ld_preload_env.c_str()));
LOGD("[dry_run] Set env %s", ld_preload_env.c_str());

fexecve(stock_fd, (char **)new_argv, environ);
// fexecve requires a non-const char* array. We must use const_cast to match the
// required signature, as is common when interfacing modern C++ with older C APIs.
fexecve(stock_fd, const_cast<char* const*>(new_argv.data()), environ);

// This part is only reached if fexecve fails.
PLOGE("fexecve failed");
return 2;
}