p4nda0s/bin-deobf-skills/deobf-indirect
Claude Code and agent skill by p4nda0s/bin-deobf-skills
- community
Checking that the SKILL.md file is still in the repository.
Deobfuscate indirect branches (CSEL + BR pattern) using symbolic execution and BFS traversal to recover control flow
Install
npx vskill@latest install p4nda0s/bin-deobf-skills/deobf-indirectSKILL.md
Deobfuscate Indirect Branches
Part 1: CSEL + BR Indirect Branch Pattern
Pattern Recognition
The obfuscator converts conditional branches into indirect jumps, where CSEL and BR instructions appear in pairs. CSEL selects one of two target addresses based on a condition, followed by some junk instructions, then BR jumps to the selected address.
Typical instruction sequence:
Key characteristics:
CSELselects one of two register values, representing two branch targets- Code between
CSELandBRis address calculation or junk code BRperforms the final indirect jump
Analysis Approach
Symbolic execution: when encountering a CSEL instruction, force different branch selections to obtain two different BR target addresses.
Steps:
- Start two symbolic execution runs from the same basic block entry
- First run: force
CSELto select the first register (condition-true branch) - Second run: force
CSELto select the second register (condition-false branch) - Each run reaches
BRand yields a different target address (A and B)
Traversal Strategy
BFS traversal starting from the function entry block:
- Add the function entry address to the work queue
- Dequeue an address, run symbolic execution with both
CSELselections untilBR - Record patch info (CSEL address, condition, two target addresses)
- Add both target addresses to the work queue
- Use a visited set to prevent revisiting
- Mark blocks ending with
RETas return blocks — do not continue from them
Patching Strategy
Code between CSEL and BR is junk — patch a conditional branch directly at the CSEL location.
For example, CSEL W11, W8, W9, CC: W8 is the target for the CC-true branch (A), W9 is the target for the CC-false branch (B).
Patch as:
Two instructions = 8 bytes, overwriting the CSEL (4 bytes) and the next junk instruction (4 bytes).
Part 2: CSET + BR Indirect Branch Pattern (Jump Table Variant)
Pattern Recognition
Unlike the CSEL variant in Part 1, the CSET variant uses a 0/1 index to look up a jump table for computing the target address. The code between CSET and BR is NOT all junk — it contains useful instructions that subsequent basic blocks depend on.
Typical instruction sequence:
Junk Code Identification
ADD Xn, Xn, Wm, SXTW is the boundary between junk and useful code. Everything from CSET to ADD (inclusive) is junk:
| Instruction | Purpose | Classification | |
|---|---|---|---|
CSET Wd, cond | Set 0/1 index | junk (replaced by patch) | |
STR Wd, [SP, #off] | Store index value | junk (only used by jump table) | |
LDR Xn, [base, #off] | Load jump table pointer | junk | |
LDR Xm, [Xn, Wd, UXTW#3] | Table lookup table[index] | junk | |
ADRP + LDR Wn | Load encrypted constant | junk | |
MOV + MOVK Wm | XOR decryption key | junk | |
EOR Wn, Wn, Wm | Decrypt offset | junk | |
NEG Wn, Wn | Negate | junk | |
ADD Xm, Xm, Wn, SXTW | Compute final address | junk (boundary) | |
| Subsequent MOV/LDR/STR/ADRP+ADD | Register and stack state init | useful | |
BR Xm | Indirect jump | junk (replaced by patch) |
Analysis Approach
Same as the CSEL variant: symbolic execution forces CSET to take 1 and 0 respectively, runs until BR to obtain two target addresses.
One difference: a single basic block may contain multiple CSEL/CSET instructions (e.g., a data-selection CSEL followed by a branch-controlling CSET). The script forces selection on every CSEL/CSET encountered; the recorded csel_addr is the last one (the one that controls the BR target).
Patching Strategy
Cannot patch directly at the CSET location (would skip useful code). Correct approach:
- Locate
ADD Xn, Xn, Wm, SXTW(the boundary) - Extract useful code bytes between ADD+4 and BR
- Move useful code up to the CSET location
- Append
Bcond A; B Bimmediately after
Prerequisite: useful code must be position-independent (SP-relative addressing, immediate assignments, or same-page ADRP). Moving ADRP within the same 4KB page requires no immediate adjustment.
Reference Implementation
Two script variants for different obfuscation sub-patterns:
script/deinbr-v3-csel.py — CSEL variant (ELF)
For binaries where CSEL directly selects between two target addresses and the code between CSEL and BR is pure address calculation junk. Patches 8 bytes at the CSEL location (Bcond + B), overwriting CSEL and the next junk instruction.
script/deinbr-v3-cset.py — CSET variant (Mach-O / ELF)
For binaries where CSET sets a 0/1 index and the code between CSET and BR contains useful side-effect instructions (register setup, stack stores for subsequent blocks) interleaved with address calculation junk.
Patching strategy: find ADD Xn, Xn, Wm, SXTW (last address calculation step), move the useful code (between ADD and BR) up to the CSET location, then append Bcond + B. This preserves register/memory state that successor blocks depend on. The moved instructions must be position-independent (SP-relative, immediates, or same-page ADRP).
Both scripts share the same core workflow:
analyze_br(proj, func_start)— BFS traversal, collects all patch pointsrun_until_br(proj, state, csel_selector)— execute from a given state until BR, forcing CSEL/CSET selectiondo_patch(binary_path, save_path, proj, patch_list)— assemble patches with keystone and write to binary
Key angr options:
CALLLESS— ignore function calls to prevent analysis divergenceLAZY_SOLVES— defer constraint solving for performanceZERO_FILL_UNCONSTRAINED_MEMORY— fill unconstrained memory with zerosZERO_FILL_UNCONSTRAINED_REGISTERS— fill unconstrained registers with zeros
Script Execution Rules
All non-trivial Python code (both analysis scripts and final patching scripts) MUST be written to a file and executed via python script.py. Never use python -c "..." inline in bash. This avoids shell quoting/escaping issues and makes scripts easier to debug and reuse.
Common Errors and Debugging
BR target is an invalid address
Cause: The user-provided address is not the function entry but an internal basic block. Stack-based jump table base addresses and offset constants have not been initialized (filled with zeros by ZERO_FILL), causing BR to compute an invalid target.
Solution: Confirm the address is the function start. Use the symbol table or IDA/Ghidra to find the function entry:
Both branch targets are identical
Same cause as above — starting execution from a non-entry point means the two registers selected by CSEL hold the same value (both zero or the same uninitialized value), producing the same BR target after address calculation.
Solution: Use the correct function entry address.
Encountering non-CSEL conditional select instructions
Or the script reaches BR without detecting CSEL, so csel_addr is missing from globals.
Cause: The basic block uses CSET instead of CSEL. CSET is a special form of CSEL, equivalent to CSEL Rd, WZR, WZR, invert(cond), selecting 1 or 0 as an index.
Handle the same way as CSEL — identify the condition and force both branch selections. The script must handle both csel and cset:
Security
- Security scanPassed, 95 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/deobf-indirect/SKILL.md
Popularity
- Repository stars
- 54
- Repository forks
- 8
- 7-day trend score
- 20
Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 21 May 2026, 5 months ago.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/p4nda0s/bin-deobf-skills/deobf-indirect)