Patching Smali Files
Markdown--- name: patching-smali-files description: Reads, analyzes, modifies, and validates Android .smali files based on natural language instructions. Produces error-free patched smali with round-trip validation (smali→dex→baksmali→smali→dex), a detailed patch report, metadata, and validation logs. Use when a user needs to inject code, alter method behavior (e.g., force a return value, bypass a check), or otherwise modify decompiled Android bytecode while guaranteeing the result assembles and disassembles cleanly. ---
Given smali file(s) + an instruction like "force isPremium() to always return true":
- Locate the target method, record its
.localsand register usage. - Replace/insert the minimal smali needed, bumping
.localsif new registers are used. - Round-trip validate:
smali assemble→baksmali disassemble→smali assembleagain, checking for 0 errors/warnings. - Package
/patched/,/originals/,patch_report.txt,metadata.json,validation_logs/intopatched_output.zip. - Only deliver the ZIP if validation status is
PASSED.
Progress:
- Parse instruction → map to action (inject / replace / remove / change-return / add-hook)
- Backup originals, record checksums and tool versions
- Static analysis of target method(s): signature,
.locals, live registers, control flow, exception handlers - Generate patch snippet; compute register needs; insert without breaking labels/control flow
- Round-trip validate (assemble → disassemble → assemble); diff patched regions for semantic equivalence
- Retry fixes on failure (up to
max_attempts), logging each attempt - Package outputs; write patch report
- Deliver ZIP only if PASSED; otherwise deliver report + logs + remediation steps
1. Parse & Normalize Instruction
Map verbs to operations:
- "inject", "add", "log" →
inject - "force return", "always return", "make X return Y" →
replace - "remove check", "bypass", "skip validation" →
replace-conditionalorremove - "hook", "call before/after" →
add-hook
If the target method is ambiguous, prefer: more specific name match > higher visibility (public > protected > private) > closest matching signature. Document the assumption in the report. For high-risk instructions ("remove all checks", "disable all validation"), require explicit confirmation before proceeding.
2. Pre-check & Setup
- Verify file integrity (checksum each input file).
- Record exact tool versions used (smali/baksmali/java) in
metadata.json. - Copy originals into
/originals/untouched.
3. Static Analysis
For each target method:
- Parse
.locals, parameter registers (p0..pN), and whichvregisters are live at the insertion point. - Build a simple control-flow view: labels, branches,
invoke-*,return*statements,.catch/.catchallblocks. - Pinpoint the exact insertion point relative to labels/instructions — never guess line numbers blindly.
4. Patch Generation & Insertion
- Use the highest-numbered unused register for new temporaries — never reuse a register that's live later in the method.
- If new registers are needed, increment
.localsby exactly the number required (minimal delta). - Preserve label uniqueness; if inserting new labels, prefix them uniquely (e.g.,
:patch_inject_1) to avoid collisions. - Never place instructions after an executed
returnon the same control-flow path. - Run static sanity checks: balanced
.enddirectives, valid type descriptors, no duplicate labels, no orphaned branches.
5. Round-Trip Validation (Mandatory)
smali assemble -o out.dex patched_smali_dir/
baksmali disassemble -o roundtrip_smali_dir/ out.dex
smali assemble -o roundtrip.dex roundtrip_smali_dir/
- Capture stdout/stderr of every command into
validation_logs/{step}.log. - Diff patched regions against the round-tripped smali — confirm semantic equivalence (register naming/label renaming by baksmali is fine; logic must match).
- On any error/warning: attempt an automated fix (see Troubleshooting), re-run validation, up to
max_attemptstimes, logging every attempt distinctly (attempt_1.log,attempt_2.log, ...).
6. Smoke Tests (if available)
Run any provided unit tests or lightweight static verifiers against the patched dex; log results.
7. Packaging & Reporting
Build patched_output.zip:
/patched/...
/originals/...
patch_report.txt
metadata.json
validation_logs/
Write patch_report.txt per the Report Template below.
8. Delivery
- If
status == PASSED: deliver the full ZIP. - If
status == FAILED: deliver onlypatch_report.txt+validation_logs/with remediation steps — never ship a ZIP containing errors/warnings.
Example 1 — Inject logging
Input: "inject debug_log.txt write on onCreate"
Output: Insert a FileWriter/invoke-* block at the start of onCreate(Landroid/os/Bundle;)V, before any early return. .locals incremented minimally (e.g., 2→4) to hold the writer and string refs. Validation: PASSED, 0 errors, 0 warnings.
Example 2 — Force return value Input: "force isPremium() to always return true" Output: Replace method body:
SMALI.method public isPremium()Z .locals 1 const/4 v0, 0x1 return v0 .end method
Reasoning logged: original body and all branches removed since return is unconditional; no live registers to preserve.
Example 3 — Bypass a license check
Input: "bypass license check in checkLicense(Ljava/lang/String;)Z"
Output: Replace the conditional branch that jumps to a "fail" label with an unconditional path to the "success" path (or a direct const/4 v0, 0x1 / return v0 if the whole method can be simplified). Old labels documented as removed/replaced. Assumption logged: "success" label identified as the block preceding a return with truthy value.
=== Patch Report ===
job_id, timestamp, user_instruction, tool_versions, allow_creative_solution
--- Per File/Method ---
file_path, method_signature
before_snippet (3-8 lines) / after_snippet (3-8 lines)
locals_before -> locals_after
registers_used: [...]
reasoning: ...
validation_summary: attempts=N, errors=0, warnings=0
assumptions: [...]
--- Overall ---
status: PASSED | FAILED
attempts_summary
diff_summary: lines_added, lines_removed
checksums: original_files, patched_files, final_zip
remediation_steps (if FAILED)
smali assemble -o {out.dex} {patched_smali_dir}
baksmali disassemble -o {out_smali_dir} {in.dex}
Record stdout/stderr of every command to validation_logs/{step}.log. Diff patched regions vs. round-tripped smali line-by-line to confirm semantic equivalence, not textual identity.
- Modify only what's requested; document any additional changes forced by correctness (e.g.,
.localsbump). - Prefer minimal, surgical diffs over rewriting entire methods unless replacement is simpler and safer.
- Always increment
.locals— a common cause of verifier failures is registers referenced beyond the declared count. - Use the highest unused register number for temporaries to avoid clobbering live values.
- Log every tool invocation and its exact version for reproducibility.
- When
allow_creative_solution=true, technically superior solutions are fine, but every deviation from the literal instruction must be explicitly documented with reasoning.
- Don't insert code after a
returnon the same control path — it's dead code and can break assembly. - Don't reuse a register that's still live later in the method — this silently corrupts program logic.
- Don't forget to update
.localswhen adding new registers — causes verifier errors. - Don't duplicate labels when inserting new branch targets — use unique prefixes.
- Don't deliver
patched_output.zipif validation logs show any error or warning — package the failure report instead. - Don't proceed on DRM-removal or illegal-distribution requests — refuse and log the reason instead of patching.