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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
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.
If the probe does not detect any supported obfuscated call patterns, no deobfuscated copy is created and the source tree is scanned directly.
Current default probe threshold:
- Total
StringDecrypt.decrypt(...)+load(new int[]{...})calls >=1
Auto output folder naming for rewritten trees:
<target_name>_deobfuscated- if it exists:
<target_name>_deobfuscated_2,_3, etc.
Default report naming:
- scanning
ExampleModwritesExampleMod.jsonandExampleMod.html - scanning a directory such as
example_projectwritesexample_project.jsonandexample_project.html
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
Behavior scanning also includes:
- 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
Discord-focused detection includes:
- Bot tokens
- Webhook URLs (
discord.com/api/webhooks/...) - Snowflake IDs (
17-20digit IDs) - Contextual IDs in literals containing labels like
guild_id,channel_id,user_id,role_id, andapplication_id - Encrypted Chromium token marker blobs (
dQw4w9WgXcQ:<base64>) commonly used in token-stealer chains
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(
This helps avoid import-only or UI text being misclassified as file access. If outbound HTTP is also present in that file, an additional high-severity signal is raised for possible exfiltration.
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
If any username or UUID read appears with outbound HTTP, the tool emits possible_minecraft_identity_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()
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
When a module pack is detected, the report includes:
- module name
- description
- category
- source file
- aggregate category counts
This data is also exposed in JSON under minecraft_modules, and the HTML/Rich reports render a dedicated module summary when detection succeeds.
When enabled, Java Triage will attempt to upload the original source JAR/ZIP to:
https://jlab.threat.rip/api/public/static-scan
Behavior details:
- Enabled by default (
--jlab-static-scan) - Can be disabled with
--no-jlab-static-scan - Requires network access (disabled by
--no-network) - Upload target priority:
- source JAR metadata path/name fallback for directory scans that originated from a JAR
- scan root file if internal analysis root resolves to a
.jar/.zip
- Size and format guardrails:
- only
.jar/.zipare uploaded - max upload size handled by the tool:
50 MB
- only
Returned data is stored under jlab_static_scan 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)
The tool can generate an AI executive summary using either OpenAI or DeepSeek.
OPENAI_API_KEY: enables OpenAI Chat CompletionsDEEPSEEK_API_KEY: enables DeepSeek Chat CompletionsTRIAGE_LLM_PROVIDER: optional provider selector:auto(default): tries OpenAI first, then DeepSeekopenai: use only OpenAIdeepseek: use only DeepSeek
TRIAGE_OPENAI_MODEL: OpenAI model override (default:gpt-4.1-mini)TRIAGE_DEEPSEEK_MODEL: DeepSeek model override (default:deepseek-v4-flash)- Common values:
deepseek-v4-flash,deepseek-v4-pro
- Common values:
TRIAGE_DEEPSEEK_REASONING_EFFORT: DeepSeek reasoning effort (default:high)
If neither API key is present, the tool behaves as if this feature does not exist and does not mention AI in the output.
- 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
No package install is required for the script itself.
# optional, for rich UI output
pip install rich
# optional, for magika metadata enrichment
pip install magikaNew 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# Scan current directory
python java_triage.py
# Scan a specific unpacked source tree
python java_triage.py ./sample_project
# Disable default auto-decrypt copy or rewrite behavior
python java_triage.py ./sample_project --no-auto-decrypt
# Explicitly write a decrypted copy to a chosen path, then scan it
python java_triage.py ./sample_project --decrypt-codebase-out ./sample_project_deobf
# Rewrite in-place
python java_triage.py ./sample_project --decrypt-codebase-in-place
# Rewrite only, then skip the post-decrypt triage scan
python java_triage.py ./sample_project --no-rescan-after-decrypt
# Disable JSON output
python java_triage.py ./sample_project --no-json
# Save JSON report to a custom file
python java_triage.py ./sample_project --out report.json
# Disable HTML report output
python java_triage.py ./sample_project --no-html
# Save HTML report to a custom file
python java_triage.py ./sample_project --html-out report.html
# Disable all network lookups during analysis
python java_triage.py ./sample_project --no-network
# Disable stage-2 static analysis
python java_triage.py ./sample_project --no-analyze-stage2
# Disable JLab static scan enrichment
python java_triage.py ./sample_project --no-jlab-static-scan
# Wider rich output
python java_triage.py ./sample_project --rich-width 220
# Decipher a single .java file (no full scan)
python java_triage.py --decipher-only ./sample_project/suspicious/Helper.java
# Produce a deciphered copy + scan both
python java_triage.py ./sample_project --decipher-codebasetarget: 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
The following behavior IDs were added for explicit methodology coverage and can be searched directly in JSON output:
obf_xor_encoded_name_accessobf_base64_encoded_name_accessobf_caesar_encoded_name_accessobf_methodhandle_token_accessobf_lambdametafactory_token_accessobf_array_indirect_dispatch_token_accessobf_split_reassembled_name_accessobf_unsafe_field_token_accessobf_varhandle_field_token_accessobf_stackwalker_indirect_accessobf_int_array_encoded_name_accessobf_classloader_bypass_token_accesstoken_class_sweep_static_field_harvesttoken_spin_race_window_harvesttoken_yggdrasil_internal_probetoken_process_commandline_harvesttoken_processhandle_commandline_probetoken_runtime_mxbean_arg_probetoken_system_property_auth_probetoken_environment_auth_probetoken_sun_java_command_probetoken_jdk_internal_process_probedataflow_token_to_network_sinkdataflow_username_to_network_sinkdataflow_uuid_to_network_sinktoken_bootstrap_constructor_capturetoken_authlib_deep_hook_accesstoken_connection_authorization_header_probetoken_urlconnection_requests_unsafe_probetoken_connection_spin_race_header_harvestblockchain_dns_c2_resolverraw_socket_http_post_clientproof_minecraft_token_raw_socket_exfil_chaintwo_payload_exfil_architecturepersistence_filesystem_copy_relaunch_chainpersistence_detached_process_relaunchc2_fallback_domainpayload_download_endpointpersistence_install_directorypython_executable_referencepython_script_referenceexfil_endpoint_prefiremcexfil_endpoint_submit_logpython_subprocess_argument_chaindetached_process_runtime_indicatorminecraft_coordinate_exfiltrationdiscord_webhook_url_reassemblymulti_path_exfil_breakdowninline_xor_string_decodersensitive_game_data_comment
The decipher section in JSON reports contains counts of XOR strings replaced and files changed when --decipher-codebase is used (enabled by default).
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
JSON output includes the full scan payload, including:
target_metadatascan_diagnostics(per-scan-root breakdown of java_files, class_files, finding_count, scan_mode)runtime_c2url_assembly(assembled C2 URLs with domain, method, path, description)infra_probe(live probe results per endpoint)stage2_analysisblockchain_indicatorsnetwork_endpoint_assessmentvariant_detectionsraw_string_detectionsheuristic_detectionsratter_scannerjlab_static_scandecipher(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
HTML output is a standalone styled report and includes:
- 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
- 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.
