Pure Rust Android Property Manipulation
Get. Set. Delete. Hexpatch. No Magisk required.
Note
resetprop-rs is a standalone reimplementation of Magisk's resetprop in pure Rust. It does not depend on Magisk, forked bionic, or any custom Android symbols. Works with any root solution β KSU, Magisk, APatch, or bare su.
Android system properties live in mmap'd shared memory at /dev/__properties__/. Each file is a 128KB arena containing a prefix trie with BST siblings β the same data structure since Android 10.
Magisk's resetprop can manipulate these, but it's locked into Magisk's build system β it depends on a forked bionic with custom symbols (__system_property_find2, __system_property_delete, etc.) that don't exist in stock Android. You can't extract it as a standalone binary.
resetprop-rs reimplements the entire property area format in pure Rust. No bionic symbols. No Magisk dependency. Ships as a ~320KB static binary and an embeddable library crate.
It also introduces --hexpatch-delete β a stealth operation that no existing tool provides.
π Truly Standalone β Zero runtime dependencies. No Magisk, no forked libc, no JNI. A single static binary that works on any rooted Android device.
π₯· Hexpatch Delete β Instead of detaching trie nodes (detectable by enumeration gaps), overwrites property name bytes with realistic dictionary words. Trie structure stays intact. Serial counters preserved. Invisible to __system_property_foreach.
π¦ Embeddable Library β resetprop crate with typed errors, no anyhow. Drop it into your Rust project and manipulate properties programmatically.
β‘ Tiny Footprint β ~320KB ARM64, ~240KB ARMv7. Hand-rolled CLI parser, panic=abort, LTO, single codegen unit. Only dependency: libc.
π§ͺ Tested Off-Device β 29 unit tests against synthetic property areas. Verified: get, set, overwrite, delete, hexpatch, trie integrity, serial preservation, name consistency, boundary conditions.
Property Operations
- Get β single property or list all
- Set β direct mmap write, bypasses
property_service - Set (init-style) β
--initzeros the serial counter, mimicking howinitwritesro.*props at boot - Delete β trie node detach + value/name wipe
- Hexpatch Delete β dictionary-based name destruction, serial-preserving
- Persistent Properties β
-pwrites to both memory and/data/property/persistent_propertieson disk;-Preads directly from the persist file - Batch Load β
-fflag loadsname=valuepairs from file - Privatize β remap areas as
MAP_PRIVATEfor per-process COW isolation
Library API
-
PropAreaβ single property file: open, get, set, delete, hexpatch, foreach -
PropSystemβ multi-file scan across/dev/__properties__/ -
PersistStoreβ read/write the on-disk persistent property store (protobuf + legacy format) - Typed errors β
NotFound,AreaCorrupt,PermissionDenied,AreaFull,Io,ValueTooLong,PersistCorrupt - RO fallback β automatically falls back to read-only when write access is denied
Format Support
- Short values β β€91 bytes, inline in prop_info
- Long values β Android 12+, >92 bytes via self-relative arena offset
- Serial protocol β spin-wait on dirty bit, verification loop for concurrent reads
- Length-first comparison β matches AOSP's
cmp_prop_nameexactly
Stealth
- Runtime harvest β replacement segments drawn from the device's own property vocabulary (unfingerprintable)
- Randomized selection β OS-seeded entropy picks different names each run
- 3-tier fallback β harvest pool β static dictionary (~95 words) β dot-split compound generation
- Plausible value β mangled properties show value
0instead of empty string - Name consistency β trie segments and prop_info name written from same source (no cross-validation mismatch)
- Length-bucketed β replacement is always exact same byte length as original
- Shared segment detection β skips renaming prefixes used by other properties
- No serial bump β preserves counter bits, avoiding NativeTest detection
Important
Write operations (set, delete, hexpatch) require root access with appropriate SELinux context. Read operations (get, list) work for any user since property files are world-readable.
You need:
- Android 10 or above
- Root access (KernelSU, Magisk, APatch, or equivalent)
- ARM64, ARMv7, x86_64, or x86 device/emulator
- Download the binary for your architecture from Releases
- Push to device:
adb push resetprop-arm64-v8a /data/local/tmp/resetprop-rs adb shell chmod +x /data/local/tmp/resetprop-rs
- Run with root:
adb shell su -c /data/local/tmp/resetprop-rs
Warning
Do NOT name the binary resetprop if you're on KernelSU or Magisk. Both ship their own resetprop in /data/adb/ksu/bin/ or /sbin/, and your shell will resolve to theirs instead of this one. Either:
- Name it
resetprop-rs(recommended) - Use the full path:
/data/local/tmp/resetprop-rs - Place it earlier in
$PATHthan the KSU/Magisk binary
If you bundle this binary in a KSU module or boot script, always call it by full path:
RESETPROP="/data/adb/modules/mymodule/resetprop-rs"
$RESETPROP -n ro.build.type user
$RESETPROP --hexpatch-delete ro.lineage.versionDo not rely on bare resetprop in scripts. It will silently use KSU/Magisk's version, which lacks --hexpatch-delete, --init, -p, and -P.
resetprop-rs [OPTIONS] [NAME] [VALUE]
# List all properties
resetprop-rs
# Get a single property
resetprop-rs ro.build.type
# List persistent properties from disk (/data/property/)
resetprop-rs -P
# Get a single persistent property from disk
resetprop-rs -P persist.sys.timezone# Set a property (direct mmap write, bypasses property_service)
resetprop-rs -n ro.build.type user
# Set with zeroed serial counter (mimics how init writes ro.* at boot)
resetprop-rs --init ro.build.fingerprint "google/raven/raven:14/..."
# Set and persist to disk (survives reboot)
resetprop-rs -p persist.sys.timezone UTC
# Batch set from file (one name=value per line, # comments allowed)
resetprop-rs -f props.txt
# Batch set with init-style serial
resetprop-rs --init -f props.txt# Delete (detaches trie node, zeroes value and name)
resetprop-rs -d ro.debuggable
# Delete from both memory and persist file
resetprop-rs -p -d persist.sys.timezone
# Stealth delete (replaces name with dictionary words, keeps trie intact)
resetprop-rs --hexpatch-delete ro.lineage.version| Flag | Description |
|---|---|
-n |
No-op (compatibility with Magisk's resetprop) |
--init |
Zero the serial counter when writing (mimics init for ro.* properties) |
-p |
Persist mode: write/delete affects both memory and /data/property/ on disk |
-P |
Read from the persist file on disk, not from the mmap'd property area |
-d NAME |
Delete a property |
--hexpatch-delete NAME |
Stealth delete with dictionary-based name replacement |
-f FILE |
Load name=value pairs from a file |
--dir PATH |
Use a custom property directory instead of /dev/__properties__/ |
-v |
Verbose output |
-h, --help |
Show help |
Add to your Cargo.toml:
[dependencies]
resetprop = "0.3"Or from git:
[dependencies]
resetprop = { git = "https://github.com/Enginex0/resetprop-rs" }use resetprop::{PropSystem, PersistStore};
let sys = PropSystem::open()?;
// read
if let Some(val) = sys.get("ro.build.type") {
println!("{val}");
}
// write (direct mmap, bypasses property_service)
sys.set("ro.build.type", "user")?;
// write with zeroed serial (mimics init for ro.* props)
sys.set_init("ro.build.fingerprint", "google/raven/...")?;
// write to both memory and disk
sys.set_persist("persist.sys.timezone", "UTC")?;
// delete
sys.delete("ro.debuggable")?;
sys.delete_persist("persist.sys.timezone")?;
// stealth delete
sys.hexpatch_delete("ro.lineage.version")?;
// enumerate
for (name, value) in sys.list() {
println!("[{name}]: [{value}]");
}
// read persist file directly
let store = PersistStore::load()?;
for record in store.list() {
println!("{}: {}", record.name, record.value);
}Requires Android NDK for cross-compilation:
export ANDROID_NDK_HOME=/path/to/ndk
./build.shOutputs stripped binaries to out/:
| ABI | Binary |
|---|---|
| arm64-v8a | resetprop-arm64-v8a |
| armeabi-v7a | resetprop-armeabi-v7a |
| x86_64 | resetprop-x86_64 |
| x86 | resetprop-x86 |
The build uses opt-level=s, LTO, panic=abort, strip, and single codegen unit for minimal binary size.
No NDK? Fork the repo and go to Actions β Build β Run workflow β GitHub builds all four ABIs for you. Download them from the workflow artifacts.
Each file in /dev/__properties__/ is a 128KB mmap'd arena:
βββββββββββββββββββββββββββββββββββββββ
β Header (128 bytes) β
β [0x00] bytes_used: u32 β
β [0x04] serial: AtomicU32 β
β [0x08] magic: 0x504f5250 "PROP" β
β [0x0C] version: 0xfc6ed0ab β
βββββββββββββββββββββββββββββββββββββββ€
β Arena (bump-allocated, append-only) β
β ββ prop_trie_node βββββββββββ β
β β namelen(4) prop(4) left(4)β β
β β right(4) children(4) β β
β β name[namelen+1] (aligned) β β
β βββββββββββββββββββββββββββββ β
β ββ prop_info ββββββββββββββββ β
β β serial(4) value[92] β β
β β name[] (full dotted name) β β
β βββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββ
Property names split on dots into a prefix trie. Each trie level uses a BST for siblings, compared length-first then lexicographically (not standard strcmp).
Standard delete detaches the trie node β but apps enumerating properties can detect the gap. Hexpatch delete takes a different approach:
Before: ro.lineage.version = "18.1"
After: ro.codec.charger = "0"
- Harvest all property segments from the device into a length-bucketed pool
- Walk the trie path, collecting each segment's node offset
- For each non-shared segment, pick a same-length replacement (harvest β dict β dot-split compound)
- Overwrite name bytes in-place, randomized selection each run
- Write mangled name to prop_info from the same chosen segments (single source of truth)
- Set value to
0with correct serial encoding β indistinguishable from a boot-time property - Do not bump the serial counter β avoids NativeTest serial-monitoring detection
| Status | |
|---|---|
| Android | 10 β 15 |
| Architecture | ARM64, ARMv7, x86_64, x86 |
| Value format | Short (β€91B) + Long (Android 12+, >92B) |
| Root | KernelSU, Magisk, APatch, any su |
- hiking90/rsproperties β Rust property area parser that proved the approach viable (Apache-2.0)
- topjohnwu/Magisk β original
resetpropand the forked bionic approach - Pixel-Props/sensitive-props β property spoofing reference that informed the target property list
- AOSP bionic β canonical property area format specification
- frknkrc44 β aspiration, proposed building this project
- fatalcoder524 β stealth strategy design and testing
This project is licensed under the MIT License.
π§ Because the best property manipulation is the one init never noticed.