Skip to content

[Bug] Legacy module dex obfuscation leaves Dex2C JNI signatures unrewritten — RegisterNatives fails on 3080, works on 3116 #1054

Closed

Description

@JeromeLi

Steps to reproduce/复现步骤

On Android 17 with the v2.2 (3080) release, a Dex2C-hardened legacy module
(cn.alhs.FQKill 1.0.8) fails to load. The legacy de.robv.android.xposed.*
classes are obfuscated at runtime, but the JNI signature strings hardcoded in
the module's native code are not rewritten, so RegisterNatives cannot match
and initZygote ends up with no implementation.

Installing a 3116 canary build on the same device makes the same module load
successfully. Nothing else changed (same module APK, same target app version,
same root setup and module list).

I could not find an existing issue for this. #821 covers a different problem —
see Notes.

Steps to reproduce

  1. Install the 3080 release.
  2. Install cn.alhs.FQKill 1.0.8, enable it, scope com.dragon.read.
  3. Cold start com.dragon.read.

Note: to observe this at all you first need a workaround for the JNI
EnsureModifiable abort described in #821 — the module writes a
runtime-injected static final field from its own native code during
<clinit>. In my setup a small Zygisk module (fqkill_artpatch) neutralises
that call inside the target process only. Without it the process aborts before
this issue becomes visible.

Actual behavior

On 3080 the same cold start fails to register the natives and the module is
dropped:

V/VectorLegacyBridge  Loading class cn.alhs.FQKill.MainHook
E/com.dragon.read     Failed to register native method
    MainHook.AlhsHookOne(Lde/robv/android/xposed/callbacks/XC_LoadPackage$LoadPackageParam;)V
E/com.dragon.read     No implementation found for void MainHook.initZygote(...)
E/VectorLegacyBridge  Failed to load class cn.alhs.FQKill.MainHook
java.lang.UnsatisfiedLinkError: ... - is the library loaded, e.g. System.loadLibrary?

The module dumps its class right before failing, and the runtime interface is
dDB.M.EnPvpLMa.AMxAkGI.IXposedHookLoadPackage — i.e. the legacy API classes
were obfuscated, while the JNI signature string still carries the original
Lde/robv/android/xposed/... descriptor. The framework cannot match the two and
the native methods are never bound.

Expected behavior

The legacy module's native methods are registered against the (possibly
obfuscated) Xposed API classes at runtime, initZygote has an implementation,
and the module loads with its hooks active — as happens on 3116, and as happens
with the same module version on a second device running Android 10.

V/VectorLegacyBridge  Loading class cn.alhs.FQKill.MainHook
I/VectorLegacyBridge  XposedModule:☞loadLibrary成功

No registration failure; the module's hooks install and work (verified
functionally: in-app ads removed, listening-time limit lifted).

Environment

Device / OS

  • Pixel 6 (oriole)
  • Android 17, build CP3A.261005.002.A1
  • Kernel 6.1.162-android14-11-g9dd05ae3a1de-ab16261587 (GKI)

Root / injection

  • KernelSU-Next 3.4.2 (LKM, boot image patched with ksud)
  • Zygisk Next 1.5.2 (864-a4a72ce-release)

Framework

  • Failing: Vector v2.2 3080-88f8e1fa-JingMatrix-Vector (current official release)
  • Working: Vector v2.2 3116-56904811-JingMatrix-Vector

Xposed modules installed (same set in both runs)

Package Version Status Scope
cn.alhs.FQKill 1.0.8 enabled com.dragon.read
com.tsng.hidemyapplist 3.6.1.r462.4524dde enabled (unrelated apps)
com.winnersonx.httpinterceptor 2.1.6 disabled —
dev.operit.fanqiehook 0.9.1 disabled —
io.github.mianxyz.fanqiecustomizer 5.0.5 disabled —
just.trust.me.pro 1.0.6 disabled —
me.kavishdevar.librepods 0.1.0-rc.4 disabled —

cn.alhs.FQKill is the unmodified APK with its original signature. Its scope is
exactly one app: com.dragon.read/0.

Root modules installed (KernelSU-Next, same set in both runs)

Module Version Status
zygisk_vector v2.2 (3080 → 3116, the only variable) enabled
zygisksu (Zygisk Next) 1.5.2 (864-a4a72ce-release) enabled
zygisk_shamiko v1.2.5 (414) enabled
playintegrityfix v4.9-inject-s enabled
trustusercerts v1.3 enabled
compatible-magisk-mirroring v1.1.3 enabled
usb-samplerate-unlocker v1.5.11 enabled
5G_enabler 1.0 enabled
fqkill_artpatch v0.1 enabled — my local workaround for the #821 abort (see Steps)
tricky_store v1.4.1 (245-72b2e84-release) disabled
zygisk_lsposed v1.9.3_mod (7244) disabled
cert-fixer v1.1 disabled
MoveCertificate v1.5.7 disabled
magisk-frida 17.9.1-1 disabled
MagiskHidePropsConf v6.1.2-v137 disabled

Target app

  • com.dragon.read 7.1.1.32 (versionCode 71132, targetSdk 35) — identical in
    both runs

Comparison (only the framework build differs)

3080 3116
Module APK FQKill 1.0.8, unmodified identical
Target app com.dragon.read 7.1.1.32 identical
Root / Zygisk KernelSU-Next 3.4.2 + Zygisk Next 1.5.2 identical
Xposed / root module lists see Environment identical
Obfuscation active yes yes
Result Failed to register native method → Failed to load class loads, hooks work

Logs

Both messages quoted above come from the framework's own log files, written by
the same process that fails:

  • /data/adb/lspd/log/modules_<timestamp>.log — Loading class ...,
    Failed to register native method, Failed to load class and the
    UnsatisfiedLinkError stack
  • /data/adb/lspd/log/verbose_<timestamp>.log — per-process verbose log
    (includes the module's own class dump showing
    dDB.M.EnPvpLMa.AMxAkGI.IXposedHookLoadPackage, and
    Fetched obfuscation map with 8 entries)
  • logcat — the same lines mirrored by VectorLegacyBridge

Excerpts are inlined above; I can attach the full log files (both builds) if
that helps.

Notes

  • Obfuscation is active on both builds (Fetched obfuscation map with 8 entries), and the same module version also loads fine on a second device
    running Android 10 with 3116 — so obfuscation itself is not the problem.
  • Write static final fields again on Android 17 #821 documents the JNI-side EnsureModifiable abort as deliberately out of
    scope, which is fine for this report. This issue is only about the legacy dex
    obfuscation not rewriting JNI signature strings.
  • enable_dex_obfuscate exists in modules_config.db, but the CLI rejects that
    key and the manager has no matching setting, so it cannot be turned off as a
    workaround.
  • I could not identify the commit responsible for the difference between 3080
    and 3116, so please treat "3116 works / 3080 fails" as an observation rather
    than a bisect result.
  • Cross-check for the Write static final fields again on Android 17 #821 side: with my workaround disabled, the target process
    dies with Cannot set private static final field short[] cn.alhs.FQKill.MainHook.short, exactly as Write static final fields again on Android 17 #821 predicts for a JNI write to a
    non-android.os.Build static final field.

Request

If 3116 already contains a fix for this, could it be included in the next
release? Users on the current release cannot run Dex2C-hardened legacy modules
on Android 17.

Expected behaviour/预期行为

On 3080 the same cold start fails to register the natives and the module is
dropped:

V/VectorLegacyBridge  Loading class cn.alhs.FQKill.MainHook
E/com.dragon.read     Failed to register native method
    MainHook.AlhsHookOne(Lde/robv/android/xposed/callbacks/XC_LoadPackage$LoadPackageParam;)V
E/com.dragon.read     No implementation found for void MainHook.initZygote(...)
E/VectorLegacyBridge  Failed to load class cn.alhs.FQKill.MainHook
java.lang.UnsatisfiedLinkError: ... - is the library loaded, e.g. System.loadLibrary?

The module dumps its class right before failing, and the runtime interface is
dDB.M.EnPvpLMa.AMxAkGI.IXposedHookLoadPackage — i.e. the legacy API classes
were obfuscated, while the JNI signature string still carries the original
Lde/robv/android/xposed/... descriptor. The framework cannot match the two and
the native methods are never bound.

Actual behaviour/实际行为

The legacy module's native methods are registered against the (possibly
obfuscated) Xposed API classes at runtime, initZygote has an implementation,
and the module loads with its hooks active — as happens on 3116, and as happens
with the same module version on a second device running Android 10.

V/VectorLegacyBridge  Loading class cn.alhs.FQKill.MainHook
I/VectorLegacyBridge  XposedModule:☞loadLibrary成功

No registration failure; the module's hooks install and work (verified
functionally: in-app ads removed, listening-time limit lifted).

Xposed Module List/Xposed 模块列表

**Xposed modules installed** (same set in both runs)

| Package | Version | Status | Scope |
| --- | --- | --- | --- |
| `cn.alhs.FQKill` | 1.0.8 | enabled | `com.dragon.read` |
| `com.tsng.hidemyapplist` | 3.6.1.r462.4524dde | enabled | (unrelated apps) |
| `com.winnersonx.httpinterceptor` | 2.1.6 | disabled | — |
| `dev.operit.fanqiehook` | 0.9.1 | disabled | — |
| `io.github.mianxyz.fanqiecustomizer` | 5.0.5 | disabled | — |
| `just.trust.me.pro` | 1.0.6 | disabled | — |
| `me.kavishdevar.librepods` | 0.1.0-rc.4 | disabled | — |

`cn.alhs.FQKill` is the unmodified APK with its original signature. Its scope is
exactly one app: `com.dragon.read/0`.

Root implementation/Root 方案

KernelSU-Next 3.4.2 + Zygisk Next 1.5.2

System Module List/系统模块列表

**Root modules installed** (KernelSU-Next, same set in both runs)

| Module | Version | Status |
| --- | --- | --- |
| `zygisk_vector` | v2.2 (3080 → 3116, the only variable) | enabled |
| `zygisksu` (Zygisk Next) | 1.5.2 (864-a4a72ce-release) | enabled |
| `zygisk_shamiko` | v1.2.5 (414) | enabled |
| `playintegrityfix` | v4.9-inject-s | enabled |
| `trustusercerts` | v1.3 | enabled |
| `compatible-magisk-mirroring` | v1.1.3 | enabled |
| `usb-samplerate-unlocker` | v1.5.11 | enabled |
| `5G_enabler` | 1.0 | enabled |
| `fqkill_artpatch` | v0.1 | enabled — my local workaround for the #821 abort (see Steps) |
| `tricky_store` | v1.4.1 (245-72b2e84-release) | disabled |
| `zygisk_lsposed` | v1.9.3_mod (7244) | disabled |
| `cert-fixer` | v1.1 | disabled |
| `MoveCertificate` | v1.5.7 | disabled |
| `magisk-frida` | 17.9.1-1 | disabled |
| `MagiskHidePropsConf` | v6.1.2-v137 | disabled |

**Target app**

- `com.dragon.read` 7.1.1.32 (`versionCode` 71132, `targetSdk` 35) — identical in
  both runs

Vector version/Vector 版本

3080

Android version/Android 版本

Android 17, build CP3A.261005.002.A1

Version requirement/版本要求

Logs/日志

Both messages quoted above come from the framework's own log files, written by
the same process that fails:

  • /data/adb/lspd/log/modules_<timestamp>.log — Loading class ...,
    Failed to register native method, Failed to load class and the
    UnsatisfiedLinkError stack
  • /data/adb/lspd/log/verbose_<timestamp>.log — per-process verbose log
    (includes the module's own class dump showing
    dDB.M.EnPvpLMa.AMxAkGI.IXposedHookLoadPackage, and
    Fetched obfuscation map with 8 entries)
  • logcat — the same lines mirrored by VectorLegacyBridge

Excerpts are inlined above; I can attach the full log files (both builds) if
that helps.

Activity

  1. JingMatrix commented on Nov 5, 2026

    @JingMatrix
    Owner

    See #670. The workaround for you is to use the debug builds of Vector.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions