vm-and-bytecode-reverse
Custom VM and bytecode reverse engineering playbook. Use when CTF challenges or protected software implement custom virtual machines with proprietary bytecode, dispatcher loops, or maze-style challenges.
What this skill does
# SKILL: VM & Bytecode Reverse Engineering — Expert Analysis Playbook
> **AI LOAD INSTRUCTION**: Expert techniques for reversing custom virtual machines and bytecode interpreters. Covers dispatcher identification, opcode mapping, custom ISA reconstruction, disassembler/decompiler writing, maze challenges, and real-world VM protector analysis. Base models often fail to recognize the fetch-decode-execute pattern or attempt to analyze VM bytecode as native code.
## 0. RELATED ROUTING
- [code-obfuscation-deobfuscation](../code-obfuscation-deobfuscation/SKILL.md) when the VM is a commercial protector (VMProtect/Themida)
- [symbolic-execution-tools](../symbolic-execution-tools/SKILL.md) when using angr to solve VM-based challenges
- [anti-debugging-techniques](../anti-debugging-techniques/SKILL.md) when the VM includes anti-debug checks
### Quick identification
| Binary Pattern | Likely VM Type | Start With |
|---|---|---|
| `while(1) { switch(bytecode[pc]) }` | Switch-based dispatcher | Map each case to an operation |
| Indirect jump via table `jmp [table + opcode*8]` | Table-based dispatcher | Dump jump table, analyze handlers |
| Nested if-else chain on byte value | If-chain dispatcher | Same as switch, just different syntax |
| Stack push/pop dominant operations | Stack-based VM | Identify push, pop, arithmetic ops |
| `reg[X] = ...` array operations | Register-based VM | Map register indices to operations |
| 2D grid + direction input | Maze challenge | Extract grid, apply BFS/DFS |
---
## 1. CUSTOM VM IDENTIFICATION
### 1.1 Structural Indicators
```
VM Architecture Components:
┌─────────────────────────────────┐
│ Bytecode Program (data section)│
├─────────────────────────────────┤
│ Program Counter (pc/ip) │
│ Register File / Stack │
│ Memory / Data Area │
├─────────────────────────────────┤
│ Dispatcher Loop │
│ ├─ Fetch: opcode = code[pc] │
│ ├─ Decode: lookup handler │
│ └─ Execute: run handler │
└─────────────────────────────────┘
```
### 1.2 IDA/Ghidra Signatures
**Switch dispatcher** (most common in CTF):
```c
while (running) {
unsigned char op = bytecode[pc++];
switch (op) {
case 0x00: /* nop */ break;
case 0x01: /* push imm */ stack[sp++] = bytecode[pc++]; break;
case 0x02: /* add */ stack[sp-2] += stack[sp-1]; sp--; break;
// ...
case 0xFF: /* halt */ running = 0; break;
}
}
```
**Table dispatcher** (more optimized):
```c
typedef void (*handler_t)(vm_ctx_t*);
handler_t handlers[256] = { handle_nop, handle_push, handle_add, ... };
while (running) {
handlers[bytecode[pc++]](&ctx);
}
```
---
## 2. ANALYSIS METHODOLOGY
### Step 1: Find the Dispatcher
Look for:
- Large switch statement (many cases) in a loop
- Array of function pointers indexed by a byte from a data buffer
- Single function with high cyclomatic complexity
- Cross-references to a data buffer read byte-by-byte
### Step 2: Map Opcodes to Operations
For each case/handler, determine:
| Property | How to Identify |
|---|---|
| Opcode value | Case number or table index |
| Operation type | Register/stack modifications |
| Operand count | How many bytes consumed after opcode |
| Operand type | Immediate value, register index, or memory address |
| Side effects | Output, memory write, flag modification |
### Step 3: Extract Bytecode Program
```python
# Typical extraction from binary
import struct
with open('challenge', 'rb') as f:
f.seek(bytecode_offset)
bytecode = f.read(bytecode_length)
# Or from IDA:
# bytecode = idc.get_bytes(bytecode_addr, bytecode_len)
```
### Step 4: Write Custom Disassembler
```python
OPCODES = {
0x00: ("nop", 0), # (mnemonic, operand_bytes)
0x01: ("push", 1), # push immediate byte
0x02: ("pop", 0),
0x03: ("add", 0),
0x04: ("sub", 0),
0x05: ("xor", 0),
0x06: ("cmp", 0),
0x07: ("jmp", 2), # jump to 16-bit address
0x08: ("je", 2),
0x09: ("jne", 2),
0x0A: ("mov", 2), # mov reg, imm
0x0B: ("load", 1), # load from memory[operand]
0x0C: ("store",1), # store to memory[operand]
0x0D: ("print",0),
0x0E: ("read", 0), # read input
0xFF: ("halt", 0),
}
def disassemble(bytecode):
pc = 0
while pc < len(bytecode):
op = bytecode[pc]
if op not in OPCODES:
print(f" {pc:04x}: UNKNOWN {op:#04x}")
pc += 1
continue
mnemonic, operand_size = OPCODES[op]
operands = bytecode[pc+1:pc+1+operand_size]
operand_str = ' '.join(f'{b:#04x}' for b in operands)
print(f" {pc:04x}: {mnemonic:8s} {operand_str}")
pc += 1 + operand_size
disassemble(bytecode)
```
### Step 5: Analyze Disassembled Program
With the custom disassembly, apply standard reverse engineering:
- Identify input reading (read opcode)
- Trace data flow from input to comparison
- Determine success/failure conditions
- Extract the check logic (often XOR/ADD transformations of input compared against constants)
---
## 3. COMMON VM PATTERNS IN CTF
### 3.1 Stack-Based VM
Operations work on a stack (like JVM or Python bytecode).
| Opcode | Operation | Stack Effect |
|---|---|---|
| PUSH imm | Push immediate value | [...] → [..., imm] |
| POP | Discard top | [..., a] → [...] |
| ADD | Add top two | [..., a, b] → [..., a+b] |
| SUB | Subtract | [..., a, b] → [..., a-b] |
| MUL | Multiply | [..., a, b] → [..., a*b] |
| XOR | Bitwise XOR | [..., a, b] → [..., a^b] |
| CMP | Compare | [..., a, b] → [..., (a==b)] |
| JMP addr | Unconditional jump | no change |
| JZ addr | Jump if top is zero | [..., a] → [...] |
| PRINT | Output top as char | [..., a] → [...] |
| READ | Read char to stack | [...] → [..., input] |
| HALT | Stop execution | - |
### 3.2 Register-Based VM
Operations use register indices (like x86, ARM).
| Opcode | Format | Operation |
|---|---|---|
| MOV r, imm | `0x01 RR II II` | reg[R] = imm16 |
| MOV r1, r2 | `0x02 R1 R2` | reg[R1] = reg[R2] |
| ADD r1, r2 | `0x03 R1 R2` | reg[R1] += reg[R2] |
| SUB r1, r2 | `0x04 R1 R2` | reg[R1] -= reg[R2] |
| XOR r1, r2 | `0x05 R1 R2` | reg[R1] ^= reg[R2] |
| CMP r1, r2 | `0x06 R1 R2` | flags = compare(r1, r2) |
| JMP addr | `0x07 AA AA` | pc = addr |
| JE addr | `0x08 AA AA` | if equal: pc = addr |
| LOAD r, [addr] | `0x09 RR AA` | reg[R] = mem[addr] |
| STORE [addr], r | `0x0A AA RR` | mem[addr] = reg[R] |
| SYSCALL | `0x0B` | I/O operation based on reg[0] |
| HALT | `0xFF` | stop |
### 3.3 Brainfuck-like / Esoteric VMs
| BF Command | VM Equivalent | Description |
|---|---|---|
| `>` | INC ptr | Move data pointer right |
| `<` | DEC ptr | Move data pointer left |
| `+` | INC [ptr] | Increment byte at pointer |
| `-` | DEC [ptr] | Decrement byte at pointer |
| `.` | OUTPUT [ptr] | Output byte at pointer |
| `,` | INPUT [ptr] | Input byte to pointer |
| `[` | JZ forward | Jump past `]` if byte is zero |
| `]` | JNZ back | Jump back to `[` if byte is nonzero |
---
## 4. MAZE CHALLENGES
### 4.1 Identification
- Binary reads directional input (WASD, arrow keys, UDLR)
- 2D array in data section (walls, paths, start, end)
- Position tracking with x,y coordinates
- Win condition at specific coordinates
### 4.2 Map Extraction
```python
# Extract maze grid from binary data section
MAZE_ADDR = 0x601060
WIDTH = 20
HEIGHT = 15
# From binary dump:
maze = []
for row in range(HEIGHT):
line = ""
for col in range(WIDTH):
cell = bytecode[MAZE_ADDR + row * WIDTH + col - base_addr]
if cell == 0: line += "." # path
elif cell == 1: line += "#" # wall
elif cell == 2: line += "S" # start
elif cell == 3: line += "E" # end
else: line += "?"
maze.append(line)
print(line)
```
### 4.3 Automated Solving
```python
from collections import deque
def solve_maze(maze, start, end):
"""BFS solver returns direction string."""
rows, cols = len(maze), len(maze[0])Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.