From 8e89b90b80f5e6bb44f6155ea4219aa8d5bcc5a8 Mon Sep 17 00:00:00 2001 From: Ylarod Date: Thu, 15 Dec 2022 12:22:43 +0800 Subject: [PATCH] Add ci for manager and userspace (#2) * kernel: move EXPECTED_* macro to Makefile * manager: add sign configs * tools: add check_v2 * CI: build manager * CI: build userspace --- .github/workflows/build-manager.yml | 40 ++++++ .github/workflows/build-userspace.yml | 24 ++++ kernel/Makefile | 4 + kernel/apk_sign.c | 3 - manager/.gitignore | 2 + manager/app/build.gradle | 9 +- manager/sign.example.properties | 4 + manager/sign.gradle | 16 +++ tools/check_v2.c | 173 ++++++++++++++++++++++++++ 9 files changed, 271 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/build-manager.yml create mode 100644 .github/workflows/build-userspace.yml create mode 100644 manager/sign.example.properties create mode 100644 manager/sign.gradle create mode 100644 tools/check_v2.c diff --git a/.github/workflows/build-manager.yml b/.github/workflows/build-manager.yml new file mode 100644 index 00000000..89596b3e --- /dev/null +++ b/.github/workflows/build-manager.yml @@ -0,0 +1,40 @@ +name: Build Manager +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./manager + steps: + - uses: actions/checkout@v3 + - name: set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + cache: gradle + - name: Extract keystore + if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }} + run: | + if [ ! -z "${{ secrets.KEY_STORE }}" ]; then + echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' >> sign.properties + echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}' >> sign.properties + echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}' >> sign.properties + echo KEYSTORE_FILE='../key.jks' >> sign.properties + echo ${{ secrets.KEYSTORE }} | base64 --decode > key.jks + fi + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: ./gradlew build + - name: Upload build artifact + uses: actions/upload-artifact@v2 + with: + name: manager + path: manager/app/build/outputs/apk/release/*.apk + diff --git a/.github/workflows/build-userspace.yml b/.github/workflows/build-userspace.yml new file mode 100644 index 00000000..a66a8a0b --- /dev/null +++ b/.github/workflows/build-userspace.yml @@ -0,0 +1,24 @@ +name: Build Userspace +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: nttld/setup-ndk@v1 + with: + ndk-version: r25b + local-cache: true + - name: Build with NDK + working-directory: ./userspace + run: ndk-build + - name: Upload a Build Artifact + uses: actions/upload-artifact@v3 + with: + name: su + path: ./userspace/libs + diff --git a/kernel/Makefile b/kernel/Makefile index 6a120cdb..ebbd5855 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -6,4 +6,8 @@ obj-y += sucompat.o obj-y += selinux/ +EXPECTED_SIZE := 0x033b +EXPECTED_HASH := 0xb0b91415 +ccflags-y += -DEXPECTED_SIZE=$(EXPECTED_SIZE) +ccflags-y += -DEXPECTED_HASH=$(EXPECTED_HASH) ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat diff --git a/kernel/apk_sign.c b/kernel/apk_sign.c index 3c791aaa..dd9c64f3 100644 --- a/kernel/apk_sign.c +++ b/kernel/apk_sign.c @@ -112,9 +112,6 @@ clean: return sign; } -#define EXPECTED_SIZE 0x033b -#define EXPECTED_HASH 0xb0b91415 - int is_manager_apk(char* path) { return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH); } \ No newline at end of file diff --git a/manager/.gitignore b/manager/.gitignore index aa724b77..c8a10379 100644 --- a/manager/.gitignore +++ b/manager/.gitignore @@ -13,3 +13,5 @@ .externalNativeBuild .cxx local.properties +sign.properties +key.jks diff --git a/manager/app/build.gradle b/manager/app/build.gradle index c08f7392..40be868f 100644 --- a/manager/app/build.gradle +++ b/manager/app/build.gradle @@ -7,6 +7,10 @@ android { namespace 'me.weishu.kernelsu' compileSdk 33 + signingConfigs { + sign + } + defaultConfig { applicationId "me.weishu.kernelsu" minSdk 26 @@ -29,6 +33,7 @@ android { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.sign } } compileOptions { @@ -83,4 +88,6 @@ dependencies { androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version" debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version" debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version" -} \ No newline at end of file +} + +apply from: rootProject.file('sign.gradle') \ No newline at end of file diff --git a/manager/sign.example.properties b/manager/sign.example.properties new file mode 100644 index 00000000..bc70a60c --- /dev/null +++ b/manager/sign.example.properties @@ -0,0 +1,4 @@ +KEYSTORE_FILE= +KEYSTORE_PASSWORD= +KEY_ALIAS= +KEY_PASSWORD= diff --git a/manager/sign.gradle b/manager/sign.gradle new file mode 100644 index 00000000..a0c1acce --- /dev/null +++ b/manager/sign.gradle @@ -0,0 +1,16 @@ +def ksFile = rootProject.file('sign.properties') +def props = new Properties() +if (ksFile.canRead()) { + props.load(new FileInputStream(ksFile)) + if (props != null) { + android.signingConfigs.sign.storeFile file(props['KEYSTORE_FILE']) + android.signingConfigs.sign.storePassword props['KEYSTORE_PASSWORD'] + android.signingConfigs.sign.keyAlias props['KEY_ALIAS'] + android.signingConfigs.sign.keyPassword props['KEY_PASSWORD'] + } +} else { + android.signingConfigs.sign.storeFile = android.signingConfigs.debug.storeFile + android.signingConfigs.sign.storePassword = android.signingConfigs.debug.storePassword + android.signingConfigs.sign.keyAlias = android.signingConfigs.debug.keyAlias + android.signingConfigs.sign.keyPassword = android.signingConfigs.debug.keyPassword +} diff --git a/tools/check_v2.c b/tools/check_v2.c new file mode 100644 index 00000000..61ca1bea --- /dev/null +++ b/tools/check_v2.c @@ -0,0 +1,173 @@ +// +// Created by Thom on 2019/3/8. +// + +// Credits: https://github.com/brevent/genuine/blob/master/src/main/jni/apk-sign-v2.c +#include +#include +#include + +#define MAIN + +#ifdef MAIN +#include +#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