A Python-based Java malware triage tool that scans source trees, decodes supported obfuscation schemes, highlights suspicious strings and behaviors, detects embedded or dropped artifacts, and can optionally resolve runtime C2 indicators from on-chain configuration.
Java Triage

java_triage.py is a static triage tool for suspicious Java codebases, decompiled JARs, and Minecraft mods.
It can decompile JARs with CFR (including hostile/eSkid-protected samples), rewrite supported obfuscated string patterns, produce deciphered copies with XOR strings replaced, scan suspicious strings and behaviors, identify suspicious artifacts, resolve runtime C2 hints from on-chain config data, assemble full C2 URLs from decoded fragments, optionally inspect a resolved stage-2 JAR in static-only mode (including AES decryption of encrypted blobs), probe live infrastructure, query external enrichment APIs (RatterScanner and JLab static scan), and produce Rich console, JSON, and HTML reports.
Under the hood, it combines bytecode-aware decompilation, constant-pool fallback scanning, invokedynamic / BootstrapMethods mapping, heuristic string recovery, and behavior classification into one triage pass.
Features
Static Analysis
- Decompiles JARs with CFR when available.
- Falls back to
.classconstant-pool scanning when source is missing or hostile. - Handles eSkid/protected samples, malformed archives, nested dropped JARs, and embedded Base32 archive resources.
- Maps
invokedynamic/BootstrapMethodssites and records suspicious bootstrap owners. - Produces a post-deobfuscation string dump with AES key candidate detection.
Triage Signals
- Scores literals, decoded strings, and file behaviors separately before collapsing them into verdict tiers.
- Tracks source locations, family breakdowns, and replacement counts for supported decryptor patterns.
- Suppresses obvious bundled-library noise so the high-signal findings stay visible.
Deobfuscation & String Recovery
- Rewrites
StringDecrypt.decrypt(new byte[]{...})andload(new int[]{...}, new int[]{...}, k1, k2)patterns. - Supports deterministic XOR-stream decoding used by common obfuscators.
- Deciphers XOR-obfuscated
getBytes("ISO-8859-1")andtoCharArray()strings in whole-codebase or single-file mode. - Recovers split strings, printable byte/char arrays, reversed
StringBuilderliterals, and inline Skidfuscator-style XOR patterns. - Tracks replace counts, unresolved values, pass counts, and family breakdowns.
Detection Coverage
- Scans literals, comments, and decoded strings for URLs, payload paths, encoded blobs, command execution, persistence clues, and suspicious keywords.
- Detects Discord, Telegram, webhook, and cryptocurrency indicators.
- Traces Minecraft session, username, UUID, and access-token reads into network/write sinks.
- Flags multi-payload exfiltration, self-copy + detached re-launch persistence, and staged dropper behavior.
- Classifies findings and behaviors into severity and verdict tiers, while suppressing obvious bundled-library noise.
- Emits methodology behaviors for obfuscation patterns, token-harvest vectors, and decompiler-failure diagnostics.
Minecraft Coverage
- Detects session/account file references such as
session.json,launcher_accounts.json, and.minecraft. - Flags possible Minecraft identity exfiltration when user identifiers appear alongside outbound HTTP activity.
- Recognizes Minecraft client module packs via
addModule(...)registration and Wurst-styleHackList/*Hackpatterns. - Exposes module metadata, category counts, and Minecraft-specific behavior IDs in the report.
Infrastructure & Enrichment
- Resolves runtime C2 from on-chain Ethereum/Polygon
eth_calldata. - Assembles full C2 URLs from decoded fragments and probes endpoints without downloading payloads.
- Supports optional stage-2 static-only analysis and an interactive download/decrypt prompt.
- Enriches results with RatterScanner and JLab public static scan when network access is allowed.
- Extracts blockchain indicators, custom header fingerprints, and payload/persistence endpoint clues.
Reporting & UX
- Produces Rich console output, JSON, and standalone HTML reports.
- Includes banner rendering, Unicode-safe output handling, summary cards, and sortable HTML tables.
- Adds metadata sections such as
Basic Properties,JAR Info, andBundle Info. - Optionally enriches metadata with
Vhash,SSDEEP,TLSH,TrID, andMagika. - Identifies suspicious artifacts such as embedded payloads, large opaque blobs, and archive-like resources.
Default Workflow
By default, running:
python java_triage.py <target>
will:
- Resolve the target folder or use the current directory.
- If applicable, decompile a selected JAR with CFR into a working source folder.
- Run a quick obfuscation-density probe on the scan root.
- If supported obfuscated call patterns are detected, copy the target to a deobfuscated working folder and rewrite supported string calls there.
- Scan the resulting source tree.
- Optionally resolve runtime C2 hints, perform stage-2 static analysis, and enrich results with RatterScanner and JLab static scan.
- Render the Rich console report and write JSON and HTML reports by default.
Current default probe threshold:
- Total
StringDecrypt.decrypt(...)+load(new int[]{...})calls >=1
<targetname>deobfuscated- if it exists:
<targetname>deobfuscated2,3, etc.
- scanning
ExampleModwritesExampleMod.jsonandExampleMod.html - scanning a directory such as
exampleprojectwritesexampleproject.jsonandexample_project.html
String + Discord Coverage
String literal scanning includes:
- URLs and endpoint-like strings
- Command and LOLBin patterns such as
cmd.exe,powershell, andcmstp - Path and payload indicators such as
.exe,.dll,.jar,.dat,.bin, and temp or appdata paths - High-entropy encoded blobs
- Suspicious keywords such as
token,authorization,webhook, anddefender
- Environment variable access (
System.getenv) - Dynamic class loading via
URLClassLoader - Local Minecraft session or account file path references such as
session.json,launcher_accounts.json, and.minecraft - Possible identity exfiltration when username or UUID reads appear alongside outbound HTTP activity
- Bot tokens
- Webhook URLs (
discord.com/api/webhooks/...) - Snowflake IDs (
17-20digit IDs) - Contextual IDs in literals containing labels like
guildid,channelid,userid,roleid, andapplication_id - Encrypted Chromium token marker blobs (
dQw4w9WgXcQ:<base64>) commonly used in token-stealer chains
Minecraft Session and Identity Detection
To reduce false positives, session or account path detection requires:
- the token to appear inside a Java string literal such as
session.json,launcher_accounts.json, or.minecraft - file I/O usage in the same file such as
new File(,Paths.get(,Files.read...,FileInputStream(, orFileReader(
The scanner also flags a high-severity indicator when user identifiers are read and outbound HTTP appears in the same file:
- Username reads:
method_1676(),getName(),getUsername() - UUID reads:
method_44717(),GameProfile.getId(),Session.getUuid(), and mapped or Yarn variants - Outbound HTTP markers: discovered host URLs,
HttpClient.send(...),OkHttpClient.newCall(...),HttpURLConnection
possibleminecraftidentity_exfiltration with the source location and evidence.
Expanded alias coverage includes:
- Session presence or access:
method_1548(),getSession(),getUser(),net.minecraft.client.util.Session,new Session(...) - Username access:
method_1676(),getName(),getUsername() - UUID access:
method_44717(),getProfileId(),getUuid(),GameProfile.getId() - Token access:
method_1674(),getAccessToken(),session.getAccessToken()
Minecraft Client Module Coverage
Java Triage now also looks beyond individual session/token reads and tries to recognize Minecraft client module packs and utility clients.
It does this by:
- locating module manager patterns such as repeated
addModule(SomeClass.class)registration - extracting module metadata from the referenced source classes
- falling back to Wurst-style
HackList/*Hackpatterns when module registration is not present
- module name
- description
- category
- source file
- aggregate category counts
minecraft_modules, and the HTML/Rich reports render a dedicated module summary when detection succeeds.
JLab Static Scan Enrichment
When enabled, Java Triage will attempt to upload the original source JAR/ZIP to:
https://jlab.threat.rip/api/public/static-scan
- Enabled by default (
--jlab-static-scan) - Can be disabled with
--no-jlab-static-scan - Requires network access (disabled by
--no-network) - Upload target priority:
.jar/.zip
- Size and format guardrails:
.jar/.zip are uploaded
- max upload size handled by the tool: 50 MB
Returned data is stored under jlabstaticscan in JSON and rendered in Rich/HTML reports, including:
- upload metadata (filename, size, status)
- rate-limit metadata when available
- matched signature count and signature rows (severity, id, name, description, type, count, match preview)
Executive Summary
The tool can generate an AI executive summary using either OpenAI or DeepSeek.
OPENAIAPIKEY: enables OpenAI Chat CompletionsDEEPSEEKAPIKEY: enables DeepSeek Chat CompletionsTRIAGELLMPROVIDER: optional provider selector:
auto (default): tries OpenAI first, then DeepSeek
- openai: use only OpenAI
- deepseek: use only DeepSeek
TRIAGEOPENAIMODEL: OpenAI model override (default:gpt-4.1-mini)TRIAGEDEEPSEEKMODEL: DeepSeek model override (default:deepseek-v4-flash)
deepseek-v4-flash, deepseek-v4-pro
TRIAGEDEEPSEEKREASONING_EFFORT: DeepSeek reasoning effort (default:high)
Requirements
- Python 3.10+ recommended
- Optional:
richfor enhanced terminal output - Optional CLI tools for metadata enrichment:
ssdeep,tlsh,trid,vhash - Optional Python package for metadata enrichment:
magika
Installation
No package install is required for the script itself.
# optional, for rich UI output
pip install rich
optional, for magika metadata enrichment
pip install magika
Usage
New here? Start with the beginner guide: BEGINNER_GUIDE.md
This project assumes Windows and PowerShell in the examples below.
python java_triage.py [target]
target is a directory path (or omitted for current directory).
For a full list of options at any time:
python java_triage.py --help
Examples
# Scan current directory
python java_triage.py
Scan a specific unpacked source tree
python javatriage.py ./sampleproject
Disable default auto-decrypt copy or rewrite behavior
python javatriage.py ./sampleproject --no-auto-decrypt
Explicitly write a decrypted copy to a chosen path, then scan it
python javatriage.py ./sampleproject --decrypt-codebase-out ./sampleprojectdeobf
Rewrite in-place
python javatriage.py ./sampleproject --decrypt-codebase-in-place
Rewrite only, then skip the post-decrypt triage scan
python javatriage.py ./sampleproject --no-rescan-after-decrypt
Disable JSON output
python javatriage.py ./sampleproject --no-json
Save JSON report to a custom file
python javatriage.py ./sampleproject --out report.json
Disable HTML report output
python javatriage.py ./sampleproject --no-html
Save HTML report to a custom file
python javatriage.py ./sampleproject --html-out report.html
Disable all network lookups during analysis
python javatriage.py ./sampleproject --no-network
Disable stage-2 static analysis
python javatriage.py ./sampleproject --no-analyze-stage2
Disable JLab static scan enrichment
python javatriage.py ./sampleproject --no-jlab-static-scan
Wider rich output
python javatriage.py ./sampleproject --rich-width 220
Decipher a single .java file (no full scan)
python javatriage.py --decipher-only ./sampleproject/suspicious/Helper.java
Produce a deciphered copy + scan both
python javatriage.py ./sampleproject --decipher-codebase
CLI Options
target: folder to scan (default: current directory)--json: emit JSON output (enabled by default)--no-json: emit text or Rich output instead of JSON--out <path>: write output to file--html: also emit an HTML report (enabled by default)--no-html: disable HTML report output--html-out <path>: write HTML report to a custom file--no-progress: disable progress messages--no-network: disable runtime C2 resolution and related network lookups--jlab-static-scan: upload source JAR/ZIP to JLab public static scan API and include matched signature results (enabled by default)--no-jlab-static-scan: disable JLab public static scan lookup--analyze-stage2: after resolving a runtime payload endpoint, download the stage-2 JAR and perform static-only analysis (enabled by default)--no-analyze-stage2: disable stage-2 static analysis--rich-width <int>: preferred Rich console width for progress and final report rendering--decrypt-codebase-in-place: rewrite supported encrypted string calls in the target tree directly--decrypt-codebase-out <path>: copy the tree to<path>, rewrite there, then scan that rewritten tree--no-rescan-after-decrypt: perform rewrite only and exit--no-auto-decrypt: disable opportunistic auto-decrypt probe and rewrite behavior--decipher-codebase: produce a deciphered copy of the target with all XOR-obfuscatedgetBytes/toCharArraystrings replaced by decoded literals, then scan both copies (enabled by default; disable with--no-auto-decryptor--no-rescan-after-decrypt)--decipher-only <path>: decipher a single.javafile and write decoded strings to JSON (no scan)--rich-width <int>: preferred Rich console width for progress and final report rendering
Methodology Behavior IDs
The following behavior IDs were added for explicit methodology coverage and can be searched directly in JSON output:
obfxorencodednameaccessobfbase64encodednameaccessobfcaesarencodednameaccessobfmethodhandletoken_accessobflambdametafactorytoken_accessobfarrayindirectdispatchtoken_accessobfsplitreassemblednameaccessobfunsafefieldtokenaccessobfvarhandlefieldtokenaccessobfstackwalkerindirect_accessobfintarrayencodedname_accessobfclassloaderbypasstokenaccesstokenclasssweepstaticfield_harvesttokenspinracewindowharvesttokenyggdrasilinternal_probetokenprocesscommandline_harvesttokenprocesshandlecommandline_probetokenruntimemxbeanargprobetokensystempropertyauthprobetokenenvironmentauth_probetokensunjavacommandprobetokenjdkinternalprocessprobedataflowtokentonetworksinkdataflowusernametonetworksinkdataflowuuidtonetworksinktokenbootstrapconstructor_capturetokenauthlibdeephookaccesstokenconnectionauthorizationheaderprobetokenurlconnectionrequestsunsafeprobetokenconnectionspinraceheader_harvestblockchaindnsc2_resolverrawsockethttppostclientproofminecrafttokenrawsocketexfilchaintwopayloadexfil_architecturepersistencefilesystemcopyrelaunchchainpersistencedetachedprocess_relaunchc2fallbackdomainpayloaddownloadendpointpersistenceinstalldirectorypythonexecutablereferencepythonscriptreferenceexfilendpointprefiremcexfilendpointsubmit_logpythonsubprocessargument_chaindetachedprocessruntime_indicatorminecraftcoordinateexfiltrationdiscordwebhookurl_reassemblymultipathexfil_breakdowninlinexorstring_decodersensitivegamedata_comment
decipher section in JSON reports contains counts of XOR strings replaced and files changed when --decipher-codebase is used (enabled by default).
Output
Text and Rich output include:
- Basic Properties, JAR Info, and Bundle Info
- Cryptocurrency Addresses
- Discord / Webhook Indicators
- Windows Persistence / Staging Indicators
- Decode and string findings (sorted by category priority)
- Assessment findings (
benign,needs_review,suspicious) - Behavioral findings (sorted by severity)
- Artifact findings
- Network Endpoint Assessment
- Runtime C2 Resolution
- Assembled C2 URLs
- Infrastructure Probe Results
- Blockchain Indicators
- Variant Detections
- Raw String Detections
- Heuristic Detections
- RatterScanner results
- JLab static scan results (sorted by severity)
- Stage-2 Analysis status
- Interactive post-scan download + decrypt prompt
- Summary counts and verdict layers
target_metadatascandiagnostics(per-scan-root breakdown of javafiles, classfiles, findingcount, scan_mode)runtime_c2url_assembly(assembled C2 URLs with domain, method, path, description)infra_probe(live probe results per endpoint)stage2_analysisblockchain_indicatorsnetworkendpointassessmentvariant_detectionsrawstringdetectionsheuristic_detectionsratter_scannerjlabstaticscandecipher(XOR string replacement stats)deobfuscationstring_dump(post-prep string dump stats)invokedynamic_bootstrap(indy/bootstrap mapping stats)findingsbehavior_findingsartifact_findingsreconstructed_strings(StringBuilder-reassembled XOR strings)minecraft_modulessummary
- top-level summary cards and overall assessment
- executive summary, when available
- expanded metadata and enrichment sections
- clickable column headers for sorting tables
- omission of categories that are completely empty
Notes and Limits
- This is a triage helper, not a full malware sandbox or decompiler.
- The deobfuscation stage is deterministic and heuristic-based; unsupported custom routines may still remain unresolved.
- Class-constant fallback mode provides useful indicators but less semantic context than full source scanning.
- Behavioral and signature detections are heuristic-based and may produce false positives or miss novel techniques.
- Network-based runtime C2 resolution and stage-2 enrichment are best-effort and may fail due to missing indicators, DNS failure, RPC issues, or decoding variance.
- External API enrichments (RatterScanner/JLab) are best-effort and may fail due to network issues, API errors, rate limits, or response format changes.
- JLab public scan is an external experimental endpoint; response fields and behavior may change over time.
- Metadata enrichments such as
SSDEEP,TLSH,TrID,Magika, andVhashare best-effort and only appear when dependencies are available. - Nested archive or payload extraction is heuristic and best-effort; highly custom packers may still evade static expansion.
- Do not rely on this tool alone to determine whether a Java application is safe.