kernel: harden the signature check (#1027)

This commit is contained in:
weishu
2023-10-11 15:53:11 +08:00
committed by GitHub
parent 7753dc0987
commit a22959beae
10 changed files with 321 additions and 293 deletions

View File

@@ -29,13 +29,13 @@ KSU_EXPECTED_SIZE := 0x033b
endif endif
ifndef KSU_EXPECTED_HASH ifndef KSU_EXPECTED_HASH
KSU_EXPECTED_HASH := 0xb0b91415 KSU_EXPECTED_HASH := c371061b19d8c7d7d6133c6a9bafe198fa944e50c1b31c9d8daa8d7f1fc2d2d6
endif endif
$(info -- KernelSU Manager signature size: $(KSU_EXPECTED_SIZE)) $(info -- KernelSU Manager signature size: $(KSU_EXPECTED_SIZE))
$(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH)) $(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH))
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE) ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
ccflags-y += -DEXPECTED_HASH=$(KSU_EXPECTED_HASH) ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
ccflags-y += -Wno-declaration-after-statement ccflags-y += -Wno-declaration-after-statement

View File

@@ -1,20 +1,136 @@
#include "linux/err.h"
#include "linux/fs.h" #include "linux/fs.h"
#include "linux/gfp.h"
#include "linux/kernel.h"
#include "linux/moduleparam.h" #include "linux/moduleparam.h"
#include "apk_sign.h" #include "apk_sign.h"
#include "klog.h" // IWYU pragma: keep #include "klog.h" // IWYU pragma: keep
#include "kernel_compat.h" #include "kernel_compat.h"
#include "crypto/hash.h"
#include "linux/slab.h"
#include "linux/version.h"
static __always_inline int #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash) #include "crypto/sha2.h"
#else
#include "crypto/sha.h"
#endif
struct sdesc {
struct shash_desc shash;
char ctx[];
};
static struct sdesc *init_sdesc(struct crypto_shash *alg)
{
struct sdesc *sdesc;
int size;
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
sdesc = kmalloc(size, GFP_KERNEL);
if (!sdesc)
return ERR_PTR(-ENOMEM);
sdesc->shash.tfm = alg;
return sdesc;
}
static int calc_hash(struct crypto_shash *alg, const unsigned char *data,
unsigned int datalen, unsigned char *digest)
{
struct sdesc *sdesc;
int ret;
sdesc = init_sdesc(alg);
if (IS_ERR(sdesc)) {
pr_info("can't alloc sdesc\n");
return PTR_ERR(sdesc);
}
ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
kfree(sdesc);
return ret;
}
static int ksu_sha256(const unsigned char *data, unsigned int datalen,
unsigned char *digest)
{
struct crypto_shash *alg;
char *hash_alg_name = "sha256";
int ret;
alg = crypto_alloc_shash(hash_alg_name, 0, 0);
if (IS_ERR(alg)) {
pr_info("can't alloc alg %s\n", hash_alg_name);
return PTR_ERR(alg);
}
ret = calc_hash(alg, data, datalen, digest);
crypto_free_shash(alg);
return ret;
}
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
unsigned expected_size, const char* expected_sha256)
{
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length
*offset += 0x4 * 3;
ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length
*pos += *size4;
*offset += 0x4 + *size4;
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificates length
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length
*offset += 0x4 * 2;
if (*size4 == expected_size) {
*offset += *size4;
#define CERT_MAX_LENGTH 1024
char cert[CERT_MAX_LENGTH];
if (*size4 > CERT_MAX_LENGTH) {
pr_info("cert length overlimit\n");
return false;
}
ksu_kernel_read_compat(fp, cert, *size4, pos);
unsigned char digest[SHA256_DIGEST_SIZE];
if (IS_ERR(ksu_sha256(cert, *size4, digest))) {
pr_info("sha256 error\n");
return false;
}
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
pr_info("sha256: %s, expected: %s\n", hash_str, expected_sha256);
if (strcmp(expected_sha256, hash_str) == 0) {
return true;
}
}
return false;
}
static __always_inline bool
check_v2_signature(char *path, unsigned expected_size, const char *expected_sha256)
{ {
unsigned char buffer[0x11] = { 0 }; unsigned char buffer[0x11] = { 0 };
u32 size4; u32 size4;
u64 size8, size_of_block; u64 size8, size_of_block;
loff_t pos; loff_t pos;
bool block_valid;
const int NOT_EXIST = 0;
const int INVALID = 1;
const int VALID = 2;
int v2_signing_status = NOT_EXIST;
int v3_signing_status = NOT_EXIST;
int sign = -1;
int i; int i;
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0); struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
if (IS_ERR(fp)) { if (IS_ERR(fp)) {
@@ -25,7 +141,6 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
// disable inotify for this file // disable inotify for this file
fp->f_mode |= FMODE_NONOTIFY; fp->f_mode |= FMODE_NONOTIFY;
sign = 1;
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD) // https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
for (i = 0;; ++i) { for (i = 0;; ++i) {
unsigned short n; unsigned short n;
@@ -64,59 +179,22 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
for (;;) { for (;;) {
uint32_t id; uint32_t id;
uint32_t offset; uint32_t offset;
ksu_kernel_read_compat(fp, &size8, 0x8, &pos); // sequence length ksu_kernel_read_compat(fp, &size8, 0x8,
&pos); // sequence length
if (size8 == size_of_block) { if (size8 == size_of_block) {
break; break;
} }
ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id
offset = 4; offset = 4;
pr_info("id: 0x%08x\n", id); pr_info("id: 0x%08x\n", id);
if ((id ^ 0xdeadbeefu) == 0xafa439f5u || if (id == 0x7109871au) {
(id ^ 0xdeadbeefu) == 0x2efed62f) { block_valid = check_block(fp, &size4, &pos, &offset,
ksu_kernel_read_compat(fp, &size4, 0x4, expected_size, expected_sha256);
&pos); // signer-sequence length v2_signing_status = block_valid ? VALID : INVALID;
ksu_kernel_read_compat(fp, &size4, 0x4, &pos); // signer length } else if (id == 0xf05368c0u) {
ksu_kernel_read_compat(fp, &size4, 0x4, block_valid = check_block(fp, &size4, &pos, &offset,
&pos); // signed data length expected_size, expected_sha256);
offset += 0x4 * 3; v3_signing_status = block_valid ? VALID : INVALID;
ksu_kernel_read_compat(fp, &size4, 0x4,
&pos); // digests-sequence length
pos += size4;
offset += 0x4 + size4;
ksu_kernel_read_compat(fp, &size4, 0x4,
&pos); // certificates length
ksu_kernel_read_compat(fp, &size4, 0x4,
&pos); // certificate length
offset += 0x4 * 2;
#if 0
int hash = 1;
signed char c;
for (i = 0; i < size4; ++i) {
ksu_kernel_read_compat(fp, &c, 0x1, &pos);
hash = 31 * hash + c;
}
offset += size4;
pr_info(" size: 0x%04x, hash: 0x%08x\n", size4, ((unsigned) hash) ^ 0x14131211u);
#else
if (size4 == expected_size) {
int hash = 1;
signed char c;
for (i = 0; i < size4; ++i) {
ksu_kernel_read_compat(fp, &c, 0x1, &pos);
hash = 31 * hash + c;
}
offset += size4;
if ((((unsigned)hash) ^ 0x14131211u) ==
expected_hash) {
sign = 0;
break;
}
}
// don't try again.
break;
#endif
} }
pos += (size8 - offset); pos += (size8 - offset);
} }
@@ -124,13 +202,15 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
clean: clean:
filp_close(fp, 0); filp_close(fp, 0);
return sign; return (v2_signing_status == NOT_EXIST && v3_signing_status == VALID) ||
(v2_signing_status == VALID && v3_signing_status == NOT_EXIST) ||
(v2_signing_status == VALID && v3_signing_status == VALID);
} }
#ifdef CONFIG_KSU_DEBUG #ifdef CONFIG_KSU_DEBUG
unsigned ksu_expected_size = EXPECTED_SIZE; unsigned ksu_expected_size = EXPECTED_SIZE;
unsigned ksu_expected_hash = EXPECTED_HASH; const char *ksu_expected_hash = EXPECTED_HASH;
#include "manager.h" #include "manager.h"
@@ -144,9 +224,10 @@ static int set_expected_size(const char *val, const struct kernel_param *kp)
static int set_expected_hash(const char *val, const struct kernel_param *kp) static int set_expected_hash(const char *val, const struct kernel_param *kp)
{ {
int rv = param_set_uint(val, kp); pr_info("set_expected_hash: %s\n", val);
int rv = param_set_charp(val, kp);
ksu_invalidate_manager_uid(); ksu_invalidate_manager_uid();
pr_info("ksu_expected_hash set to %x\n", ksu_expected_hash); pr_info("ksu_expected_hash set to %s\n", ksu_expected_hash);
return rv; return rv;
} }
@@ -157,7 +238,8 @@ static struct kernel_param_ops expected_size_ops = {
static struct kernel_param_ops expected_hash_ops = { static struct kernel_param_ops expected_hash_ops = {
.set = set_expected_hash, .set = set_expected_hash,
.get = param_get_uint, .get = param_get_charp,
.free = param_free_charp,
}; };
module_param_cb(ksu_expected_size, &expected_size_ops, &ksu_expected_size, module_param_cb(ksu_expected_size, &expected_size_ops, &ksu_expected_size,
@@ -165,14 +247,14 @@ module_param_cb(ksu_expected_size, &expected_size_ops, &ksu_expected_size,
module_param_cb(ksu_expected_hash, &expected_hash_ops, &ksu_expected_hash, module_param_cb(ksu_expected_hash, &expected_hash_ops, &ksu_expected_hash,
S_IRUSR | S_IWUSR); S_IRUSR | S_IWUSR);
int is_manager_apk(char *path) bool is_manager_apk(char *path)
{ {
return check_v2_signature(path, ksu_expected_size, ksu_expected_hash); return check_v2_signature(path, ksu_expected_size, ksu_expected_hash);
} }
#else #else
int is_manager_apk(char *path) bool is_manager_apk(char *path)
{ {
return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH); return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH);
} }

View File

@@ -1,7 +1,8 @@
#ifndef __KSU_H_APK_V2_SIGN #ifndef __KSU_H_APK_V2_SIGN
#define __KSU_H_APK_V2_SIGN #define __KSU_H_APK_V2_SIGN
// return 0 if signature match #include "linux/types.h"
int is_manager_apk(char *path);
bool is_manager_apk(char *path);
#endif #endif

View File

@@ -70,7 +70,7 @@ bool become_manager(char *pkg)
pr_info("invalid pkg: %s\n", pkg); pr_info("invalid pkg: %s\n", pkg);
continue; continue;
} }
if (is_manager_apk(cwd) == 0) { if (is_manager_apk(cwd)) {
// check passed // check passed
uid_t uid = current_uid().val; uid_t uid = current_uid().val;
pr_info("manager uid: %d\n", uid); pr_info("manager uid: %d\n", uid);

View File

@@ -1,174 +0,0 @@
//
// Created by Thom on 2019/3/8.
//
// Credits: https://github.com/brevent/genuine/blob/master/src/main/jni/apk-sign-v2.c
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdint.h>
#define MAIN
#ifdef MAIN
#include <stdio.h>
#else
#include "common.h"
#include "openat.h"
#endif
static bool isApkSigBlock42(const char *buffer) {
// APK Sig Block 42
return *buffer == 'A'
&& *++buffer == 'P'
&& *++buffer == 'K'
&& *++buffer == ' '
&& *++buffer == 'S'
&& *++buffer == 'i'
&& *++buffer == 'g'
&& *++buffer == ' '
&& *++buffer == 'B'
&& *++buffer == 'l'
&& *++buffer == 'o'
&& *++buffer == 'c'
&& *++buffer == 'k'
&& *++buffer == ' '
&& *++buffer == '4'
&& *++buffer == '2';
}
int checkSignature(const char *path) {
unsigned char buffer[0x11] = {0};
uint32_t size4;
uint64_t size8, size_of_block;
#ifdef DEBUG
LOGI("check signature for %s", path);
#endif
int sign = -1;
int fd = (int) openat(AT_FDCWD, path, O_RDONLY);
#ifdef DEBUG_OPENAT
LOGI("openat %s returns %d", path, fd);
#endif
if (fd < 0) {
return sign;
}
sign = 1;
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
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 ^ 0xcafebabeu) == 0xccfbf1eeu) {
#ifdef MAIN
if (i > 0) {
printf("warning: comment length is %d\n", i);
}
#endif
break;
}
}
if (i == 0xffff) {
#ifdef MAIN
printf("error: cannot find eocd\n");
#endif
goto clean;
}
}
lseek(fd, 12, SEEK_CUR);
// offset
read(fd, &size4, 0x4);
lseek(fd, (off_t) (size4 - 0x18), SEEK_SET);
read(fd, &size8, 0x8);
read(fd, buffer, 0x10);
if (!isApkSigBlock42((char *) buffer)) {
goto clean;
}
lseek(fd, (off_t) (size4 - (size8 + 0x8)), SEEK_SET);
read(fd, &size_of_block, 0x8);
if (size_of_block != size8) {
goto clean;
}
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;
#ifdef MAIN
// printf("id: 0x%08x\n", id);
#endif
if ((id ^ 0xdeadbeefu) == 0xafa439f5u || (id ^ 0xdeadbeefu) == 0x2efed62f) {
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;
#ifdef MAIN
int hash = 1;
signed char c;
for (unsigned i = 0; i < size4; ++i) {
read(fd, &c, 0x1);
hash = 31 * hash + c;
}
offset += size4;
// printf(" size: 0x%04x, hash: 0x%08x\n", size4, ((unsigned) hash) ^ 0x14131211u);
printf("0x%04x 0x%08x\n", size4, ((unsigned) hash) ^ 0x14131211u);
#else
#if defined(GENUINE_SIZE) && defined(GENUINE_HASH)
if (size4 == GENUINE_SIZE) {
int hash = 1;
signed char c;
for (unsigned i = 0; i < size4; ++i) {
read(fd, &c, 0x1);
hash = 31 * hash + c;
}
offset += size4;
if ((((unsigned) hash) ^ 0x14131211u) == GENUINE_HASH) {
sign = 0;
break;
}
}
#else
sign = 0;
break;
#endif
#endif
}
lseek(fd, (off_t) (size8 - offset), SEEK_CUR);
}
clean:
close(fd);
return sign;
}
#ifdef MAIN
int main(int argc, char **argv) {
if (argc > 1) {
checkSignature(argv[1]);
}
}
#endif

View File

@@ -2,6 +2,15 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "addr2line"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
dependencies = [
"gimli",
]
[[package]] [[package]]
name = "adler" name = "adler"
version = "1.0.2" version = "1.0.2"
@@ -74,12 +83,38 @@ version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
[[package]]
name = "async-trait"
version = "0.1.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]] [[package]]
name = "base64ct" name = "base64ct"
version = "1.6.0" version = "1.6.0"
@@ -132,6 +167,12 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]] [[package]]
name = "bzip2" name = "bzip2"
version = "0.4.4" version = "0.4.4"
@@ -234,7 +275,7 @@ dependencies = [
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 1.0.107",
] ]
[[package]] [[package]]
@@ -407,7 +448,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"scratch", "scratch",
"syn", "syn 1.0.107",
] ]
[[package]] [[package]]
@@ -424,7 +465,7 @@ checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 1.0.107",
] ]
[[package]] [[package]]
@@ -435,7 +476,7 @@ checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 1.0.107",
] ]
[[package]] [[package]]
@@ -604,6 +645,12 @@ dependencies = [
"wasi 0.11.0+wasi-snapshot-preview1", "wasi 0.11.0+wasi-snapshot-preview1",
] ]
[[package]]
name = "gimli"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
[[package]] [[package]]
name = "glob" name = "glob"
version = "0.3.1" version = "0.3.1"
@@ -700,7 +747,7 @@ dependencies = [
"proc-macro-hack", "proc-macro-hack",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 1.0.107",
] ]
[[package]] [[package]]
@@ -816,6 +863,7 @@ dependencies = [
"rust-embed", "rust-embed",
"serde", "serde",
"serde_json", "serde_json",
"sha256",
"sys-mount", "sys-mount",
"which", "which",
"zip 0.6.4", "zip 0.6.4",
@@ -979,6 +1027,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "object"
version = "0.30.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.17.0" version = "1.17.0"
@@ -1053,7 +1110,7 @@ dependencies = [
"proc-macro-error-attr", "proc-macro-error-attr",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 1.0.107",
"version_check", "version_check",
] ]
@@ -1076,9 +1133,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.50" version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -1100,9 +1157,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.23" version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@@ -1212,7 +1269,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rust-embed-utils", "rust-embed-utils",
"syn", "syn 1.0.107",
"walkdir", "walkdir",
] ]
@@ -1226,6 +1283,12 @@ dependencies = [
"walkdir", "walkdir",
] ]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]] [[package]]
name = "rustc-hash" name = "rustc-hash"
version = "1.1.0" version = "1.1.0"
@@ -1312,6 +1375,19 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "sha256"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7895c8ae88588ccead14ff438b939b0c569cd619116f14b4d13fdff7b8333386"
dependencies = [
"async-trait",
"bytes",
"hex",
"sha2",
"tokio",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.1.0" version = "1.1.0"
@@ -1326,7 +1402,7 @@ checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 1.0.107",
] ]
[[package]] [[package]]
@@ -1352,6 +1428,17 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "syn"
version = "2.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]] [[package]]
name = "sys-mount" name = "sys-mount"
version = "2.0.2" version = "2.0.2"
@@ -1391,7 +1478,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 1.0.107",
] ]
[[package]] [[package]]
@@ -1421,6 +1508,18 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
[[package]]
name = "tokio"
version = "1.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
dependencies = [
"autocfg",
"backtrace",
"bytes",
"pin-project-lite",
]
[[package]] [[package]]
name = "tracing" name = "tracing"
version = "0.1.37" version = "0.1.37"
@@ -1441,7 +1540,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 1.0.107",
] ]
[[package]] [[package]]
@@ -1527,7 +1626,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 1.0.107",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@@ -1549,7 +1648,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 1.0.107",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]

View File

@@ -33,6 +33,7 @@ rust-embed = { version = "6.4.2", features = [
] } ] }
which = "4.2.2" which = "4.2.2"
getopts = "0.2.21" getopts = "0.2.21"
sha256 = "1.4.0"
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies] [target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies]
sys-mount = { git = "https://github.com/tiann/sys-mount", branch = "loopfix" } sys-mount = { git = "https://github.com/tiann/sys-mount", branch = "loopfix" }

View File

@@ -1,7 +1,7 @@
use anyhow::{ensure, Result}; use anyhow::{ensure, Result};
use std::io::{Read, Seek, SeekFrom}; use std::io::{Read, Seek, SeekFrom};
pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> { pub fn get_apk_signature(apk: &str) -> Result<(u32, String)> {
let mut buffer = [0u8; 0x10]; let mut buffer = [0u8; 0x10];
let mut size4 = [0u8; 4]; let mut size4 = [0u8; 4];
let mut size8 = [0u8; 8]; let mut size8 = [0u8; 8];
@@ -49,9 +49,11 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
ensure!(size_of_block == size8, "not a signed apk"); ensure!(size_of_block == size8, "not a signed apk");
let mut v2_signing: Option<(u32, String)> = None;
let mut v3_signing: Option<(u32, String)> = None;
loop { loop {
let mut id = [0u8; 4]; let mut id = [0u8; 4];
let offset = 4u32; let mut offset = 4u32;
f.read_exact(&mut size8)?; // sequence length f.read_exact(&mut size8)?; // sequence length
if size8 == size_of_block { if size8 == size_of_block {
@@ -61,36 +63,10 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
f.read_exact(&mut id)?; // id f.read_exact(&mut id)?; // id
let id = u32::from_le_bytes(id); let id = u32::from_le_bytes(id);
if (id ^ 0xdead_beef_u32) == 0xafa4_39f5_u32 || (id ^ 0xdead_beef_u32) == 0x2efe_d62f_u32 { if id == 0x7109_871a_u32 {
f.read_exact(&mut size4)?; // signer-sequence length v2_signing = Some(calc_cert_sha256(&mut f, &mut size4, &mut offset)?);
f.read_exact(&mut size4)?; // signer length } else if id == 0xf053_68c0_u32 {
f.read_exact(&mut size4)?; // signed data length v3_signing = Some(calc_cert_sha256(&mut f, &mut size4, &mut offset)?);
// offset += 0x4 * 3;
f.read_exact(&mut size4)?; // digests-sequcence length
let pos = u32::from_le_bytes(size4);
f.seek(SeekFrom::Current(i64::from(pos)))?;
// offset += 0x4 + pos;
f.read_exact(&mut size4)?; // certificates length
f.read_exact(&mut size4)?; // certificate length
// offset += 0x4 * 2;
let mut hash = 1i32;
let mut c = [0u8; 1];
let j = u32::from_le_bytes(size4);
for _ in 0..j {
f.read_exact(&mut c)?;
hash = hash.wrapping_mul(31).wrapping_add(i32::from(c[0] as i8));
}
// offset += j;
let out_size = j;
let out_hash = (hash as u32) ^ 0x1413_1211_u32;
return Ok((out_size, out_hash));
} }
f.seek(SeekFrom::Current( f.seek(SeekFrom::Current(
@@ -98,5 +74,47 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
))?; ))?;
} }
Err(anyhow::anyhow!("Unknown error")) match (v2_signing, v3_signing) {
(None, Some(s)) => Ok(s),
(Some(s), None) => Ok(s),
(Some(s1), Some(s2)) => {
if s1 == s2 {
Ok(s1)
} else {
Err(anyhow::anyhow!(
"Inconsisent signature, v2: {}, v3: {}!",
s1.1,
s2.1
))
}
}
_ => Err(anyhow::anyhow!("Unknown signature!")),
}
}
fn calc_cert_sha256(
f: &mut std::fs::File,
size4: &mut [u8; 4],
offset: &mut u32,
) -> Result<(u32, String)> {
f.read_exact(size4)?; // signer-sequence length
f.read_exact(size4)?; // signer length
f.read_exact(size4)?; // signed data length
*offset += 0x4 * 3;
f.read_exact(size4)?; // digests-sequence length
let pos = u32::from_le_bytes(*size4); // skip digests
f.seek(SeekFrom::Current(i64::from(pos)))?;
*offset += 0x4 + pos;
f.read_exact(size4)?; // certificates length
f.read_exact(size4)?; // certificate length
*offset += 0x4 * 2;
let cert_len = u32::from_le_bytes(*size4);
let mut cert: Vec<u8> = vec![0; cert_len as usize];
f.read_exact(&mut cert)?;
*offset += cert_len;
Ok((cert_len, sha256::digest(&cert)))
} }

View File

@@ -228,7 +228,7 @@ pub fn run() -> Result<()> {
Debug::SetManager { apk } => debug::set_manager(&apk), Debug::SetManager { apk } => debug::set_manager(&apk),
Debug::GetSign { apk } => { Debug::GetSign { apk } => {
let sign = apk_sign::get_apk_signature(&apk)?; let sign = apk_sign::get_apk_signature(&apk)?;
println!("size: {:#x}, hash: {:#x}", sign.0, sign.1); println!("size: {:#x}, hash: {}", sign.0, sign.1);
Ok(()) Ok(())
} }
Debug::Version => { Debug::Version => {

View File

@@ -15,24 +15,25 @@ fn read_u32(path: &PathBuf) -> Result<u32> {
Ok(content) Ok(content)
} }
fn set_kernel_param(size: u32, hash: u32) -> Result<()> { fn set_kernel_param(size: u32, hash: String) -> Result<()> {
let kernel_param_path = Path::new(KERNEL_PARAM_PATH).join("parameters"); let kernel_param_path = Path::new(KERNEL_PARAM_PATH).join("parameters");
let expeced_size_path = kernel_param_path.join("ksu_expected_size"); let expeced_size_path = kernel_param_path.join("ksu_expected_size");
let expeced_hash_path = kernel_param_path.join("ksu_expected_hash"); let expeced_hash_path = kernel_param_path.join("ksu_expected_hash");
println!( println!(
"before size: {:#x} hash: {:#x}", "before size: {:#x} hash: {}",
read_u32(&expeced_size_path)?, read_u32(&expeced_size_path)?,
read_u32(&expeced_hash_path)? std::fs::read_to_string(&expeced_hash_path)?
); );
std::fs::write(&expeced_size_path, size.to_string())?; std::fs::write(&expeced_size_path, size.to_string())?;
std::fs::write(&expeced_hash_path, hash.to_string())?; std::fs::write(&expeced_hash_path, hash)?;
println!( println!(
"after size: {:#x} hash: {:#x}", "after size: {:#x} hash: {}",
read_u32(&expeced_size_path)?, read_u32(&expeced_size_path)?,
read_u32(&expeced_hash_path)? std::fs::read_to_string(&expeced_hash_path)?
); );
Ok(()) Ok(())