wangyin572/dsh-plugin-reverse ↗★ 1
dsh-plugin-reverse
DSH plugin: JS reverse-engineering toolkit - host-env stripping for static deobfuscation and crypto verification.
Install
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:wangyin572/dsh-plugin-reverseREADME
Read the full README ↗Configuration
Override by line id in the profile's cordis.patch.yml:
- id: reverse-toolkit
name: 'dsh-plugin-reverse'
config:
maxSourceBytes: 2000000 # maximum source size allowed per analysis, in bytes
hookLogLimit: 2000 # Hook script log ring-buffer size
hookAutoDiscover: true # whether the Hook auto-discovers suspicious functions by default
The config block is optional; every field has a default.
3. Usage flow
3.1 Always assess first
rev_assess(code = )
The report reaches one of four conclusions:
| Conclusion | Meaning |
|---|---|
static-first | The code is statically complete and lightly obfuscated → restore it directly |
hook-then-static | Both routes are viable → hook first to obtain a set of real input→output pairs, then restore and compare byte by byte |
hook-first | eval / new Function / with present, so the real logic is not statically visible → you can only hook first |
blocked | Insufficient information (e.g. the logic lives in a Worker or WASM); more material is needed |
It also gives an obfuscation score, a feature list, ranked entry-point candidates, the environment values that must be captured, and an ordered execution plan.
3.2 Route ①: hook to locate the entry point
rev_hook_generate(targets = [{ object: "window", method: "makeSign" }])
Save the returned script as a .js file and run it in the target page (DevTools console /
Sources → Snippets / CDP Page.addScriptToEvaluateOnNewDocument). Afterwards:
__revHook.dump() // inspect captured records
__revHook.save() // export as JSON
__revHook.unhook() // restore all replaced methods
Every record carries a call stack — the most effective information for locating who calls the encryption function.
3.3 Route ②: static restoration (the main path)
rev_assess(code = ...) # confirm feasibility first
rev_deobfuscate(code = ...) # preprocessing: escape restoration / constant folding / member-access normalisation
rev_extract_pure(code = ..., entry = ["makeSign"])
Three files are produced; write them to disk and run them directly to verify:
node demo.mjs
The shape of pure.mjs:
export function createRuntime(env) {
// host objects are all injected via env, rather than rewriting every reference site
const { navigator, screen, document, btoa } = env
// ---- from original file line 4: _0x1a2b (function) ----
function _0x1a2b(a, b) { /* verbatim source, not rewritten */ }
// ---- from original file line 17: makeSign (function) ----
function makeSign(payload) { /* verbatim source, not rewritten */ }
return { makeSign }
}
Call site (no browser involved at all):
import { createRuntime } from './pure.mjs'
import { createEnv } from './env.mjs'
// real captured environment values are injected here
const runtime = createRuntime(createEnv({
navigator: { userAgent: 'real UA', platform: 'MacIntel' },
document: { cookie: 'real cookie' },
screen: { width: 1512 },
}))
console.log(runtime.makeSign('business payload'))
3.4 Verification
rev_crypto_calc(operation = "aes-decrypt", data = "", passphrase = "passphrase", dataEncoding = "base64")
rev_crypto_calc(operation = "hash", algorithm = "md5", data = "abc")
rev_crypto_calc(operation = "rsa-manual-pow", value = "", exponent = "", modulus = "")
rev_crypto_calc(operation = "xor-brute-force", data = "")
The 20 supported operations: hash, hmac, aes-encrypt, aes-decrypt, openssl-decrypt,
openssl-encrypt, xor, xor-brute-force, xor-recover-key, rsa-encrypt, rsa-decrypt,
rsa-sign, rsa-verify, rsa-public-from-modulus, rsa-manual-pow, rsa-key-info,
mod-pow, mod-inverse, bigint-parse, hex-normalize.
Encoding is always explicit (utf8/hex/base64/base64url/latin1): the same string
interpreted as hex and as utf8 yields completely different results, and silently guessing only
leads to confidently holding a wrong answer. Invalid hex, odd lengths and wrong-length IVs
fail loudly rather than being truncated.