diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d6d829a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,62 @@ +name: Release + +on: + push: + tags: + - "*.*.*" + +permissions: + contents: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "17" + + - name: Set up Android SDK + uses: android-actions/setup-android@v4 + with: + packages: "platform-tools platforms;android-35 build-tools;35.0.0" + + # Optional — only produces a properly release-signed APK if these repo secrets are set: + # KEYSTORE_BASE64 (base64 of a release .jks), KEYSTORE_PASSWORD, KEY_ALIAS, KEY_PASSWORD. + # Without them, the build falls back to debug signing (still installable, just not + # signed with a dedicated release key). + - name: Set up release signing + env: + KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} + KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} + run: | + if [ -n "$KEYSTORE_BASE64" ]; then + echo "$KEYSTORE_BASE64" | base64 -d > release.jks + cat > keystore.properties < [!IMPORTANT] +> Built entirely by AI in one evening to fix a personal problem. YMMV — it works for me +> and hasn't been audited beyond that. + +## Requirements + +- Rooted device with a working Zygisk implementation and LSPosed (or an LSPosed-compatible + framework such as Vector). +- Android 8.1+. + +## Setup + +1. Build (below) or grab a release APK from the repo's Releases page. +2. Enable the module in LSPosed Manager and scope it to the target app. +3. Open the target app once — it appears in Veil's own settings screen automatically. +4. Uncheck anything you don't want hidden for that app. + +If the target app still won't launch, check whether it's on your root manager's DenyList +(e.g. KernelSU: app profile → "Umount modules"). An app on that list never gets Zygisk +modules injected at all, Veil included. The settings screen's help panel covers this and +a few other gotchas. + +## Features + +Per app, per feature — anything not listed gets everything hidden by default. + +| Feature | Hides | +| ------------------------ | ------------------------------------------------------ | +| Accessibility services | Any accessibility service running | +| USB / wireless debugging | `adb_enabled` / `adb_wifi_enabled` | +| Developer options | Developer Options enabled state | +| Mock location | Legacy pre-Marshmallow flag only | +| Root / manager apps | Configurable package list (default: KernelSU, Magisk) | +| Build tags/type | Real build swapped for `release-keys` / `user` | + +Doesn't cover native-level root/hook detection or remote attestation (Play Integrity, +SafetyNet) — that's your Zygisk implementation's job, not Veil's. + +## Building + +Requires JDK 17 and Android SDK (platform 35, build-tools 35.0.0). + +``` +./gradlew assembleDebug # debug-signed APK +./gradlew assembleRelease # minified release build, debug-signed unless keystore.properties exists +scripts/generate-keystore.sh # create a personal release-signing keystore +scripts/release.sh # build + copy signed release APK into dist/ +``` + +## CI releases + +Pushing a tag matching `*.*.*` (e.g. `1.2.0`) triggers `.github/workflows/release.yml`. +For a properly signed CI build, set repo secrets: `KEYSTORE_BASE64` (base64 of your +`release.jks`), `KEYSTORE_PASSWORD`, `KEY_ALIAS`, `KEY_PASSWORD`. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8bf2a53..2653866 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,23 +1,56 @@ +import java.util.Properties + plugins { id("com.android.application") id("org.jetbrains.kotlin.android") } +val keystorePropertiesFile = rootProject.file("keystore.properties") +val keystoreProperties = Properties() +val hasReleaseKeystore = keystorePropertiesFile.exists() +if (hasReleaseKeystore) { + keystoreProperties.load(keystorePropertiesFile.inputStream()) +} + android { namespace = "space.bdeshi.veil" compileSdk = 35 defaultConfig { applicationId = "space.bdeshi.veil" + // 27 (Android 8.1) is LSPosed/Zygisk's own practical floor — nothing here restricts + // it further. No upper bound: compileSdk is a compile-time API surface, not a ceiling + // on which OS versions can install and run the app. minSdk = 27 targetSdk = 35 versionCode = 1 versionName = "1.0" } + signingConfigs { + create("release") { + if (hasReleaseKeystore) { + storeFile = rootProject.file(keystoreProperties["storeFile"] as String) + storePassword = keystoreProperties["storePassword"] as String + keyAlias = keystoreProperties["keyAlias"] as String + keyPassword = keystoreProperties["keyPassword"] as String + } + } + } + buildTypes { release { - isMinifyEnabled = false + isMinifyEnabled = true + isShrinkResources = true + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + // Falls back to debug signing when no keystore.properties exists (e.g. a fresh + // clone, or CI without release-signing secrets configured), so assembleRelease + // always produces something installable. See scripts/generate-keystore.sh. + signingConfig = if (hasReleaseKeystore) { + signingConfigs.getByName("release") + } else { + signingConfigs.getByName("debug") + } } } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..4b277ab --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,5 @@ +# LSPosed discovers the module entry point by fully-qualified class name via the plaintext +# assets/xposed_init file, not through anything R8 can see (no manifest reference, no +# reflection call it can trace) — keep this class and its members intact or the module +# silently stops loading with no build error to point at why. +-keep class space.bdeshi.veil.VeilHook { *; } diff --git a/scripts/generate-keystore.sh b/scripts/generate-keystore.sh new file mode 100755 index 0000000..872572b --- /dev/null +++ b/scripts/generate-keystore.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Creates a personal release-signing keystore and keystore.properties. Both are gitignored — +# back them up yourself if you want release builds to keep the same signing identity across +# reinstalls (Android treats a differently-signed update as a different app). +set -euo pipefail +cd "$(dirname "$0")/.." + +if [ -f keystore.properties ]; then + echo "keystore.properties already exists — remove it first if you want to regenerate." >&2 + exit 1 +fi + +KEYSTORE_PATH="release.jks" +ALIAS="veil" + +read -rsp "New keystore password: " STORE_PASSWORD +echo +read -rsp "Key password (leave blank to reuse the keystore password): " KEY_PASSWORD +echo +KEY_PASSWORD="${KEY_PASSWORD:-$STORE_PASSWORD}" + +keytool -genkeypair \ + -keystore "$KEYSTORE_PATH" \ + -alias "$ALIAS" \ + -keyalg RSA \ + -keysize 2048 \ + -validity 10000 \ + -storepass "$STORE_PASSWORD" \ + -keypass "$KEY_PASSWORD" \ + -dname "CN=Veil, OU=Personal, O=Personal, L=Unknown, ST=Unknown, C=US" + +cat > keystore.properties <