- Repeatable
--seal.resetprop --seal A v1 --seal B v2seals 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_updatein 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/kmsgwrites for a window (default 5s, aarch64 only). Use it to confirm which init thread services a property write before you seal it.
- 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/mapsthe page shows as/memfd:resetprop-hook (deleted), mappedPROT_R|Xso init'sprocess:execmemSELinux class is never exercised.
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 invalue[], 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)+1bytes ofvalue[]into aPROP_VALUE_MAXbuffer before it checkskLongFlag, so a length byte at or abovePROP_VALUE_MAXoverflows that buffer and trips a FORTIFY abort.
- Stop init's entire thread group before poking, and resume any partially seized threads when
PTRACE_SEIZEfails 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:
.advancenow emitsldrbwith a#1post-index.
- Reject a persistent-property field whose declared length overflows the record instead of trusting the on-disk length.
- Pin the Rust toolchain to 1.96.0.
- Run the full fmt, clippy, and test wall on every PR.
- 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.
--seal NAME VALUE/-sl: Tier B per-prop hook installed in init via ptrace plus remotemmap. Only the named property freezes; neighbour properties keep updating normally.--seal-arena NAME VALUE/-sla: Tier A arena-levelMAP_PRIVATE|MAP_FIXEDremap in init. Broader blast radius, used as a manual fallback when Tier B refuses (HookInstallFailed,ElfParse,SymbolNotFound).--unseal NAMEand--unseal-arena NAMErevert a specific seal.--sealslists active seal records (name, tier, arena path).- AArch64 only at runtime. Builds for other ABIs return
Error::Unsupportedrather than corrupting init's libc.text. - File-backed hook page at
/data/adb/resetprop-rs/hook-<pid>-<nanos>.bin, unlinked post-mmapsoprocess:execmemSELinux 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.
--if-diffwith positional NAME VALUE: write only when the property exists and the current value differs from VALUE.--if-match NEEDLEwith 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 NAME [VALUE]with optional--timeout SECSblocks until the property either exists (no VALUE) or equals VALUE.- Library API:
PropSystem::wait(name, expected, timeout)returnsSome(value)on match,Noneon timeout.
normalize_serialAPI canonicalises the dirty bit and length field on an existing entry without bumping the serial counter. Mirrors thefix_serialsstep real init runs at boot.- Seal write routes
ro.*properties throughset_stealth(no listeners, so a wake would be the detection signal) and everything else throughset_init(real listeners present, so a missing wake would be the signal). - Hybrid wake policy:
futex_wakeonly fires when listeners exist on the prop. Tracer-busy errors now surface the holding pid for diagnosis.
- 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 inpropdetect-bionicno longer block the shipping binary. - Tagged releases auto-publish:
release.ymlextracts the matching## vX.Y.Zsection fromCHANGELOG.mdvia awk and feeds it tosoftprops/action-gh-releaseas the release body. - Workspace lints clean under Rust 1.95.0 (
clippy::sort_by_key,clippy::collapsible_match).
- 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.
--stealth/-stflag for detection-resistant property writes- Suppresses all three detection vectors: per-property serial (zeroed), global serial bump (no
notify()), and futex wake (skipped) - Combine with
-pfor stealth persist:resetprop -st -p persist.sys.timezone UTC - Library API:
PropSystem::set_stealth(),PropSystem::set_stealth_persist()
--nuke/-nkflag for atomic count-preserving stealth deletion- Pipeline: delete target → generate plausible replacement (harvested from device vocabulary) → stealth-insert with value
"0"→ compact arena - Property count stays identical after nuke; zero forensic traces
- Library API:
PropSystem::nuke(),PropArea::nuke()
--compactflag to defragment arenas after deletes- Slides live allocations forward to fill holes, rebuilding trie offsets in-place
- Library API:
PropSystem::compact(),PropArea::compact()
delete()now prunes orphaned trie leaves after detaching the property- Thorough
wipe()zeroes the full prop_info record (header, long value, name)
- New
propdetectcrate: adversarial validation against real detection heuristics (serial anomalies, count drift, name entropy, enumeration gaps) propdetect-bionicvariant for on-device validation against libc's__system_property_*API
- 50 unit tests + 2 doc-tests (up from 29)
- On-device stress test script: 10 test cases covering stealth, nuke, rapid cycles, and neighbor preservation
- Library crate now publishable to crates.io with full metadata (repository, keywords, categories)
- Crate-level README for the library, focused on API usage and dependency setup
- Public API doc comments on all exported types and methods for docs.rs
- Re-export
Recordfrom crate root so consumers can useresetprop::Recorddirectly - CLI crate also carries crates.io metadata for independent publishing
- Version bump from 0.2.0 to 0.3.1 to align Cargo.toml with tag history
- Persistent property support via
-pand-Pflags -pwrites to both prop_area (memory) and/data/property/persistent_properties(disk)-Preads directly from the persist file without requiring prop_area access- Hand-rolled proto2 encode/decode matching AOSP's
PersistentPropertiesschema - 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)
PersistStorepublic API: load, get, set, delete, listPropSystem::set_persist()andPropSystem::delete_persist()for dual memory+disk writes
- Add
set_init()API for init-style serial writes on ro.* properties write_value_init()zeroes the low-24 counter bits instead of incrementing by 2- Handles both short and long (kLongFlag) property values
- Exposed at
PropArea::set_init()andPropSystem::set_init() - CLI:
--initflag for init-style writes (resetprop --init ro.build.fingerprint "...") - CLI:
--initworks with-ffile load (resetprop --init -f props.txt)
- Stealth overhaul: runtime harvest, plausible hexpatch values, name consistency
privatize()for per-process prop isolation via COW- Harden prop area against corrupt data, add futex wake
- Protect intermediate trie nodes with own properties during hexpatch
- Randomize pool selection and add dot-split generation for harvest
- On-device stress test and stealth verification
- Manual build workflow for fork users
- Initial release: pure Rust property area manipulation
- Get, set, delete, list, foreach operations
hexpatch_deletefor stealth property removal- Trie-based property lookup matching AOSP format
- Cross-compiled for arm64, arm, x86_64, x86