| name | hermes-dec-bytecode-reverse-engineering |
| description | Disassemble and decompile React Native Hermes bytecode (HBC) files for reverse engineering |
| triggers | ["decompile hermes bytecode","disassemble react native hbc file","reverse engineer hermes bundle","parse hermes bytecode headers","extract javascript from android apk","analyze hermes vm bytecode","convert hbc to javascript","decode react native bundle"] |
Hermes-Dec Bytecode Reverse Engineering
Skill by ara.so — Hermes Skills collection.
Overview
hermes-dec is a reverse-engineering tool for disassembling and decompiling React Native applications compiled to Hermes VM bytecode (HBC) format. It provides three main utilities:
- hbc-file-parser: Parse and display HBC file headers and metadata
- hbc-disassembler: Convert bytecode to assembly-like representation (.hasm)
- hbc-decompiler: Decompile bytecode to pseudo-JavaScript code
Hermes is the default JavaScript engine for React Native on Android (since RN 0.70) and is commonly found in mobile apps at assets/index.android.bundle.
Installation
pipx install hermes-dec
uv tool install hermes-dec
pip install hermes-dec
git clone https://github.com/P1sec/hermes-dec.git
cd hermes-dec
pip install -e .
System-specific installations
sudo snap install hermes-dec
yay -S hermes-dec
Extracting Hermes Bytecode from APK
React Native Android apps package Hermes bytecode in APK files. Extract it first:
unzip my_application.apk -d my_application/
cd my_application/
file assets/index.android.bundle
7z x my_application.apk
Common locations in APK:
assets/index.android.bundle (main bundle)
assets/*.hbc (additional bundles)
assets/index.android.bundle.hbc
Core Commands
1. Parse File Headers
Inspect HBC file structure, bytecode version, and metadata:
hbc-file-parser assets/index.android.bundle
hbc-file-parser assets/index.android.bundle > analysis.txt
Example output:
Hermes Bytecode File v84
File size: 2,453,678 bytes
SHA1 hash: a1b2c3d4...
Number of functions: 1,234
String table size: 45,678 entries
Debug info: present
2. Disassemble to Assembly
Convert bytecode to human-readable assembly (.hasm format):
hbc-disassembler assets/index.android.bundle
hbc-disassembler assets/index.android.bundle output.hasm
hbc-disassembler index.android.bundle disassembled.hasm
Example .hasm output:
Function<loginUser>(3 params, 15 registers):
Offset in debug table: source 0x0000
LoadParam r2, 1
LoadParam r3, 2
LoadConstString r1, "username"
GetByVal r0, r2, r1
LoadConstString r1, "password"
GetByVal r4, r2, r1
Call2 r0, r3, r0, r4
Ret r0
3. Decompile to Pseudo-JavaScript
Generate pseudo-JavaScript code (not yet valid JS, missing control flow):
hbc-decompiler assets/index.android.bundle
hbc-decompiler assets/index.android.bundle decompiled.js
hbc-decompiler index.android.bundle output.js
Example decompiled output:
function loginUser(param0, param1, param2) {
var r0, r1, r2, r3, r4;
r2 = param1;
r3 = param2;
r1 = "username";
r0 = r2[r1];
r1 = "password";
r4 = r2[r1];
r0 = r3(r0, r4);
return r0;
}
Practical Workflows
Full Analysis Pipeline
#!/bin/bash
APK_FILE="$1"
OUTPUT_DIR="analysis_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$OUTPUT_DIR"
echo "[+] Extracting APK..."
unzip -q "$APK_FILE" -d "$OUTPUT_DIR/extracted"
echo "[+] Locating HBC files..."
find "$OUTPUT_DIR/extracted" -name "*.bundle" -o -name "*.hbc" > "$OUTPUT_DIR/hbc_files.txt"
while IFS= read -r hbc_file; do
basename=$(basename "$hbc_file")
echo "[+] Processing $basename..."
hbc-file-parser "$hbc_file" > "$OUTPUT_DIR/${basename}.info.txt"
hbc-disassembler "$hbc_file" "$OUTPUT_DIR/${basename}.hasm"
hbc-decompiler "$hbc_file" "$OUTPUT_DIR/${basename}.js"
done < "$OUTPUT_DIR/hbc_files.txt"
echo "[+] Analysis complete in $OUTPUT_DIR"
Searching for Specific Functions
hbc-disassembler index.android.bundle output.hasm
grep -i "https://" output.hasm
grep -i -E "(token|auth|password|login)" output.hasm
hbc-decompiler index.android.bundle output.js
grep -i "api_key" output.js
Comparing Versions
hbc-disassembler old_version/index.android.bundle old.hasm
hbc-disassembler new_version/index.android.bundle new.hasm
diff -u old.hasm new.hasm > changes.diff
code --diff old.hasm new.hasm
Python API Usage
While hermes-dec is primarily a CLI tool, you can import its modules in Python:
"""
Example: Programmatic access to hermes-dec functionality
"""
import sys
from pathlib import Path
def analyze_hbc_file(hbc_path: Path):
"""
Analyze an HBC file programmatically
"""
if not hbc_path.exists():
print(f"Error: {hbc_path} not found")
return
with open(hbc_path, 'rb') as f:
magic = f.read(8)
if magic[:4] != b'\xC6\x1F\xBC\x03':
print("Not a valid Hermes bytecode file")
return
version = int.from_bytes(magic[4:8], 'little')
print(f"Hermes bytecode version: {version}")
import subprocess
result = subprocess.run(
['hbc-file-parser', str(hbc_path)],
capture_output=True,
text=True
)
if result.returncode == 0:
print("\n=== File Info ===")
print(result.stdout)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python analyze.py <hbc_file>")
sys.exit(1)
analyze_hbc_file(Path(sys.argv[1]))
Batch Processing Script
"""
Batch process multiple APK files for Hermes bytecode analysis
"""
import subprocess
import zipfile
from pathlib import Path
from typing import List
def extract_hbc_from_apk(apk_path: Path, output_dir: Path) -> List[Path]:
"""
Extract HBC files from an APK
Returns list of extracted HBC file paths
"""
hbc_files = []
output_dir.mkdir(parents=True, exist_ok=True)
with zipfile.ZipFile(apk_path, 'r') as apk:
for name in apk.namelist():
if name.endswith('.bundle') or name.endswith('.hbc'):
target = output_dir / Path(name).name
with apk.open(name) as source, open(target, 'wb') as dest:
dest.write(source.read())
if is_hermes_bytecode(target):
hbc_files.append(target)
print(f"[+] Extracted {name} -> {target}")
return hbc_files
def is_hermes_bytecode(file_path: Path) -> bool:
"""Check if file is Hermes bytecode"""
try:
with open(file_path, 'rb') as f:
magic = f.read(4)
return magic == b'\xC6\x1F\xBC\x03'
except:
return False
def decompile_hbc(hbc_path: Path, output_path: Path):
"""Decompile HBC file to pseudo-JavaScript"""
subprocess.run(
['hbc-decompiler', str(hbc_path), str(output_path)],
check=True
)
print(f"[+] Decompiled {hbc_path.name} -> {output_path}")
def main():
apk_dir = Path("apks")
output_base = Path("decompiled")
for apk in apk_dir.glob("*.apk"):
print(f"\n[*] Processing {apk.name}")
apk_output = output_base / apk.stem
apk_output.mkdir(parents=True, exist_ok=True)
hbc_files = extract_hbc_from_apk(apk, apk_output / "extracted")
for hbc in hbc_files:
js_output = apk_output / f"{hbc.stem}.js"
try:
decompile_hbc(hbc, js_output)
except subprocess.CalledProcessError as e:
print(f"[!] Failed to decompile {hbc.name}: {e}")
if __name__ == "__main__":
main()
Understanding Output Formats
Assembly (.hasm) Format
The disassembler outputs Hermes VM assembly instructions:
Function<ComponentName>(params, registers):
# Instruction Dest Source1 Source2
LoadParam r0, 1 # Load first parameter
LoadConstString r1, "key" # Load string constant
GetByVal r2, r0, r1 # Property access: r0[r1]
Call r3, r2 # Function call
Ret r3 # Return value
Common opcodes:
LoadParam: Load function parameter
LoadConst*: Load constants (string, number, undefined, etc.)
GetByVal/PutByVal: Property access
Call*: Function calls
NewObject/NewArray: Object/array creation
Ret: Return from function
Jmp*: Jump instructions (control flow)
Pseudo-JavaScript Format
The decompiler outputs register-based pseudo-code:
function example(param0, param1) {
var r0, r1, r2, r3;
r0 = param0;
r1 = "property";
r2 = r0[r1];
r3 = someFunc(r2);
return r3;
}
Limitations:
- No control flow reconstruction (loops, conditionals appear linear)
- Register-based (not variable-based)
- May have dead code
- Requires manual cleanup for valid JS
Troubleshooting
Not a Hermes Bytecode File
$ file index.android.bundle
index.android.bundle: ASCII text, with very long lines
Solution: This is minified JavaScript, not Hermes bytecode. The app doesn't use Hermes engine or you have the wrong file.
Unsupported Bytecode Version
Error: Unsupported bytecode version 96
Solution: Update hermes-dec to the latest version:
pipx upgrade hermes-dec
pip install --upgrade hermes-dec
If still unsupported, check the GitHub issues or open a new one.
Incomplete Decompilation
The decompiler output is incomplete or has missing functions.
Solution: Try disassembly first to understand structure:
hbc-disassembler file.bundle output.hasm
Empty Output Files
$ hbc-decompiler index.android.bundle output.js
$ wc -l output.js
0 output.js
Solution: Check file format and permissions:
file index.android.bundle
hexdump -C index.android.bundle | head
ls -la index.android.bundle
hbc-decompiler index.android.bundle
APK Extraction Issues
$ unzip app.apk
error: cannot find or open app.apk
Solution: Ensure APK is not corrupted:
7z t app.apk
jar xf app.apk
python -m zipfile -e app.apk output_dir/
Advanced Techniques
Finding Obfuscated Strings
hbc-decompiler index.android.bundle output.js
grep -E '[A-Za-z0-9+/]{20,}={0,2}' output.js
grep -E '0x[0-9a-fA-F]{4,}' output.js
Extracting API Endpoints
hbc-disassembler index.android.bundle output.hasm
grep -oE 'https?://[^"]+' output.hasm | sort -u > endpoints.txt
hbc-decompiler index.android.bundle output.js
grep -oE '(http|https)://[a-zA-Z0-9./?=_-]*' output.js | sort -u
Cross-referencing Functions
hbc-disassembler index.android.bundle output.hasm
grep "Function<" output.hasm > functions.txt
grep "Call" output.hasm > calls.txt
awk '/Function</{print $0} /Call/{print " -> " $0}' output.hasm
Documentation Resources
Version Compatibility
Hermes bytecode versions and React Native releases:
- v59: Hermes 0.1.0 (July 2019)
- v74: React Native 0.64 (iOS support)
- v84: React Native 0.70 (default on Android)
- v90+: Recent React Native versions (2023+)
Check bytecode version:
hbc-file-parser file.bundle | grep -i version