LSPosed/dex2oat/src/main/cpp/dex2oat.c

149 lines
4.3 KiB
C

/*
* 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 <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include "logging.h"
#if defined(__LP64__)
#define LP_SELECT(lp32, lp64) lp64
#else
#define LP_SELECT(lp32, lp64) lp32
#endif
#define ID_VEC(is64, is_debug) (((is64) << 1) | (is_debug))
const char kSockName[] = "5291374ceda0aef7c5d86cd2a4f6a3ac\0";
static ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags) {
int 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),
};
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);
if (msg.msg_controllen != bufsz || cmsg == NULL ||
cmsg->cmsg_len != CMSG_LEN(sizeof(int) * cnt) || cmsg->cmsg_level != SOL_SOCKET ||
cmsg->cmsg_type != SCM_RIGHTS) {
return NULL;
}
return CMSG_DATA(cmsg);
}
static 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;
int result;
memcpy(&result, data, sizeof(int));
return result;
}
static 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) {
if (fd < 0) return;
write(fd, &val, sizeof(val));
}
int main(int argc, char **argv) {
LOGD("dex2oat wrapper ppid=%d", getppid());
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)) {
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));
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)) {
PLOGE("failed to connect to %s", sock.sun_path + 1);
return 1;
}
write_int(sock_fd, LP_SELECT(4, 5));
int hooker_fd = recv_fd(sock_fd);
read_int(sock_fd);
close(sock_fd);
if (hooker_fd == -1) {
PLOGE("failed to read liboat_hook.so");
}
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;
if (getenv("LD_LIBRARY_PATH") == NULL) {
char const *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);
}
// 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);
fexecve(stock_fd, (char **)new_argv, environ);
PLOGE("fexecve failed");
return 2;
}