mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-11-27 20:15:29 +00:00
Verify app signature
This commit is contained in:
parent
4cff0384f7
commit
05d6d2b51b
@ -41,6 +41,7 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
const val DENYLIST = "denylist"
|
const val DENYLIST = "denylist"
|
||||||
const val SU_MANAGER = "requester"
|
const val SU_MANAGER = "requester"
|
||||||
const val KEYSTORE = "keystore"
|
const val KEYSTORE = "keystore"
|
||||||
|
const val CERTDIGEST = "cert_digest"
|
||||||
|
|
||||||
// prefs
|
// prefs
|
||||||
const val SU_REQUEST_TIMEOUT = "su_request_timeout"
|
const val SU_REQUEST_TIMEOUT = "su_request_timeout"
|
||||||
@ -152,6 +153,7 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
var denyList by DBBoolSettingsNoWrite(Key.DENYLIST, false)
|
var denyList by DBBoolSettingsNoWrite(Key.DENYLIST, false)
|
||||||
var suManager by dbStrings(Key.SU_MANAGER, "", true)
|
var suManager by dbStrings(Key.SU_MANAGER, "", true)
|
||||||
var keyStoreRaw by dbStrings(Key.KEYSTORE, "", true)
|
var keyStoreRaw by dbStrings(Key.KEYSTORE, "", true)
|
||||||
|
var certDigest by dbStrings(Key.CERTDIGEST, "", true)
|
||||||
|
|
||||||
private const val SU_FINGERPRINT = "su_fingerprint"
|
private const val SU_FINGERPRINT = "su_fingerprint"
|
||||||
|
|
||||||
|
@ -121,6 +121,7 @@ object HideAPK {
|
|||||||
val repack = File(activity.cacheDir, "patched.apk")
|
val repack = File(activity.cacheDir, "patched.apk")
|
||||||
val pkg = genPackageName()
|
val pkg = genPackageName()
|
||||||
Config.keyStoreRaw = ""
|
Config.keyStoreRaw = ""
|
||||||
|
Config.certDigest = ""
|
||||||
|
|
||||||
if (!patch(activity, stub, FileOutputStream(repack), pkg, label))
|
if (!patch(activity, stub, FileOutputStream(repack), pkg, label))
|
||||||
return false
|
return false
|
||||||
|
@ -12,6 +12,7 @@ import java.io.ByteArrayOutputStream
|
|||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.security.KeyPairGenerator
|
import java.security.KeyPairGenerator
|
||||||
import java.security.KeyStore
|
import java.security.KeyStore
|
||||||
|
import java.security.MessageDigest
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -73,6 +74,9 @@ class Keygen : CertKeyProvider {
|
|||||||
}
|
}
|
||||||
Config.keyStoreRaw = bytes.toString("UTF-8")
|
Config.keyStoreRaw = bytes.toString("UTF-8")
|
||||||
|
|
||||||
|
val digest = MessageDigest.getInstance("SHA-256").digest(cert.encoded)
|
||||||
|
Config.certDigest = digest.joinToString("") { "%02x".format(it) }
|
||||||
|
|
||||||
return ks
|
return ks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,8 @@ LOCAL_STATIC_LIBRARIES := \
|
|||||||
libnanopb \
|
libnanopb \
|
||||||
libsystemproperties \
|
libsystemproperties \
|
||||||
libphmap \
|
libphmap \
|
||||||
libxhook
|
libxhook \
|
||||||
|
libmincrypt
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
core/applets.cpp \
|
core/applets.cpp \
|
||||||
|
@ -20,6 +20,7 @@ using namespace std;
|
|||||||
static bool safe_mode = false;
|
static bool safe_mode = false;
|
||||||
static int stub_fd = -1;
|
static int stub_fd = -1;
|
||||||
bool zygisk_enabled = false;
|
bool zygisk_enabled = false;
|
||||||
|
string APKCERT;
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
* Setup *
|
* Setup *
|
||||||
@ -124,6 +125,7 @@ static bool magisk_env() {
|
|||||||
LOGI("* Initializing Magisk environment\n");
|
LOGI("* Initializing Magisk environment\n");
|
||||||
|
|
||||||
string stub_path = MAGISKTMP + "/stub.apk";
|
string stub_path = MAGISKTMP + "/stub.apk";
|
||||||
|
APKCERT = read_certificate(stub_path);
|
||||||
stub_fd = xopen(stub_path.data(), O_RDONLY | O_CLOEXEC);
|
stub_fd = xopen(stub_path.data(), O_RDONLY | O_CLOEXEC);
|
||||||
unlink(stub_path.data());
|
unlink(stub_path.data());
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <db.hpp>
|
#include <db.hpp>
|
||||||
#include <socket.hpp>
|
#include <socket.hpp>
|
||||||
#include <utils.hpp>
|
#include <utils.hpp>
|
||||||
|
#include <mincrypt/sha256.h>
|
||||||
|
|
||||||
#define DB_VERSION 12
|
#define DB_VERSION 12
|
||||||
|
|
||||||
@ -359,43 +360,89 @@ int get_db_strings(db_strings &str, int key) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_stub_trusted(const char *pkg, const char *trust_hash) {
|
||||||
|
// TODO: Remove when next stable released
|
||||||
|
if (trust_hash[0] == 0) {
|
||||||
|
LOGW("su: skip check stub.apk signature\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string cert = read_certificate(find_apk_path(pkg));
|
||||||
|
if (cert.empty())
|
||||||
|
return false;
|
||||||
|
uint8_t hash[SHA256_DIGEST_SIZE];
|
||||||
|
SHA256_hash(cert.data(), cert.length(), hash);
|
||||||
|
char hash_hex[SHA256_DIGEST_SIZE * 2 + 1];
|
||||||
|
char *ptr = &hash_hex[0];
|
||||||
|
for (uint8_t i: hash) {
|
||||||
|
ptr += sprintf(ptr, "%02x", i);
|
||||||
|
}
|
||||||
|
return strcmp(trust_hash, hash_hex) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool get_manager(int user_id, std::string *pkg, struct stat *st) {
|
bool get_manager(int user_id, std::string *pkg, struct stat *st) {
|
||||||
db_strings str;
|
db_strings str;
|
||||||
get_db_strings(str, SU_MANAGER);
|
get_db_strings(str, SU_MANAGER);
|
||||||
|
get_db_strings(str, CERT_DIGEST);
|
||||||
char app_path[128];
|
char app_path[128];
|
||||||
|
|
||||||
|
if (APKCERT.empty())
|
||||||
|
LOGW("su: skip check app signature\n");
|
||||||
|
|
||||||
if (!str[SU_MANAGER].empty()) {
|
if (!str[SU_MANAGER].empty()) {
|
||||||
// App is repackaged
|
// App is repackaged
|
||||||
sprintf(app_path, "%s/%d/%s", APP_DATA_DIR, user_id, str[SU_MANAGER].data());
|
sprintf(app_path, "%s/%d/%s", APP_DATA_DIR, user_id, str[SU_MANAGER].data());
|
||||||
if (stat(app_path, st) == 0) {
|
if (stat(app_path, st) == 0) {
|
||||||
|
if (is_stub_trusted(str[SU_MANAGER].data(), str[CERT_DIGEST].data())) {
|
||||||
|
strcpy(app_path, "/dyn/current.apk");
|
||||||
|
if (!APKCERT.empty() && access(app_path, F_OK) == 0) {
|
||||||
|
if (read_certificate(app_path) == APKCERT) {
|
||||||
if (pkg)
|
if (pkg)
|
||||||
pkg->swap(str[SU_MANAGER]);
|
pkg->swap(str[SU_MANAGER]);
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
LOGW("su: current.apk signature mismatch\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pkg)
|
||||||
|
pkg->swap(str[SU_MANAGER]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOGW("su: stub.apk signature mismatch\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the official package name
|
// Check the official package name
|
||||||
sprintf(app_path, "%s/%d/" JAVA_PACKAGE_NAME, APP_DATA_DIR, user_id);
|
sprintf(app_path, "%s/%d/" JAVA_PACKAGE_NAME, APP_DATA_DIR, user_id);
|
||||||
if (stat(app_path, st) == 0) {
|
if (stat(app_path, st) == 0) {
|
||||||
|
string cert = read_certificate(find_apk_path(JAVA_PACKAGE_NAME));
|
||||||
|
if (APKCERT.empty())
|
||||||
|
cert.clear();
|
||||||
|
if (cert == APKCERT) {
|
||||||
if (pkg)
|
if (pkg)
|
||||||
*pkg = JAVA_PACKAGE_NAME;
|
*pkg = JAVA_PACKAGE_NAME;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
LOGE("su: cannot find manager\n");
|
LOGW("su: app signature mismatch\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGE("su: cannot find trusted app\n");
|
||||||
memset(st, 0, sizeof(*st));
|
memset(st, 0, sizeof(*st));
|
||||||
if (pkg)
|
if (pkg)
|
||||||
pkg->clear();
|
pkg->clear();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool get_manager(string *pkg) {
|
bool get_manager(string *pkg) {
|
||||||
struct stat st;
|
struct stat st{};
|
||||||
return get_manager(0, pkg, &st);
|
return get_manager(0, pkg, &st);
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_manager_app_id() {
|
int get_manager_app_id() {
|
||||||
struct stat st;
|
struct stat st{};
|
||||||
if (get_manager(0, nullptr, &st))
|
if (get_manager(0, nullptr, &st))
|
||||||
return to_app_id(st.st_uid);
|
return to_app_id(st.st_uid);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -152,6 +152,7 @@ void exec_module_scripts(const char *stage, const vector<string_view> &modules)
|
|||||||
|
|
||||||
constexpr char install_script[] = R"EOF(
|
constexpr char install_script[] = R"EOF(
|
||||||
APK=%s
|
APK=%s
|
||||||
|
log -t Magisk "apk_uninstall: $(pm uninstall %s 2>&1)"
|
||||||
log -t Magisk "apk_install: $APK"
|
log -t Magisk "apk_install: $APK"
|
||||||
log -t Magisk "apk_install: $(pm install -r $APK 2>&1)"
|
log -t Magisk "apk_install: $(pm install -r $APK 2>&1)"
|
||||||
rm -f $APK
|
rm -f $APK
|
||||||
@ -163,7 +164,7 @@ void install_apk(const char *apk) {
|
|||||||
.fork = fork_no_orphan
|
.fork = fork_no_orphan
|
||||||
};
|
};
|
||||||
char cmds[sizeof(install_script) + 4096];
|
char cmds[sizeof(install_script) + 4096];
|
||||||
sprintf(cmds, install_script, apk);
|
sprintf(cmds, install_script, apk, JAVA_PACKAGE_NAME);
|
||||||
exec_command_sync(exec, "/system/bin/sh", "-c", cmds);
|
exec_command_sync(exec, "/system/bin/sh", "-c", cmds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,11 +84,12 @@ protected:
|
|||||||
* DB Strings *
|
* DB Strings *
|
||||||
**************/
|
**************/
|
||||||
|
|
||||||
constexpr const char *DB_STRING_KEYS[] = { "requester" };
|
constexpr const char *DB_STRING_KEYS[] = { "requester", "cert_digest" };
|
||||||
|
|
||||||
// Strings keys indices
|
// Strings keys indices
|
||||||
enum {
|
enum {
|
||||||
SU_MANAGER = 0
|
SU_MANAGER = 0,
|
||||||
|
CERT_DIGEST
|
||||||
};
|
};
|
||||||
|
|
||||||
class db_strings : public db_dict<std::string, std::size(DB_STRING_KEYS)> {
|
class db_strings : public db_dict<std::string, std::size(DB_STRING_KEYS)> {
|
||||||
|
@ -35,6 +35,7 @@ constexpr const char *applet_names[] = { "su", "resetprop", nullptr };
|
|||||||
|
|
||||||
extern int SDK_INT;
|
extern int SDK_INT;
|
||||||
#define APP_DATA_DIR (SDK_INT >= 24 ? "/data/user_de" : "/data/user")
|
#define APP_DATA_DIR (SDK_INT >= 24 ? "/data/user_de" : "/data/user")
|
||||||
|
extern std::string APKCERT;
|
||||||
|
|
||||||
// Multi-call entrypoints
|
// Multi-call entrypoints
|
||||||
int magisk_main(int argc, char *argv[]);
|
int magisk_main(int argc, char *argv[]);
|
||||||
|
@ -57,17 +57,33 @@ static void post_order_walk(int dirfd, const Func &fn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum visit_result {
|
||||||
|
CONTINUE, SKIP, TERMINATE
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
static void pre_order_walk(int dirfd, const Func &fn) {
|
static visit_result pre_order_walk(int dirfd, const Func &fn) {
|
||||||
auto dir = xopen_dir(dirfd);
|
auto dir = xopen_dir(dirfd);
|
||||||
if (!dir) return;
|
if (!dir) {
|
||||||
|
close(dirfd);
|
||||||
|
return SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||||
if (!fn(dirfd, entry))
|
switch (fn(dirfd, entry)) {
|
||||||
|
case CONTINUE:
|
||||||
|
break;
|
||||||
|
case SKIP:
|
||||||
continue;
|
continue;
|
||||||
if (entry->d_type == DT_DIR)
|
case TERMINATE:
|
||||||
pre_order_walk(xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC), fn);
|
return TERMINATE;
|
||||||
}
|
}
|
||||||
|
if (entry->d_type == DT_DIR) {
|
||||||
|
int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||||
|
if (pre_order_walk(fd, fn) == TERMINATE) return TERMINATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void remove_at(int dirfd, struct dirent *entry) {
|
static void remove_at(int dirfd, struct dirent *entry) {
|
||||||
@ -400,20 +416,20 @@ void parse_mnt(const char *file, const function<bool(mntent*)> &fn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void backup_folder(const char *dir, vector<raw_file> &files) {
|
void backup_folder(const char *dir, vector<raw_file> &files) {
|
||||||
char path[4096];
|
char path[PATH_MAX];
|
||||||
xrealpath(dir, path);
|
xrealpath(dir, path);
|
||||||
int len = strlen(path);
|
int len = strlen(path);
|
||||||
pre_order_walk(xopen(dir, O_RDONLY), [&](int dfd, dirent *entry) -> bool {
|
pre_order_walk(xopen(dir, O_RDONLY), [&](int dfd, dirent *entry) -> visit_result {
|
||||||
int fd = xopenat(dfd, entry->d_name, O_RDONLY);
|
int fd = xopenat(dfd, entry->d_name, O_RDONLY);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return false;
|
return SKIP;
|
||||||
run_finally f([&]{ close(fd); });
|
run_finally f([&]{ close(fd); });
|
||||||
if (fd_path(fd, path, sizeof(path)) < 0)
|
if (fd_path(fd, path, sizeof(path)) < 0)
|
||||||
return false;
|
return SKIP;
|
||||||
raw_file file;
|
raw_file file;
|
||||||
file.path = path + len + 1;
|
file.path = path + len + 1;
|
||||||
if (fgetattr(fd, &file.attr) < 0)
|
if (fgetattr(fd, &file.attr) < 0)
|
||||||
return false;
|
return SKIP;
|
||||||
if (entry->d_type == DT_REG) {
|
if (entry->d_type == DT_REG) {
|
||||||
fd_full_read(fd, file.buf, file.sz);
|
fd_full_read(fd, file.buf, file.sz);
|
||||||
} else if (entry->d_type == DT_LNK) {
|
} else if (entry->d_type == DT_LNK) {
|
||||||
@ -423,7 +439,7 @@ void backup_folder(const char *dir, vector<raw_file> &files) {
|
|||||||
memcpy(file.buf, path, file.sz);
|
memcpy(file.buf, path, file.sz);
|
||||||
}
|
}
|
||||||
files.emplace_back(std::move(file));
|
files.emplace_back(std::move(file));
|
||||||
return true;
|
return CONTINUE;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,3 +523,99 @@ mmap_data::mmap_data(const char *name, bool rw) {
|
|||||||
close(fd);
|
close(fd);
|
||||||
buf = static_cast<uint8_t *>(b);
|
buf = static_cast<uint8_t *>(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string find_apk_path(const char *pkg) {
|
||||||
|
char buf[PATH_MAX];
|
||||||
|
pre_order_walk(xopen("/data/app", O_RDONLY), [&](int dfd, dirent *entry) -> visit_result {
|
||||||
|
if (entry->d_type != DT_DIR)
|
||||||
|
return SKIP;
|
||||||
|
size_t len = strlen(pkg);
|
||||||
|
if (strncmp(entry->d_name, pkg, len) == 0 && entry->d_name[len] == '-') {
|
||||||
|
fd_pathat(dfd, entry->d_name, buf, sizeof(buf));
|
||||||
|
return TERMINATE;
|
||||||
|
} else if (strncmp(entry->d_name, "~~", 2) == 0) {
|
||||||
|
return CONTINUE;
|
||||||
|
} else return SKIP;
|
||||||
|
});
|
||||||
|
string path(buf);
|
||||||
|
return path.append("/base.apk");
|
||||||
|
}
|
||||||
|
|
||||||
|
string read_certificate(string app_path) {
|
||||||
|
string certificate;
|
||||||
|
uint32_t size4;
|
||||||
|
uint64_t size8, size_of_block;
|
||||||
|
|
||||||
|
int fd = xopen(app_path.data(), O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
return certificate;
|
||||||
|
}
|
||||||
|
run_finally f([&] { close(fd); });
|
||||||
|
|
||||||
|
for (int i = 0;; i++) {
|
||||||
|
unsigned short n;
|
||||||
|
lseek(fd, -i - 2, SEEK_END);
|
||||||
|
read(fd, &n, 2);
|
||||||
|
if (n == i) {
|
||||||
|
lseek(fd, -22, SEEK_CUR);
|
||||||
|
read(fd, &size4, 4);
|
||||||
|
if (size4 == 0x6054b50u) { // central directory end magic
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == 0xffff) {
|
||||||
|
return certificate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lseek(fd, 12, SEEK_CUR);
|
||||||
|
|
||||||
|
read(fd, &size4, 0x4);
|
||||||
|
lseek(fd, (off_t) (size4 - 0x18), SEEK_SET);
|
||||||
|
|
||||||
|
read(fd, &size8, 0x8);
|
||||||
|
unsigned char buffer[0x10] = {0};
|
||||||
|
read(fd, buffer, 0x10);
|
||||||
|
if (memcmp(buffer, "APK Sig Block 42", 0x10) != 0) {
|
||||||
|
return certificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
lseek(fd, (off_t) (size4 - (size8 + 0x8)), SEEK_SET);
|
||||||
|
read(fd, &size_of_block, 0x8);
|
||||||
|
if (size_of_block != size8) {
|
||||||
|
return certificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
uint32_t id;
|
||||||
|
uint32_t offset;
|
||||||
|
read(fd, &size8, 0x8); // sequence length
|
||||||
|
if (size8 == size_of_block) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
read(fd, &id, 0x4); // id
|
||||||
|
offset = 4;
|
||||||
|
|
||||||
|
if (id == 0x7109871au) {
|
||||||
|
read(fd, &size4, 0x4); // signer-sequence length
|
||||||
|
read(fd, &size4, 0x4); // signer length
|
||||||
|
read(fd, &size4, 0x4); // signed data length
|
||||||
|
offset += 0x4 * 3;
|
||||||
|
|
||||||
|
read(fd, &size4, 0x4); // digests-sequence length
|
||||||
|
lseek(fd, (off_t) (size4), SEEK_CUR);// skip digests
|
||||||
|
offset += 0x4 + size4;
|
||||||
|
|
||||||
|
read(fd, &size4, 0x4); // certificates length
|
||||||
|
read(fd, &size4, 0x4); // certificate length
|
||||||
|
offset += 0x4 * 2;
|
||||||
|
|
||||||
|
certificate.resize(size4);
|
||||||
|
read(fd, certificate.data(), size4);
|
||||||
|
|
||||||
|
offset += size4;
|
||||||
|
}
|
||||||
|
lseek(fd, (off_t) (size8 - offset), SEEK_CUR);
|
||||||
|
}
|
||||||
|
return certificate;
|
||||||
|
}
|
||||||
|
@ -102,6 +102,9 @@ void parse_mnt(const char *file, const std::function<bool(mntent*)> &fn);
|
|||||||
void backup_folder(const char *dir, std::vector<raw_file> &files);
|
void backup_folder(const char *dir, std::vector<raw_file> &files);
|
||||||
void restore_folder(const char *dir, std::vector<raw_file> &files);
|
void restore_folder(const char *dir, std::vector<raw_file> &files);
|
||||||
|
|
||||||
|
std::string find_apk_path(const char *pkg);
|
||||||
|
std::string read_certificate(std::string app_path);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void full_read(const char *filename, T &buf, size_t &size) {
|
void full_read(const char *filename, T &buf, size_t &size) {
|
||||||
static_assert(std::is_pointer<T>::value);
|
static_assert(std::is_pointer<T>::value);
|
||||||
|
Loading…
Reference in New Issue
Block a user