mirror of
https://github.com/okhsunrog/vpnhide.git
synced 2026-04-28 14:44:43 +00:00
Build system: - Replace hardcoded paths in Makefile with env vars (KERNEL_SRC, CLANG_DIR) - Add .env.example and .envrc for direnv-based config - Simplify build-zip.sh to delegate to make instead of duplicating build command - Rewrite BUILDING.md: 5-step happy path with direnv, standalone prep as appendix - Remove redundant quick-reference script and step 7 (manual module.lds hack) Kernel module (vpnhide_kmod.c): - Fix fib_route_seq_show hook: save seq_file pointer and buffer position in entry handler instead of reading regs->regs[0] in return handler (which holds the return value on arm64, not the original argument). Rewrite buffer scanning as clean forward iteration with memmove compaction. - Remove dead SIOCGIFCONF case from dev_ioctl hook (confirmed in kernel source: SIOCGIFCONF goes through sock_ioctl -> dev_ifconf, not dev_ioctl on GKI 6.1) - Fix header comment: remove false tcp4_seq_show claim, correct rtnl symbol name Test app: - Auto-run checks on launch (LaunchedEffect) for easier adb-driven testing
291 lines
8.9 KiB
Markdown
291 lines
8.9 KiB
Markdown
# Building vpnhide-kmod for a new device
|
|
|
|
This guide walks through building the kernel module for any Android
|
|
device with a GKI 2.0 kernel (Android 12+, kernel 5.10+). The process
|
|
was developed and tested on Pixel 8 Pro (android14-6.1) and applies
|
|
identically to any GKI generation — only the kernel branch and
|
|
Module.symvers change.
|
|
|
|
Total time: ~15 minutes once toolchain is set up.
|
|
Download size: ~500 MB (shallow kernel clone) + ~100 MB (toolchain if
|
|
not already available).
|
|
|
|
## Prerequisites
|
|
|
|
- A rooted Android device with KernelSU or Magisk (for adb root shell)
|
|
- Linux host (Arch, Ubuntu, Debian — anything with make, clang, git)
|
|
- `adb` connected to the device
|
|
- `modprobe` on the host (for extracting CRCs from .ko files)
|
|
- [`direnv`](https://direnv.net/) (for automatic env var loading)
|
|
|
|
## Step 1: Identify the GKI generation
|
|
|
|
```bash
|
|
adb shell uname -r
|
|
```
|
|
|
|
Example outputs and what they mean:
|
|
|
|
| `uname -r` output | GKI generation | Kernel branch |
|
|
|---|---|---|
|
|
| `5.10.xxx-android13-4-g...` | android13-5.10 | `android13-5.10` |
|
|
| `5.15.xxx-android14-6-g...` | android14-5.15 | `android14-5.15` |
|
|
| `6.1.xxx-android14-11-g...` | android14-6.1 | `android14-6.1` |
|
|
| `6.1.xxx-android15-8-g...` | android15-6.1 | `android15-6.1` |
|
|
| `6.6.xxx-android15-...` | android15-6.6 | `android15-6.6` |
|
|
|
|
The `androidXX` part is the GKI generation (kernel branch name), NOT
|
|
the Android version running on the device. A Pixel 7 Pro running
|
|
Android 16 still has an `android13-5.10` kernel because the generation
|
|
is frozen at manufacturing time.
|
|
|
|
## Step 2: Clone the full Pixel kernel tree
|
|
|
|
Google ships a single `android14-6.1` kernel covering all Pixels from 6 to
|
|
9a. The full tree includes kernel sources, prebuilt toolchain, device trees,
|
|
and all out-of-tree Google modules — everything needed with no extra downloads:
|
|
|
|
```bash
|
|
mkdir ~/pixel-kernel && cd ~/pixel-kernel
|
|
|
|
repo init --depth=1 \
|
|
-u https://android.googlesource.com/kernel/manifest \
|
|
-b android-gs-shusky-6.1-android16
|
|
|
|
repo sync -c --no-tags -j$(nproc)
|
|
```
|
|
|
|
Then build once to produce all artifacts:
|
|
|
|
```bash
|
|
./build_shusky.sh # or build_raviole.sh etc. — same kernel image for all
|
|
```
|
|
|
|
Set an env var pointing to the tree root — subsequent steps reference it:
|
|
|
|
```bash
|
|
export PIXEL_KERNEL_TREE=~/pixel-kernel
|
|
```
|
|
|
|
### Useful paths within the tree
|
|
|
|
| Path | Purpose |
|
|
|---|---|
|
|
| `$PIXEL_KERNEL_TREE/aosp/` | Full GKI kernel source tree — browse subsystems, understand exported APIs, read in-tree driver implementations |
|
|
| `$PIXEL_KERNEL_TREE/out/shusky/dist/kernel-headers.tar.gz` | Kernel headers for building against (extract and use as `KDIR`) |
|
|
| `$PIXEL_KERNEL_TREE/out/shusky/dist/vmlinux.symvers` | Symbol versions for `modpost` — use instead of extracting CRCs from device .ko files |
|
|
| `$PIXEL_KERNEL_TREE/out/shusky/dist/System.map` | Symbol addresses for debugging |
|
|
| `$PIXEL_KERNEL_TREE/private/google-modules/` | Reference implementations of out-of-tree modules (Kconfig, Makefile patterns) |
|
|
| `$PIXEL_KERNEL_TREE/prebuilts/clang/host/linux-x86/clang-r487747c/bin` | Bundled clang 17.0.2 — exact compiler used to build the kernel |
|
|
|
|
`raviole/dist/` and `shusky/dist/` contain identical `vmlinux.symvers` and
|
|
`Image` since it's the same kernel build — use either.
|
|
|
|
### Prepare kernel headers for module builds
|
|
|
|
```bash
|
|
mkdir -p ~/pixel-kernel-headers
|
|
tar -xzf $PIXEL_KERNEL_TREE/out/shusky/dist/kernel-headers.tar.gz \
|
|
-C ~/pixel-kernel-headers
|
|
cp $PIXEL_KERNEL_TREE/out/shusky/dist/vmlinux.symvers \
|
|
~/pixel-kernel-headers/Module.symvers
|
|
```
|
|
|
|
Then skip to step 3.
|
|
|
|
### Alternative: shallow clone (non-Pixel or different GKI generation)
|
|
|
|
If you don't need the full tree, clone just the kernel source:
|
|
|
|
```bash
|
|
BRANCH="android13-5.10" # ← replace with your generation from step 1
|
|
|
|
git clone --depth=1 -b $BRANCH \
|
|
https://android.googlesource.com/kernel/common \
|
|
~/kernel-source
|
|
```
|
|
|
|
You'll then need to prepare it manually — see [Preparing a standalone kernel source](#preparing-a-standalone-kernel-source) at the end.
|
|
|
|
---
|
|
|
|
## Step 3: Configure .env
|
|
|
|
The build system uses `direnv` to load `KERNEL_SRC` and `CLANG_DIR` from
|
|
a `.env` file. Copy the example and fill in your paths:
|
|
|
|
```bash
|
|
cd kmod/
|
|
cp .env.example .env
|
|
```
|
|
|
|
Edit `.env`:
|
|
|
|
```bash
|
|
# Pixel kernel tree approach (after extracting headers above):
|
|
KERNEL_SRC=~/pixel-kernel-headers
|
|
CLANG_DIR=~/pixel-kernel/prebuilts/clang/host/linux-x86/clang-r487747c/bin
|
|
|
|
# Or standalone kernel source approach:
|
|
# KERNEL_SRC=~/kernel-source
|
|
# CLANG_DIR=/path/to/clang/bin
|
|
```
|
|
|
|
Allow direnv to load it:
|
|
|
|
```bash
|
|
direnv allow
|
|
```
|
|
|
|
From now on, entering the `kmod/` directory automatically exports
|
|
`KERNEL_SRC` and `CLANG_DIR`. The Makefile reads both from the environment.
|
|
|
|
## Step 4: Build and package
|
|
|
|
```bash
|
|
make # builds vpnhide_kmod.ko
|
|
./build-zip.sh # builds .ko (if needed) + packages vpnhide-kmod.zip
|
|
```
|
|
|
|
## Step 5: Install and test
|
|
|
|
```bash
|
|
adb push vpnhide-kmod.zip /sdcard/Download/
|
|
# Install via KernelSU-Next manager -> Modules -> Install from storage
|
|
# Reboot
|
|
```
|
|
|
|
After reboot, verify:
|
|
|
|
```bash
|
|
# Module loaded?
|
|
adb shell "su -c 'lsmod | grep vpnhide'"
|
|
|
|
# kretprobes registered?
|
|
adb shell "su -c 'dmesg | grep vpnhide'"
|
|
|
|
# UIDs loaded?
|
|
adb shell "su -c 'cat /proc/vpnhide_targets'"
|
|
```
|
|
|
|
Pick target apps via the WebUI in KernelSU-Next manager.
|
|
|
|
---
|
|
|
|
## Preparing a standalone kernel source
|
|
|
|
If you used the shallow clone (non-Pixel path), you need to prepare the
|
|
kernel source before building. If you used the Pixel kernel tree with
|
|
extracted headers, skip this section entirely.
|
|
|
|
### Pull .config from the device
|
|
|
|
```bash
|
|
adb shell "su -c 'gzip -d < /proc/config.gz'" > ~/kernel-source/.config
|
|
```
|
|
|
|
If `/proc/config.gz` doesn't exist, use the GKI defconfig:
|
|
|
|
```bash
|
|
cd ~/kernel-source
|
|
make ARCH=arm64 LLVM=1 CC=$CLANG_DIR/clang gki_defconfig
|
|
```
|
|
|
|
### Generate Module.symvers from device .ko files
|
|
|
|
The Module.symvers file contains CRC checksums for every exported
|
|
kernel symbol. These must match the running kernel exactly
|
|
(CONFIG_MODVERSIONS):
|
|
|
|
```bash
|
|
# Pull all vendor modules from the device
|
|
mkdir -p /tmp/device-modules
|
|
adb shell "su -c 'ls /vendor/lib/modules/*.ko'" | tr -d '\r' | while read ko; do
|
|
adb shell "su -c 'cat $ko'" > "/tmp/device-modules/$(basename $ko)"
|
|
done
|
|
|
|
# Extract CRCs from all modules and build Module.symvers
|
|
for ko in /tmp/device-modules/*.ko; do
|
|
modprobe --dump-modversions "$ko" 2>/dev/null
|
|
done | sort -u -k2 | \
|
|
awk '{printf "%s\t%s\tvmlinux\tEXPORT_SYMBOL\t\n", $1, $2}' \
|
|
> ~/kernel-source/Module.symvers
|
|
|
|
echo "Generated Module.symvers with $(wc -l < ~/kernel-source/Module.symvers) symbols"
|
|
```
|
|
|
|
Expect 3000-5000 symbols. If you get 0, check that `modprobe` is
|
|
installed (`apt install kmod` or `pacman -S kmod`).
|
|
|
|
Alternative: if the device's ROM has a `-kernels` repo on GitHub
|
|
(e.g. `crdroidandroid/android_device_google_shusky-kernels`), you can
|
|
download the .ko files from there instead of pulling from device.
|
|
|
|
### Prepare headers
|
|
|
|
```bash
|
|
cd ~/kernel-source
|
|
|
|
# Create empty ABI symbol list (GKI build expects it)
|
|
touch abi_symbollist.raw
|
|
|
|
# Generate headers
|
|
make ARCH=arm64 LLVM=1 LLVM_IAS=1 \
|
|
CC=$CLANG_DIR/clang LD=$CLANG_DIR/ld.lld AR=$CLANG_DIR/llvm-ar \
|
|
NM=$CLANG_DIR/llvm-nm OBJCOPY=$CLANG_DIR/llvm-objcopy \
|
|
OBJDUMP=$CLANG_DIR/llvm-objdump STRIP=$CLANG_DIR/llvm-strip \
|
|
CROSS_COMPILE=aarch64-linux-gnu- \
|
|
olddefconfig prepare
|
|
```
|
|
|
|
**Common issue:** `make prepare` may fail on `tools/bpf/resolve_btfids`
|
|
due to host clang version mismatch. This is fine — the module can
|
|
still build without BTF. Ignore this error.
|
|
|
|
### Set UTS_RELEASE (vermagic)
|
|
|
|
KernelSU bypasses vermagic checks, so any value works if using KSU.
|
|
For Magisk or manual insmod, the vermagic must match `uname -r`.
|
|
|
|
```bash
|
|
cd ~/kernel-source
|
|
|
|
# For KernelSU users (any placeholder works):
|
|
echo '#define UTS_RELEASE "6.1.0-vpnhide"' > include/generated/utsrelease.h
|
|
echo -n "6.1.0-vpnhide" > include/config/kernel.release
|
|
|
|
# For Magisk users (must match exactly):
|
|
KVER="$(adb shell uname -r | tr -d '\r')"
|
|
echo "#define UTS_RELEASE \"$KVER\"" > include/generated/utsrelease.h
|
|
echo -n "$KVER" > include/config/kernel.release
|
|
```
|
|
|
|
Then set `KERNEL_SRC=~/kernel-source` in your `.env` and proceed to step 4.
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
**`insmod: Exec format error`**
|
|
- Vermagic mismatch (Magisk doesn't bypass it). Set UTS_RELEASE to
|
|
exact `uname -r` value.
|
|
- Or Module.symvers CRCs don't match — re-extract from device .ko files.
|
|
|
|
**`insmod: File exists`**
|
|
- Module already loaded. `rmmod vpnhide_kmod` first.
|
|
|
|
**`modprobe --dump-modversions: no output`**
|
|
- .ko files might be stripped. Try pulling from a different path
|
|
(`/vendor/lib/modules/`, `/system/lib/modules/`,
|
|
`/lib/modules/$(uname -r)/`).
|
|
|
|
**`make prepare` fails on resolve_btfids**
|
|
- Ignore — BTF is optional. The module builds without it.
|
|
|
|
**No `/proc/config.gz`**
|
|
- Use `make gki_defconfig` instead.
|
|
|
|
**kretprobe not firing (ioctl not filtered)**
|
|
- Check `dmesg | grep vpnhide` for registration messages.
|
|
- Check `/proc/vpnhide_targets` has the right UIDs.
|
|
- The target app's UID changes on reinstall — re-resolve via WebUI.
|