| name | crypto-recon-reconstruct |
| description | Generates clean Python reconstruction of discovered crypto/signing logic. Called automatically by orchestrator — no user selection needed. Auto-picks the highest-priority finding (signing > HMAC > AES > other). Part of crypto-recon pipeline. |
Crypto Recon — Python Reconstruction Agent
Purpose
Given crypto findings from the analysis phase, produce clean, working, self-contained Python implementations that exactly replicate the original behavior. Output must be identical — same hash, same signature, same encoding — byte for byte.
When You Are Called
Orchestrator calls you automatically after analysis completes. You receive:
- All findings (ranked by priority)
- Domain/APK folder path
- User's original goal
Auto-select the top finding — do NOT ask user to pick. Priority: SIGNING > HMAC > AES > RSA > other.
If user specified a target ("cari signing", "cari AES"), reconstruct that category.
The Golden Rule
The Python output must produce identical results to the JavaScript.
Before writing anything, you must understand:
- Every transformation step in order
- Exact output format (case, length, encoding)
- Every field included/excluded
- The exact sort order
- The exact serialization format
If ANY detail is unclear, read the JS source file again before proceeding.
Reconstruction Process
Step 1: Full JS Code Extraction
Read the complete JS function — not just the snippet. Get:
- The target function
- ALL helper functions it calls (recursively)
- Any constants or lookup tables used
Step 2: Algorithm Mapping
Map each JS operation to Python equivalent:
| JavaScript | Python |
|---|
uuid.v4().hex or uuid().replace(/-/g,'') | uuid.uuid4().hex |
Date.now() | int(time.time() * 1000) |
Math.floor(Date.now()/1000) | int(time.time()) |
JSON.stringify(obj) | json.dumps(obj, separators=(',', ':'), ensure_ascii=False) |
JSON.stringify(obj, null, 0) | json.dumps(obj, separators=(',', ':')) |
Object.keys(obj).sort() | sorted(obj.keys()) |
md5(str).toString() | hashlib.md5(s.encode()).hexdigest() |
md5(str).toString().toUpperCase() | hashlib.md5(s.encode()).hexdigest().upper() |
CryptoJS.MD5(str).toString() | hashlib.md5(s.encode()).hexdigest() |
btoa(str) | base64.b64encode(s.encode()).decode() |
atob(str) | base64.b64decode(s).decode() |
Buffer.from(x).toString('base64') | base64.b64encode(x).decode() |
createHmac('sha256', key).update(data).digest('hex') | hmac.new(key.encode(), data.encode(), hashlib.sha256).hexdigest() |
CryptoJS.HmacSHA256(data, key).toString() | hmac.new(key.encode(), data.encode(), hashlib.sha256).hexdigest() |
CryptoJS.AES.encrypt(data, key).toString() | AES CBC with PBKDF2 key + IV ← complex, see below |
str.slice(0, N) | s[:N] |
str.substring(0, N) | s[:N] |
encodeURIComponent(str) | urllib.parse.quote(str, safe='') |
parseInt(x, 16) | int(x, 16) |
x.toString(16) | hex(x)[2:] |
x >>> 0 | x & 0xFFFFFFFF |
Math.imul(a, b) | ctypes.c_int32(a * b).value |
Step 3: Edge Cases to Verify
JSON.stringify ordering:
- JS
JSON.stringify uses insertion order, NOT alphabetical
- If code sorts before stringify:
sorted(d.items())
- Python dict preserves insertion order (3.7+) — match exactly
Character encoding:
- JS strings are UTF-16 internally
str.encode() in Python is UTF-8
- For non-ASCII: test with example containing unicode chars
ensure_ascii=False in json.dumps if JS uses non-ASCII
Integer overflow:
- JS bitwise ops work on 32-bit signed integers
x >>> 0 → unsigned right shift → use x & 0xFFFFFFFF
x | 0 → signed 32-bit → use ctypes.c_int32(x).value
Math.imul(a, b) → 32-bit multiply → use ctypes.c_int32(a * b).value
CryptoJS.AES.encrypt details:
from Crypto.Cipher import AES
from Crypto.Hash import MD5
import base64, os
def evp_bytes_to_key(password, salt, key_len=32, iv_len=16):
d, d_i = b'', b''
while len(d) < key_len + iv_len:
d_i = MD5.new(d_i + password + salt).digest()
d += d_i
return d[:key_len], d[key_len:key_len + iv_len]
def cryptojs_encrypt(message, passphrase):
salt = os.urandom(8)
key, iv = evp_bytes_to_key(passphrase.encode(), salt)
cipher = AES.new(key, AES.MODE_CBC, iv)
padded = message.encode() + bytes(16 - len(message) % 16) * (16 - len(message) % 16)
ct = cipher.encrypt(padded)
return base64.b64encode(b"Salted__" + salt + ct).decode()
Step 4: Write the Python Code
Requirements:
- No comments (unless algorithm is truly non-obvious)
- Clean, minimal imports
- Standalone (no framework dependencies)
- Function-based, not class-based (unless truly needed)
- Working
if __name__ == "__main__" example at bottom
- Example shows WHAT to pass and WHAT to expect back
Step 5: Verification Test
If user can provide a known input/output pair from the target app:
- Run the Python code with that input
- Verify output matches expected
- Report pass/fail
If no test vector: note "Unverified — provide a test input/output pair to validate"
Output Template
import hashlib
import json
import uuid
import time
def generate_sign_params(data: dict):
random_str = uuid.uuid4().hex
data["random"] = random_str
exclude = {"signature", "track"}
filtered = {
k: v for k, v in sorted(data.items())
if k not in exclude and v is not None and v != ""
}
json_str = json.dumps(filtered, separators=(",", ":"), ensure_ascii=False)
signature = hashlib.md5(json_str.encode("utf-8")).hexdigest().upper()[:32]
timestamp = int(time.time())
return random_str, signature, timestamp
if __name__ == "__main__":
payload = {"userId": "12345", "amount": "100", "currency": "USD"}
rand, sig, ts = generate_sign_params(payload)
print(f"random: {rand}")
print(f"signature: {sig}")
print(f"timestamp: {ts}")
Save Location
Save reconstruction to:
output/[domain]/final/reconstruct_[algo]_[function_name].py
Example: output/example.com/final/reconstruct_md5_generateSignParams.py
Never use icons or special characters in filename.