dsh-plugin-reverse
No description
- Stars
- 1
- Language
- JavaScript
- Created
- Oct 7, 2026
- Updated
- Oct 7, 2026
Introduction
dsh-plugin-reverse
A JavaScript reverse-engineering toolkit for DeepSeek Harness: assess the route first, then deliver a result along that route.
English · 简体中文
- Static restoration (the main path): turn extracted obfuscated front-end JS into
computation functions that run standalone in plain Node — strip
window/document/navigator/canvashost dependencies and smooth over environment differences, so you can compute a signature, encrypt a request body or decrypt a response without a browser and without injecting into a page. - Assisted location: generate Hook scripts that capture arguments, return values and call stacks, for debugging, forensics and locating the encryption entry point.
- Verification: built-in AES / RSA / MD5 / SHA / HMAC / cyclic XOR / single-byte brute force / big-integer modular exponentiation, so you can recompute against a known ciphertext, key or IV immediately.
Scope: this plugin is for code you are authorised to analyse — your own projects, authorised security testing, malware analysis, CTF, and interoperability research. The tools are generic debugging and static-analysis techniques and do not implement targeted bypasses for any specific system. Hooks record function arguments and return values and may therefore contain credentials; only use them in environments you are authorised to analyse. Do not point this at other people's accounts or systems.
1. Capability matrix
| Tool | Purpose | Typical output |
|---|---|---|
rev_assess | Run this before every reverse-engineering task: decides whether hooking is viable and whether static restoration is viable, and gives the blockers and an ordered plan | Markdown assessment report |
rev_hook_generate | Generate a browser-executable Hook script (WebCrypto / CryptoJS / JSEncrypt / network layer + auto-discovery) | Self-contained .js script |
rev_extract_pure | Static restoration: dependency-closure extraction plus environment injection, producing host-free computation modules | pure.mjs / env.mjs / demo.mjs |
rev_deobfuscate | Semantics-preserving safe rewrites (escape restoration, constant folding, member-access normalisation) plus obfuscation feature detection | Rewritten source + analysis report |
rev_crypto_calc | 20 cryptographic verification operations, covering both standard-library and hand-rolled big-integer paths | Structured verification results |
2. Installation
Requires Node.js ≥ 24. The built lib/ output is committed, so installing from GitHub source needs
no build approval.
Option 1 — install directly (recommended; no npm needed on the user side)
dsh plugin --profile web add github:wangyin572/dsh-plugin-reverse
This path needs no npm run and no build approval: pnpm installs the one real dependency,
acorn, straight from the manifest, while@deepseek-ai/*is resolved by the DSH resolution layer to the single instance inside the dsh installation. Measured afterwards, the profile'snode_modulesholds onlyacornand this package — no duplicated@deepseek-ai/*.
Option 2 — clone and install locally (when you want to change the code)
git clone https://github.com/wangyin572/dsh-plugin-reverse.git
cd dsh-plugin-reverse
npm install # runtime dependencies (@deepseek-ai/* and acorn)
dsh plugin --profile web add "$(pwd)"
Then restart that profile. To verify it took effect:
dsh --profile web --dump-config | grep -A3 dsh-plugin-reverse
In Option 2,
npm installis not optional.dsh plugin add "$(pwd)"useslink:: pnpm only creates a symlink and installs nothing for it, and this package does not declareacornas a peer, soacorncan only come from the repository's ownnode_modules. Skipping it fails immediately withCannot find package 'acorn'(measured: a bare copy holding onlypackage.json,cordis.patch.ymlandlib/fails at module import).lib/is already in the repository and needs no build; runnpm run buildand commit the result only after editingsrc/.
After installation the five tools above appear on the model side. The runtime dependencies are
@deepseek-ai/cordis, @deepseek-ai/dsh-tools, @deepseek-ai/schemastery and acorn, all published
publicly on npm. Note the tags differ: dsh-tools@0.2.0-rc.2 is published under the next tag,
whereas cordis ~4.0.4 and schemastery ~3.18.4 resolve from latest. The three
@deepseek-ai/* packages are declared as peerDependencies: the profile resolution layer points them
at the single instance inside the dsh installation (pnpm's autoInstallPeers is off in a profile, so no
duplicate instance is installed).
Prebuilt tarball
npm pack produces a ready-to-install tarball containing the already-built lib/ plus the docs:
npm pack
dsh plugin --profile web add ./dsh-plugin-reverse-0.1.0.tgz
Being listed by community directories
Community plugin directories auto-discover repositories through the dsh-plugin GitHub topic
(Settings → Topics, or gh repo edit --add-topic dsh-plugin). The crawler reads the package.json at
the repository root, and this repository already satisfies it:
dsh.bundle.patchpoints at a relative path inside the repo →./cordis.patch.yml✅- the repository is public and not archived;
descriptionbecomes the one-line summary ✅
Do not add
dsh.profilehere. The official docs are explicit: a bundle manifest declaresdsh.bundle, a profile manifest declaresdsh.profile, and nothing is both.dsh.profilebelongs to the profile directory'spackage.jsonand is written bydsh plugin add. Some installers (such as find-plugin) filter out repositories that mix bundle and profile manifests as invalid candidates.
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 JS you extracted>)
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 = "<ciphertext>", passphrase = "passphrase", dataEncoding = "base64")
rev_crypto_calc(operation = "hash", algorithm = "md5", data = "abc")
rev_crypto_calc(operation = "rsa-manual-pow", value = "<m>", exponent = "<e>", modulus = "<n>")
rev_crypto_calc(operation = "xor-brute-force", data = "<hex>")
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.
4. The pure computation core is usable on its own
The core is a host-dependency-free pure function layer: it does not import cordis, does not import DSH and performs no I/O. A project that lists this package as a dependency can use it through the exposed subpath:
import { aesDecrypt, opensslDecrypt, hmac, xorBytes, rsaManualPow } from 'dsh-plugin-reverse/core'
You can also copy the core out entirely: the source is src/core/ in this repository, and the compiled
lib/core/index.js is committed (npm run build rebuilds it after you edit src/).
It follows exactly the same constraints as this plugin's static-restoration output: no DOM/BOM, no side effects, runnable standalone.
5. Project structure
src/
├── core/ pure computation core (encoding / hash / aes / rsa / xor / errors)
├── analysis/ static analysis (ast / host / deobfuscate / extract / hook / assess)
├── services/ cordis services (ctx.reverseCrypto / ctx.reverseAnalysis)
├── tools/ the 5 agent tools (defineTool)
└── index.ts root plugin: registers services and tools
test/
├── core.test.mjs cryptographic core (including external OpenSSL vectors)
├── analysis.test.mjs deobfuscation / host detection / closure extraction / Hook / assessment
├── plugin.test.mjs plugin contract and end-to-end tool chain
└── fixtures/ simulated obfuscated samples
Dependencies flow one way only: tools → services → analysis → core. The core layer never depends
on DSH in reverse, so "the plugin's core" and "the pure code the plugin produces" obey the same
host-free constraints.
6. Verification
npm run build # TypeScript strict-mode compilation
npm test # run the test suite
npm run check # the two steps above
npm run verify:integration # real launcher integration check (isolated DSH_HOME, ~1 minute)
npm run verify:all # everything
The integration check creates an isolated DSH_HOME under .dsh-scratch/ in the workspace, builds a
profile from the web template shipped with the product, really runs dsh plugin add, and asserts
that --dump-config contains the line this package contributes and that --dump-config-schema can
import this package's modules. The script has a guard rail that refuses to place the temporary home
outside the repository, and never touches the real ~/.dsh.
A few key assertions in the tests:
- AES/KDF align with external implementations: vectors are generated on the spot by the system
OpenSSL 3.6 (
openssl enc -aes-256-cbc -md md5 -S ... -pass pass:...), not self-generated and self-verified. - Semantic equivalence: the original obfuscated code is run inside a simulated browser in
node:vm, the static-restoration output is run in plain Node, and the two outputs are asserted to be byte-for-byte identical; it is further asserted that changing an environment value changes the result. - Generated artifacts are executable: both Hook scripts and restoration output first pass AST syntax validation, then are really executed in a sandbox, asserting "same return value", "restorable" and "no module loader required".
- Plugin contract: a real cordis mount, asserting that all 5 tools register, both services are available, and the parameter validation chain is intact.
7. Honest boundaries
These are capability boundaries, not a to-do list. In these situations the tools explicitly report a blocker instead of producing code that "runs but computes the wrong thing":
| Situation | Tool behaviour |
|---|---|
eval / new Function / with | Judged as blocking static restoration; hooking is recommended; reports a blocker and generates no code |
| Control-flow flattening | No automatic reordering (wrong reordering silently changes semantics); reports the case order for manual reconstruction and lowers confidence to low |
| String arrays + decoder functions | Detected and reported only, not automatically inlined (inlining before the rotation offset is fully restored amounts to guessing) |
| canvas / WebGL / Audio fingerprints | Cannot be reproduced in Node; env.mjs leaves explicit placeholders and lists them under unmockable, requiring external capture |
| Environment values that depend on a real browser instance | Produces MUST_CAPTURE placeholders and never fabricates a "plausible" value |
| The entry point depends on an outer scope that cannot be extracted | Reports an unresolved-reference blocker and states which code needs to be supplied |
Only (n, e, d) given, without CRT parameters | A PEM private key cannot be constructed (mathematically requires p and q); use rsa-manual-pow to do the modular exponentiation directly instead |
MUST_CAPTURE is an inherent boundary of static restoration, not an implementation defect: if a
signature mixes in navigator.userAgent or a canvas fingerprint, any attempt to "simulate a value"
only yields a signature that looks right but is wrong. The correct approach is to capture it once in
the browser and then inject it.
8. License
MIT