Skip to content

Releases: Enginex0/resetprop-rs

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 18 Jun 16:10

Seal: new capabilities

  • Repeatable --seal. resetprop --seal A v1 --seal B v2 seals several properties in one run, building a single multi-entry lock list inside init instead of spawning one process per prop.
  • --seal NAME --check: dry-run the Tier B install. Resolves __system_property_update in init and validates the splice site without ptrace-writing anything. Single NAME only.
  • --observe-init [--duration SECS]: ptrace init (PID 1) and print its /dev/kmsg writes for a window (default 5s, aarch64 only). Use it to confirm which init thread services a property write before you seal it.

Hook page now backed by memfd

  • The Tier B hook body is mapped into init from an anonymous memfd, not a file under /data/adb/resetprop-rs/. Nothing is written to or unlinked from disk. In /proc/1/maps the page shows as /memfd:resetprop-hook (deleted), mapped PROT_R|X so init's process:execmem SELinux class is never exercised.

Long values: create support

  • set() now creates properties of 92 bytes or more, not just reads and overwrites them. A created long prop is byte-identical to one init writes: kLongFlag, the bionic legacy error string in value[], the self-relative offset to the full value, and a serial length byte equal to the error-message length.
  • Fix: a long prop's serial length byte stores the error-message length, not the value length. Bionic copies (serial>>24)+1 bytes of value[] into a PROP_VALUE_MAX buffer before it checks kLongFlag, so a length byte at or above PROP_VALUE_MAX overflows that buffer and trips a FORTIFY abort.

Seal hardening

  • Stop init's entire thread group before poking, and resume any partially seized threads when PTRACE_SEIZE fails midway. No more half-stopped init.
  • Verify init's identity before the first poke, and reject a re-seal over an init that already carries our trampoline.
  • Read the trampoline back after writing it to confirm the bytes landed.
  • Split the ptrace register layout per architecture (aarch64, arm, x86_64, x86) instead of sharing one struct.
  • Free the remote bootstrap page when an arena step fails, tolerate benign signal-stops while waiting on the tracee, and throttle hook-install retries.
  • Encoder fix: .advance now emits ldrb with a #1 post-index.

Persist

  • Reject a persistent-property field whose declared length overflows the record instead of trusting the on-disk length.

Build and CI

  • Pin the Rust toolchain to 1.96.0.
  • Run the full fmt, clippy, and test wall on every PR.

Testing

  • 165 library unit tests (up from 133 at v0.5.0).
  • Per-arch ptrace smoke fixtures plus an independent A64 assembler oracle that re-derives the hook body from canonical opcodes.
  • Long-value create roundtrip test that asserts the serial length byte stays below PROP_VALUE_MAX.

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 17 May 15:10

Seal (Two-Tier Property Locking)

  • --seal NAME VALUE / -sl: Tier B per-prop hook installed in init via ptrace plus remote mmap. Only the named property freezes; neighbour properties keep updating normally.
  • --seal-arena NAME VALUE / -sla: Tier A arena-level MAP_PRIVATE|MAP_FIXED remap in init. Broader blast radius, used as a manual fallback when Tier B refuses (HookInstallFailed, ElfParse, SymbolNotFound).
  • --unseal NAME and --unseal-arena NAME revert a specific seal. --seals lists active seal records (name, tier, arena path).
  • AArch64 only at runtime. Builds for other ABIs return Error::Unsupported rather than corrupting init's libc.text.
  • File-backed hook page at /data/adb/resetprop-rs/hook-<pid>-<nanos>.bin, unlinked post-mmap so process:execmem SELinux class is never exercised.
  • In-session only: seals do not persist across reboots, SystemProperties::Reload, or init restart. Re-apply on every boot.
  • Library API: PropSystem::seal, PropSystem::unseal, PropSystem::seal_arena, PropSystem::unseal_arena, PropSystem::seals.

Conditional Property Primitives

  • --if-diff with positional NAME VALUE: write only when the property exists and the current value differs from VALUE.
  • --if-match NEEDLE with positional NAME VALUE: write only when the current value equals NEEDLE and differs from VALUE.
  • --delete-if-exist NAME: delete only when the property is present, exit 0 on absent.
  • All three skip absent properties and short-circuit equal-value writes so the per-prop serial never bumps spuriously.
  • Library API: PropSystem::set_if_diff, PropSystem::set_if_match.

Wait For Value

  • --wait NAME [VALUE] with optional --timeout SECS blocks until the property either exists (no VALUE) or equals VALUE.
  • Library API: PropSystem::wait(name, expected, timeout) returns Some(value) on match, None on timeout.

Bionic-Correct Serial Discipline

  • normalize_serial API canonicalises the dirty bit and length field on an existing entry without bumping the serial counter. Mirrors the fix_serials step real init runs at boot.
  • Seal write routes ro.* properties through set_stealth (no listeners, so a wake would be the detection signal) and everything else through set_init (real listeners present, so a missing wake would be the signal).
  • Hybrid wake policy: futex_wake only fires when listeners exist on the prop. Tracer-busy errors now surface the holding pid for diagnosis.

Build And CI Hardening

  • New Error::Unsupported(String) variant for arch-gated features.
  • Release matrix runs with fail-fast: false. One failed ABI no longer cancels the other three in flight.
  • Release-artifact build scoped to cargo build -p resetprop-cli. Toolchain quirks in propdetect-bionic no longer block the shipping binary.
  • Tagged releases auto-publish: release.yml extracts the matching ## vX.Y.Z section from CHANGELOG.md via awk and feeds it to softprops/action-gh-release as the release body.
  • Workspace lints clean under Rust 1.95.0 (clippy::sort_by_key, clippy::collapsible_match).

Testing

  • 133 library unit tests (up from 50 at v0.4.0). Five integration test binaries cover ptrace primitives, Tier A and Tier B child isolation, ELF fixture parsing, plus the existing doc tests.
  • New seal coverage: A64 encoder vectors versus canonical opcodes, hook body splice equivalence, lock-list capacity envelope, PT_DYNAMIC duplicate-tolerant parsing.
  • Device-side stress: Tests 21 and 22 exercise seal under SELinux denials and ksu_props bypass probes.

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 24 Mar 19:36
feat(core): add context, appcompat, bionic, and wait

Property area lookup now parses the property_info binary trie for
O(depth) resolution instead of linear scanning all areas. Falls back
to text property_contexts on Android 8-9, then to linear scan if
neither is available.

Android 14+ appcompat_override areas are detected and dual-written
automatically, preserving the write mode (stealth, init, standard)
on the mirror area.

Bionic __system_property_* functions are loaded via dlsym as a read
fallback when direct mmap access is unavailable. Entire bionic module
is cfg-gated to Android targets only.

New --wait flag blocks until a property exists or matches a value,
with optional --timeout. Uses bionic __system_property_wait when
available, falls back to futex_wait on the property serial or the
global serial for properties that don't exist yet.

New modules: context.rs (643 LOC), bionic.rs (202 LOC),
appcompat.rs (52 LOC), wait.rs (115 LOC). All 60 tests pass.

v0.3.1

Choose a tag to compare

@github-actions github-actions released this 23 Mar 23:01
build: add x86_64 and x86 Android targets

Extends from 2 ABIs to all 4 Android ABIs so emulator users
and x86 devices get prebuilt binaries too.

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 22 Mar 22:57

v0.3.0

  • Persistent property support via -p and -P flags
  • -p writes to both prop_area (memory) and /data/property/persistent_properties (disk)
  • -P reads directly from the persist file without requiring prop_area access
  • Hand-rolled proto2 encode/decode matching AOSP's PersistentProperties schema
  • Atomic write with SELinux xattr preservation, matching AOSP's temp+rename+fsync pattern
  • Legacy format read support for pre-Android 9 devices (one file per property)
  • PersistStore public API: load, get, set, delete, list
  • PropSystem::set_persist() and PropSystem::delete_persist() for dual memory+disk writes

v0.2.1

Choose a tag to compare

@github-actions github-actions released this 22 Mar 20:30
v0.2.1: add set_init() and --init CLI for init-style serial writes

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 10 Mar 22:43

resetprop-rs v0.2.0 — Stealth Harvest Overhaul

Major hexpatch-delete rework. Replacement names now come from the device's own property vocabulary instead of a static dictionary.

What changed

Runtime segment harvesting — hexpatch scans all properties on the device and builds a per-length word pool. Replacements are drawn from segments that already exist on that specific device, making them indistinguishable from real properties.

Randomized selection — pool picks use OS-seeded entropy instead of deterministic first-match. Same device, different mangled names each run.

Dot-split compound generation — when harvest pool and static dict are exhausted, the fallback now joins short stems with dots (e.g. cfg.hw.svc) instead of cyclic underscore patterns. Produces more natural-looking segment names for long replacements.

Plausible value — mangled properties now show value 0 instead of empty string. Serial field encodes length=1 with counter bits preserved. Indistinguishable from a boot-time property via the public API.

Name consistency fix — trie segments and prop_info name are now written from the same source vector. Previously they could diverge, creating a detectable mismatch between __system_property_find and __system_property_foreach.

Shared segment protection — trie nodes that have children with their own properties are no longer renamed, preventing corruption of sibling properties.

3-tier replacement strategy

  1. Harvest pool (device vocabulary, randomized)
  2. Static dictionary (~95 words, lengths 1-16)
  3. Dot-split compound (stem combinations with dots)

Testing

  • 29 unit tests (mock property areas)
  • 20 on-device tests (OnePlus, Android 14, KSU Next) — all pass
  • Verified against Magisk resetprop: property counts match within 3

Binaries

ABI File
arm64-v8a resetprop-arm64-v8a
armeabi-v7a resetprop-armeabi-v7a

Static, stripped, zero runtime dependencies beyond libc.

v0.1.0

Choose a tag to compare

@Enginex0 Enginex0 released this 10 Mar 14:41

resetprop-rs v0.1.0

Pure Rust Android property manipulation. No Magisk dependency.

Features

  • Get/Set/Delete — direct mmap writes, bypasses property_service
  • Hexpatch Delete — trie-aware name destruction with dictionary words, serial preserved
  • Batch Load-f flag loads name=value pairs from file
  • Library cratePropArea and PropSystem with typed errors

Hexpatch Stealth

Unlike magiskboot's blind byte substitution, resetprop-rs walks the actual property prefix trie:

  • Segments renamed with same-length dictionary words (thermal, display, sensor, codec)
  • Trie structure intact — no enumeration gaps
  • Serial counters preserved — no NativeTest detection
  • Shared prefixes (ro, persist) automatically skipped

Binaries

ABI Size
arm64-v8a 322 KB
armeabi-v7a 237 KB

Static, stripped, zero runtime dependencies beyond libc.

Testing

  • 16 off-device unit tests (mock property areas)
  • 17 on-device tests (Redmi, Android 14, KSU Next) — all pass
  • Verified against Magisk resetprop: property count and values match