-
Notifications
You must be signed in to change notification settings - Fork 1
YARA X Modules
Complete function-level reference for the custom YARA-X modules in the HydraDragonAntivirus yara-x fork, the standard dex module, every shipped rule file, and the offline .yar → .yrc build chain.
The mobile app pulls the engine from a git dependency (not a submodule):
yara-x = { git = "https://github.com/HydraDragonAntivirus/yara-x", features = ["pulley", "hydradragon-module", "dex-module"] }The pulley feature selects wasmtime's portable bytecode interpreter (no JIT) so a single compiled .yrc loads across all ABIs and on 16 KB-page Android devices. The module features MUST match between the offline compiler and the on-device hydradragonclamav, because the serialized ruleset is backend-specific.
See also: YARA-X-Usage for the usage-level overview; Rust-API-Reference for how the Rust side populates module metadata; Detection-Engines for how rules combine with other engines.
The Koodous androguard YARA module (Apache-2.0, "The Koodous Authors") was ported from C to YARA-X and folded into the single hydradragon module — there is no separate androguard module. Rules call these functions with the hydradragon. prefix and import "hydradragon". Like yara-x's cuckoo module, it does not parse the scanned file itself — it consumes an external JSON report handed to the scanner as module metadata under the key "hydradragon" and exposes functions to query it.
Source in the fork: lib/src/modules/hydradragon/mod.rs + schema.rs + protos/hydradragon.proto (the AndroguardJson-side exports live in the same module as the HIPS exports below).
set_module_metadata("hydradragon", json_bytes) is called by hydradragonclamav before each scan. In the module's main(ctx, _data):
-
ctx.get_module_metadata(...)— ifNone/empty, an emptyAndroguardJsonis stored thread-local and an empty protobuf returned (no error). - Otherwise parsed with
serde_json::from_slice::<schema::AndroguardJson>. On failure, the empty default is stored and aModuleError::MetadataErrorreturned. - On success, the integer fields exposed as protobuf fields are populated (
min_sdk/max_sdk/target_sdk/permissions_number), and the full parsed JSON is stored in a thread-localRcso the exported functions can read it during evaluation.
The JSON is built on-device by build_hydradragon_json in hydradragonandroid/src/lib.rs from a parsed binary AndroidManifest.xml plus a URL sweep and the PKCS#7 signing certificate, then merged with the DEX findings/API histogram by merge_dex_findings — see Rust-API-Reference for the exact keys emitted.
| Field (JSON key) | Rust type | Notes |
|---|---|---|
package_name |
Option<String> |
App package |
app_name |
Option<String> |
Displayed app name |
main_activity |
Option<String> |
The MAIN/LAUNCHER activity (None/empty ⇒ "hidden") |
activities |
Option<Vec<String>> |
All declared activities |
services |
Option<Vec<String>> |
All declared services |
receivers |
Option<Vec<String>> |
Broadcast receivers |
urls |
Option<Vec<String>> |
URLs found in the APK |
permissions |
Option<Vec<String>> |
Manifest-declared permissions |
new_permissions |
Option<Vec<String>> |
"New" permissions (added vs. a baseline) |
certificate |
Option<CertificateJson> |
Signing cert |
min_sdk_version |
Option<i64> |
Lenient deserializer (string-or-int) |
max_sdk_version |
Option<i64> |
Lenient deserializer (string-or-int) |
target_sdk_version |
Option<i64> |
Lenient deserializer (string-or-int) |
meta_data |
Option<Vec<MetaDataEntry>> |
Manifest <meta-data> name/value pairs |
miner_events |
Option<Vec<MinerEventJson>> |
Crypto-miner CPU+memory events |
CertificateJson: subjectDN (renamed subject_dn), IssuerDN (renamed issuer_dn), sha1 — all Option<String>.
MetaDataEntry: name: Option<String>, value: Option<String> — one per <meta-data android:name=… android:value=…/> element in AndroidManifest.xml. Populated on-device by parse_manifest in hydradragonandroid/src/lib.rs (capped at 256 entries).
MinerEventJson: package_name: Option<String>, cpu_usage: Option<f64> (0.0–1.0), memory_mb: Option<i64>, known_name: Option<bool>, is_malicious: Option<bool>.
The SDK-version deserializer accepts a JSON string ("19"), a bare number (19), or null/absent — mirroring the original C module's lenient atoi. On a non-parseable leading integer it yields 0.
Every "search" export has two overloads with the same name: a regex form (RegexId arg, matched via ctx.regexp_matches) and a string form (RuntimeString arg, case-insensitive equality via eq_ignore_ascii_case, matching the C module's strcasecmp). List queries return 1/0 for "any element matches"; single-value queries check the one value.
| Export | Args | Returns | Checks |
|---|---|---|---|
hydradragon.certificate.issuer(re) |
RegexId |
i64 (1/0) |
regex match against certificate.IssuerDN
|
hydradragon.certificate.issuer(str) |
RuntimeString |
i64 |
case-insensitive equality with certificate.IssuerDN
|
hydradragon.certificate.subject(re) |
RegexId |
i64 (1/0) |
regex match against certificate.subjectDN
|
hydradragon.certificate.subject(str) |
RuntimeString |
i64 |
case-insensitive equality with certificate.subjectDN
|
hydradragon.certificate.sha1(str) |
RuntimeString |
i64 |
case-insensitive equality with certificate.sha1 (string form only) |
hydradragon.url(re) |
RegexId |
i64 (1/0) |
any element of urls matches the regex |
hydradragon.url(str) |
RuntimeString |
i64 |
any element of urls equals (case-insensitive) |
hydradragon.app_name(re) |
RegexId |
i64 (1/0) |
regex match against app_name
|
hydradragon.app_name(str) |
RuntimeString |
i64 |
case-insensitive equality with app_name
|
hydradragon.permission(re) |
RegexId |
i64 (1/0) |
any element of permissions or new_permissions matches |
hydradragon.permission(str) |
RuntimeString |
i64 |
any element of permissions or new_permissions equals |
hydradragon.activity(re) |
RegexId |
i64 (1/0) |
any element of activities matches |
hydradragon.activity(str) |
RuntimeString |
i64 |
any element of activities equals |
hydradragon.main_activity(re) |
RegexId |
i64 (1/0) |
regex match against main_activity
|
hydradragon.main_activity(str) |
RuntimeString |
i64 |
case-insensitive equality with main_activity
|
hydradragon.service(re) |
RegexId |
i64 (1/0) |
any element of services matches |
hydradragon.service(str) |
RuntimeString |
i64 |
any element of services equals |
hydradragon.receiver(re) |
RegexId |
i64 (1/0) |
any element of receivers matches |
hydradragon.receiver(str) |
RuntimeString |
i64 |
any element of receivers equals |
hydradragon.package_name(re) |
RegexId |
i64 (1/0) |
regex match against package_name
|
hydradragon.package_name(str) |
RuntimeString |
i64 |
case-insensitive equality with package_name
|
hydradragon.min_sdk |
(none) |
int64 field |
set from min_sdk_version
|
hydradragon.max_sdk |
(none) |
int64 field |
set from max_sdk_version
|
hydradragon.target_sdk |
(none) |
int64 field |
set from target_sdk_version
|
hydradragon.permissions_number |
(none) |
int64 field |
count of declared permissions |
hydradragon.metadata(re) |
RegexId |
i64 (1/0) |
1 if any <meta-data> element's name matches the regex |
hydradragon.metadata(str) |
RuntimeString |
i64 (1/0) |
1 if any <meta-data> element's name equals (exact, case-sensitive) |
hydradragon.rootkit_behavior() |
(none) |
i64 (1/0) |
stealth-rootkit composite — see below |
hydradragon.device_admin_permission() |
(none) |
i64 (1/0) |
1 if BIND_DEVICE_ADMIN is declared (regardless of icon-hiding) |
The "no icon + high-privilege/persistence permission" combination. The module does not walk the manifest's intent-filters itself; it trusts that the host already collapsed "has an enabled activity with both android.intent.action.MAIN and android.intent.category.LAUNCHER" into the main_activity string (present ⇒ launchable, absent/empty ⇒ hidden).
Computation:
-
hidden = main_activity is None or empty. If not hidden, return0immediately. - Check the suspicious-permission set
ROOTKIT_SUSPICIOUS_PERMSagainstpermissionsandnew_permissions(case-insensitive):BIND_DEVICE_ADMIN,BIND_ACCESSIBILITY_SERVICE,SYSTEM_ALERT_WINDOW,REQUEST_INSTALL_PACKAGES,RECEIVE_BOOT_COMPLETED,QUERY_ALL_PACKAGES,WRITE_SECURE_SETTINGS,BIND_NOTIFICATION_LISTENER_SERVICE,PACKAGE_USAGE_STATS. - Return
1if any suspicious permission is present, else0.
This permission list mirrors the host app's Java heuristic ScanEngine.ROOTKIT_SUSPICIOUS_PERMS. The rule hidden_icon_rootkit at the end of hydradragon.yar calls hydradragon.rootkit_behavior() == 1.
Reads the meta_data array (the <meta-data android:name=… android:value=…/> elements parsed from AndroidManifest.xml). Two overloads, both matching against the entry name (not value): a regex form (ctx.regexp_matches) and an exact-string form. Returns 1 if any entry's name matches, else 0.
This closes a gap for malware that keys off custom manifest metadata. For example, Android.DownLoader.1049.origin reads RSOTA_APP_ID and RSOTA_CHANNEL_ID from the host app's metadata — a rule can now flag them directly:
import "hydradragon"
rule Android_DownLoader_1049_RSOTA {
meta:
description = "Android.DownLoader.1049.origin RSOTA metadata markers"
condition:
hydradragon.metadata("RSOTA_APP_ID") or
hydradragon.metadata("RSOTA_CHANNEL_ID")
}
The on-device parse_manifest handles the "meta-data" element and emits {"name":…,"value":…} pairs into the hydradragon JSON (capped at 256). Value-based matching is not exposed yet — only the name is queried.
The project's unified HIPS + static-analysis module for Android. It provides network-level, behavioral-level, and static APK-analysis signals. Like cuckoo it consumes an external JSON report under the metadata key "hydradragon" (dynamic HIPS/network + static APK report merged into one object) and exposes functions to query it; it does not parse files. Gated by the hydradragon-module feature.
Source in the fork: lib/src/modules/hydradragon/mod.rs (841 lines) + schema.rs (276 lines) + protos/hydradragon.proto (the proto message Hydradragon {} is empty — all surface area is #[module_export] functions reading the thread-local JSON).
The JSON is built on-device by merge_dex_findings in hydradragonandroid/src/lib.rs, which starts from Java's live-network/HIPS report, merges in the static APK report (build_hydradragon_json), and folds in DEX static-analysis findings + the API-call histogram — see Rust-API-Reference.
| Field (JSON key) | Rust type | Purpose |
|---|---|---|
network |
Option<NetworkJson> |
DNS + packet capture data |
urls |
Option<Vec<String>> |
Full URLs observed live (host+path) |
screen_text |
Option<String> |
OCR screen-capture text |
ui_spam_events |
Option<Vec<UISpamEventJson>> |
UI click/window spam events |
notification_spam_events |
Option<Vec<NotificationSpamEventJson>> |
Notification spam events |
clickjack_events |
Option<Vec<ClickjackEventJson>> |
Clickjacking events |
ransomware_events |
Option<Vec<RansomwareEventJson>> |
Ransomware rename-burst events |
canary_events |
Option<Vec<CanaryEventJson>> |
Decoy-file trap hits |
network_events |
Option<Vec<NetworkEventJson>> |
Per-app network connection summaries |
strandhogg_events |
Option<Vec<StrandHoggEventJson>> |
StrandHogg task-hijack events |
removal_resistance_events |
Option<Vec<RemovalResistanceEventJson>> |
Uninstall/device-admin "kick" events |
launcher_change_events |
Option<Vec<LauncherChangeEventJson>> |
Default-home hijack attempts |
system |
Option<SystemEventJson> |
Device/system state |
behavior_flags |
Option<Vec<BehaviorFlagsJson>> |
Per-package flag arrays |
behavior_state |
Option<BehaviorStateJson> |
Foreground + observed packages |
dex_findings |
Option<Vec<DexFindingJson>> |
Static DEX-analysis findings |
api_calls |
Option<Vec<String>> |
Unique API calls in Lpkg/Cls;->method(params)return format (stored as "sig\tcount") |
NetworkJson (custom deserializer tolerates both new domains[] of {domain} and legacy dns[] of {hostname}, converting the latter; skips null hosts/packets):
-
domains: Option<Vec<DomainJson>>— DNS-resolved domains (DomainJson { domain: Option<String> }) -
hosts: Option<Vec<String>>— destination IPs from DNS resolution -
packets: Option<Vec<CapturedPacketJson>>— ~50 recent packets
CapturedPacketJson: src_ip, dst_ip (Option<String>), src_port, dst_port (Option<i32>), protocol (Option<String>, "TCP"/"UDP"), payload_b64 (Option<String>, base64 of first 2048 bytes max).
UISpamEventJson: package_name, click_count: Option<i64>, window_count: Option<i64>, time_window_seconds: Option<i64>, is_malicious: Option<bool>.
NotificationSpamEventJson: package_name, notification_count: Option<i64>, time_window_seconds: Option<i64>, is_malicious: Option<bool>.
ClickjackEventJson: package_name, rapid_clicks: Option<i64>, target_package: Option<String>, time_window_seconds: Option<i64>, is_malicious: Option<bool>.
RansomwareEventJson: package_name, rename_count: Option<i64>, appended_suffix: Option<String>, access_granted: Option<bool>, is_all_files: Option<bool>, time_window_seconds: Option<i64>, is_malicious: Option<bool>.
CanaryEventJson: package_name, canary_triggered: Option<bool>.
NetworkEventJson: package_name, connection_count: Option<i64>, unique_hosts: Option<i64>, dns_queries: Option<i64>.
StrandHoggEventJson: package_name, activity_count: Option<i64>, is_suspicious: Option<bool>.
RemovalResistanceEventJson: package_name, kick_count: Option<i64>, screen_kind: Option<String>, time_window_seconds: Option<i64>, is_malicious: Option<bool>.
LauncherChangeEventJson (emitted by HipsMonitor on PackageManager.clearPackagePreferredActivities, addPreferredActivity, or RoleManager.ROLE_HOME): package_name, changed: Option<bool> (true if launcher actually changed), method: Option<String> ("clearPackagePreferredActivities" / "addPreferredActivity" / "role_manager" / "category_home_registration"), is_suspicious: Option<bool>.
SystemEventJson: is_rooted: Option<bool>, is_debug_mode: Option<bool>, is_self_protection_triggered: Option<bool>, package_name: Option<String>.
BehaviorFlagsJson: package_name: Option<String>, flags: Option<Vec<String>>.
BehaviorStateJson: foreground_package: Option<String>, observed_packages: Option<Vec<String>>.
DexFindingJson: severity: Option<String>, kind: Option<String>, class_descriptor: Option<String>, message: Option<String>.
| Export | Args | Returns | Checks / computes |
|---|---|---|---|
hydradragon.network.dns_lookup(re) |
RegexId |
i64 count |
count of network.domains[] whose domain matches |
hydradragon.network.host(re) |
RegexId |
i64 count |
count of network.hosts[] matching |
hydradragon.network.payload_hex(hexstr) |
RuntimeString |
i64 count |
hex-decodes the needle; for each packet, base64-decodes payload_b64 and counts packets whose decoded payload contains the byte pattern (sliding window), OR whose src_ip/dst_ip/protocol/src_port/dst_port (string form) contains the needle bytes. Suricata-style payload matching. |
hydradragon.network.http_request(re) |
RegexId |
i64 count |
per TCP packet, base64-decode payload, parse as HTTP request; count if the URI matches (any method) |
hydradragon.network.http_get(re) |
RegexId |
i64 count |
same, method == GET only |
hydradragon.network.http_post(re) |
RegexId |
i64 count |
same, method == POST only |
hydradragon.network.http_user_agent(re) |
RegexId |
i64 count |
per TCP packet with a parseable HTTP request, count if the User-Agent header matches |
hydradragon.network.tcp(re) |
RegexId |
i64 count |
per TCP packet, count if dst_ip OR dst_port (as string) matches |
hydradragon.network.udp(re) |
RegexId |
i64 count |
same as tcp but for UDP packets |
hydradragon.url(re) |
RegexId |
i64 count |
count of top-level urls[] matching |
hydradragon.url(str) |
RuntimeString |
i64 count |
count of top-level urls[] equal (case-insensitive) |
hydradragon.screen_text(re) |
RegexId |
i64 (1/0) |
1 if screen_text (OCR text) matches, else 0 |
parse_http_from_payload (internal): UTF-8 decodes the payload, takes the request line, splits into method+uri, uppercases method and validates against GET|POST|PUT|DELETE|HEAD|OPTIONS|PATCH|CONNECT, then scans header lines (case-insensitively) for User-Agent.
Most behavioral functions take a package_re: RegexId and return a summed score (i64), not a boolean — they filter events whose package_name matches the regex, then accumulate a score from the event's numeric fields with bonuses for short time windows and is_malicious. The /./ regex (matches any package) is the common "any app" form used in the rule files.
| Export | Args | Returns | Score computation (per matching event, summed) |
|---|---|---|---|
hydradragon.ui_spam(package_re) |
RegexId |
i64 score |
click_count (default 1) + window_count (default 0); if time_window_seconds in (0,60) ×2; if is_malicious ×2 |
hydradragon.notification_spam(package_re) |
RegexId |
i64 score |
notification_count (default 0); if time_window_seconds in (0,60) ×2; if is_malicious +10 (saturating) |
hydradragon.clickjack(package_re) |
RegexId |
i64 score |
rapid_clicks (default 0); if is_malicious +5; if time_window_seconds in (0,60) ×2; if target_package set +2 |
hydradragon.ransomware_behavior(package_re) |
RegexId |
i64 score |
rename_count (default 0); if access_granted AND is_all_files +5; if appended_suffix non-empty +3; if time_window_seconds in (0,60) ×2; if is_malicious ×2 |
hydradragon.canary_triggered(package_re) |
RegexId |
i64 (1/0) |
first canary event with canary_triggered == true AND package_name matching ⇒ 1; else 0 (short-circuit, not a sum) |
hydradragon.strandhogg(package_re) |
RegexId |
i64 score |
filter to is_suspicious == true; per event 1 + activity_count (default 0); summed |
hydradragon.rooted() |
(none) |
i64 score |
system.is_rooted ⇒ +1, system.is_self_protection_triggered ⇒ +1 (0/1/2) |
hydradragon.debug_mode() |
(none) |
i64 (1/0) |
system.is_debug_mode as i64 |
hydradragon.system_package(package_re) |
RegexId |
i64 (1/0) |
1 if system.package_name matches |
hydradragon.behavior_flagged(package_re) |
RegexId |
i64 count |
per behavior_flags[] entry whose package_name matches, add flags.len(); summed |
hydradragon.foreground_package(package_re) |
RegexId |
i64 (1/0) |
1 if behavior_state.foreground_package matches |
hydradragon.observed_packages(package_re) |
RegexId |
i64 count |
count of behavior_state.observed_packages[] matching |
hydradragon.network_connections(package_re) |
RegexId |
i64 score |
per matching network_events[]: connection_count + unique_hosts + dns_queries; summed |
hydradragon.removal_resistance(package_re) |
RegexId |
i64 score |
per matching removal_resistance_events[]: kick_count (default 0); if time_window_seconds in (0,60) ×2; if is_malicious ×2; summed |
hydradragon.launcher_change(package_re) |
RegexId |
i64 score |
per matching launcher_change_events[]: start at 1; if changed == true +3; if is_suspicious == true ×2 (saturating); summed. So an attempt=1, an actual change=4, a suspicious actual change=8. The method field is informational only — not read by the export. |
hydradragon.miner_count(package_re) |
RegexId |
i64 count |
number of miner_events[] whose package_name matches |
hydradragon.miner_cpu(package_re) |
RegexId |
i64 max CPU % |
max sustained CPU usage (0–100) among matching miner events |
hydradragon.miner_memory(package_re) |
RegexId |
i64 max MB |
max resident memory (MB) at detection among matching miner events |
hydradragon.miner_known_name(name_re) |
RegexId |
i64 (1/0) |
1 if any miner_events[].known_name == true whose package_name matches, else 0 |
| Export | Args | Returns | Checks |
|---|---|---|---|
hydradragon.dex_finding(re) |
RegexId |
i64 count |
count of dex_findings[] whose message matches (any severity) |
hydradragon.dex_severe_finding_count() |
(none) |
i64 count |
count of dex_findings[] whose severity is "High" or "Critical"
|
hydradragon.api_call(re) |
RegexId |
i64 sum |
iterates api_calls[] (each "sig\tcount"); for each entry matching the regex, takes the part after the last tab (the count) and parses it as i64; sums the counts. The regex is matched against the full "sig\tcount" string, but in practice the \t won't appear in a signature-only regex. |
Added in fork commit 5a33aebb ("feat(hydradragon): add launcher_change export function + schema"). Schema LauncherChangeEventJson and the launcher_change_events field. Score: attempt=1, actual change=4 (1+3), suspicious actual change=8. The export is present and compiled into the shipped .so, but no rule currently calls it — the launcher-hijack rules in hips_rules_filtered_verified.yar use foreground_package + notification_spam + ui_spam + strandhogg + hydradragon.activity(/HOME/) instead. The infrastructure is ready; a rule is the missing piece.
Unlike hydradragon (which consumes external metadata), the dex module does parse the scanned file. main(_ctx, data) calls parser::Dex::parse(data); on success returns the populated Dex, on failure returns Dex::new() with set_is_dex(false). Gated by the dex-module feature. This is upstream yara-x's DEX module; the on-device scanner also does its own DEX parsing via the separate dex-core/dex-analysis crates, and the dex module lets YARA rules query the parsed DEX directly.
| Export | Args | Returns | Checks |
|---|---|---|---|
dex.checksum |
(none) | Option<i64> |
Adler32 of bytes from offset 12 onward (cached); compare to dex.header.checksum
|
dex.signature |
(none) | Option<Lowercase<FixedLenString<40>>> |
SHA-1 of bytes from offset 32 onward (cached, hex lowercase, 40 chars); compare to dex.header.signature
|
dex.contains_string(str) |
RuntimeString |
Option<bool> |
binary-search the sorted dex.strings pool for the exact string |
dex.contains_method(str) |
RuntimeString |
Option<bool> |
any dex.methods[] whose name equals |
dex.contains_class(str) |
RuntimeString |
Option<bool> |
binary-search dex.class_defs[] by class descriptor |
The protobuf schema (dex.proto) exposes is_dex, header (DexHeader: magic, version, checksum, signature, file_size, endian_tag, …), strings, types, protos, fields, methods, class_defs (each with access_flags rendered as AccessFlag enums), and map_list.
All located in the repo's yara-x/ directory. Every file is committed. Compiled .yrc files are deployed to app/src/main/assets/scan/ and loaded by the native engine at cold start. Rule counts use anchored ^rule .
| File | Size | Rules | Imports | Character |
|---|---|---|---|---|
hydradragon.yar |
84 KB | 116 |
hydradragon, math
|
Project's own, hand-curated (Koodous-derived) |
hips_rules_filtered_verified.yar |
18 KB | 60 |
hydradragon, math
|
Project's own, hand-curated HIPS behavioral |
machine_learning_apk.yar |
397 KB | 330 | none | Project's own, auto-generated (uniform structure) |
clean_rules_filtered_verified.yar |
464 KB | 317 |
hash, elf, console, math, time
|
Third-party, filtered+verified (ELF/Linux) |
valhalla-rules_filtered_verified.yar |
97 KB | 94 | none | Third-party, filtered+verified (VALHALLA demo) |
emerging-all.yar |
25.5 MB | 49,364 | hydradragon |
Third-party, large generated bundle (Emerging Threats Suricata-converted) |
Committed but NOT shipped (dropped by the compile tool): clean_rules_filtered_unverified.yar, valhalla-rules_filtered_unverified.yar (both _unverified), and the raw valhalla-rules.yar / clean_rules.7z source archives.
Android APK malware: adware, droppers, SMS fraud, bankers, fake apps, ransomware, RATs/spyware, rootkits, Dendroid, Marcher, Koler, Mazar, Triada, FinSpy, HackingTeam, Metasploit/meterpreter, etc. GNU-GPLv2; the Koodous androguard functionality (now part of the hydradragon module) is credited to Koodous and rewritten in Rust. Authored by Fernando Denis Ramirez, plutec_net, Jacob Soo Lead Re, Tim Strazzere, Thorsten Schröder, and others, plus several HydraDragonAV-authored rules.
Representative condition structures (paraphrased):
-
adware : ads—all of ($string_*)over banner/adpath literals. -
BaDoink—hydradragon.app_name("BaDoink")OR obfuscated string literals ORall of ($type_b*). -
assd_developer—hydradragon.certificate.sha1("ED9A…C34A")(cert SHA-1 match). -
Android_Godlike—hydradragon.service(/godlike\.s/i)ANDhydradragon.service(/godlike\.g/i)ANDhydradragon.receiver(/godlike\.e/i), OR alibgodlikelib.sostring. -
finspy— a regex matching a ZIP local-file-header pattern with$re and (#re > 50)(count-based). -
HackingTeam_Android— Dalvik bytecode hex patterns for the implant's decryptor. -
hidden_icon_rootkit—hydradragon.rootkit_behavior() == 1(the stealth-rootkit composite).
Other notable rule families: Mapin/dropperMapin, fake_facebook/fake_instagram/fake_whatsapp/fake_market/fake_minecraft, koler_*, marcher*, android_mazarBot_z, android_meterpreter/android_metasploit/Metasploit_Payload, Android_Triada, SandroRat/androrat/sandrorat, SlemBunk, Banker_Acecard, Android_RuMMS, spynote_variants/SpyNet, Trojan_Dendroid/Trojan_Droidjack, VikingBotnet.
Behavioral HIPS rules for the hydradragon module, plus three static adware rules using its static APK-analysis exports. Every rule has severity, category, and suggestion meta fields (suggestion is "warn" or "uninstall"). Categories: UI_SPAM, NOTIFICATION_SPAM, CLICKJACK, RANSOMWARE, CANARY, STRANDHOGG, SYSTEM, BEHAVIOR, FOREGROUND, URL, DEX, OBSERVED, HTTP, NETWORK, ADWARE, MINER, FILE_READ, FILE_CREATED, FILE_COPY, FILE_EXTENSION_ADDED, FILE_EXTENSION_CHANGE, WIPER.
Full list:
-
UI spam:
HIPS_UI_Spam(ui_spam(/./) >= 30),HIPS_UI_Spam_Excessive(>= 100). -
Notification spam:
HIPS_Notification_Spam(notification_spam(/./) >= 20),…_Excessive(>= 50). -
Clickjacking:
HIPS_Clickjack(clickjack(/./) >= 3),HIPS_Clickjack_PackageInstaller(clickjack(/com\.android\.packageinstaller/) >= 1). -
Ransomware:
HIPS_Ransomware(ransomware_behavior(/./) >= 5),HIPS_Ransomware_Mass_Encryption(>= 20),HIPS_Ransomware_And_HighMemory(>= 5ANDminer_memory(/./) >= 64),HIPS_Ransomware_With_MemoryFlag(>= 5ANDminer_memory(/./) >= 1),HIPS_Ransomware_All_Sensors(>= 5ANDminer_memory(/./) >= 64AND entropy flag). -
Canary traps:
HIPS_Canary_Triggered(canary_triggered(/./) >= 1),HIPS_Canary_And_Flags(canary ANDbehavior_flagged(/./) >= 1). -
StrandHogg:
HIPS_StrandHogg(strandhogg(/./) >= 1),HIPS_StrandHogg_With_Flags. -
System:
HIPS_Rooted(rooted() >= 1),HIPS_Debug_Mode(debug_mode() >= 1),HIPS_Rooted_And_Debug. -
Behavioral combos:
HIPS_Multiple_Flags(behavior_flagged(/./) >= 3),HIPS_Extensive_Flags(>= 5),HIPS_UI_And_Notification_Spam,HIPS_Ransomware_And_Canary,HIPS_Network_And_Flags(network_connections(/./) >= 10ANDbehavior_flagged(/./) >= 1). -
Foreground:
HIPS_Foreground_Threat(foreground_package(/./) >= 1ANDbehavior_flagged(/./) >= 1). -
URLs:
HIPS_Malicious_URL(url(/…tor2web|onion|bitcoin|malware|exploit|shell|backdoor|rat…/) >= 1),HIPS_Phishing_URL(regex over login/signin/verify/paypal/2fa-bypass),HIPS_URL_And_Flags. -
DEX static:
HIPS_DEX_And_Behavior(DEX finding ANDbehavior_flagged(/./) >= 1). -
System package:
HIPS_Suspicious_System_Package(system_package(/…spy|stalk|camera|keylogger|trojan|malware|rat|backdoor…/) >= 1). -
Observed:
HIPS_Multiple_Observed_Packages(observed_packages(/./) >= 50). -
HTTP/network:
HIPS_HTTP_Suspicious_Request(network.http_request(/…admin|shell|cmd|exec|upload|backdoor|wp-admin…/) >= 1),HIPS_HTTP_Data_Exfil(network.http_post(/…upload|send|data|log|collect|sync|gate…/) >= 1ANDbehavior_flagged(/./) >= 1),HIPS_HTTP_Suspicious_UserAgent(network.http_user_agent(/…curl|wget|python|okhttp|dalvik…/) >= 1),HIPS_TCP_Suspicious_Port(network.tcp(/^(4444|5555|6666|1337|8080…)$/) >= 1),HIPS_TCP_Unknown_Service,HIPS_UDP_Suspicious_Port. -
Adware (behavioral):
HIPS_Adware_Aggressive_Notification_UI,HIPS_Adware_Launcher_Hijack_Behavior,HIPS_Adware_Overlay_Abuse,HIPS_Adware_Ad_Network_URL_UI_Spam,HIPS_Adware_Boot_Persist_Notification_Spam,HIPS_Adware_Multiple_Ad_Network_Connections. -
Adware (static):
Android_Murder_Back_Look_Win(hydradragon.package_name(/com\.murder\.back\.look\.win/)),Android_Aggressive_Adware_Launcher_Hijack(hydradragon.activity(/android\.intent\.category\.HOME/)AND 2+ ad-SDK strings AND one of SYSTEM_ALERT_WINDOW/RECEIVE_BOOT_COMPLETED/FOREGROUND_SERVICE),Android_Launcher_Hijack_Hidden_Ads. -
Packed device-admin rootkit:
HIPS_Packed_DeviceAdmin_HiddenRootkit— packed APK that requestsBIND_DEVICE_ADMINand suppresses its own icon. -
Crypto-miner:
HIPS_Miner_BehaviorFlag(miner_count(/./) >= 1),HIPS_Miner_HighCpuAndMemory(miner_cpu(/./) >= 50ANDminer_memory(/./) >= 40),HIPS_Miner_KnownProcess(miner_known_name(/./) >= 1),HIPS_Miner_MultipleFlags(miner_count(/./) >= 1ANDbehavior_flagged(/./) >= 1). -
File read estimation:
HIPS_FileRead_Detected(FILE_READflag present),HIPS_FileRead_HighConfidence(confidence ≥80),HIPS_FileRead_DataExfil(confidence ≥80 ANDbehavior_flagged(/./) >= 3),HIPS_FileRead_WithNetwork(file read ANDnetwork_connections(/./) >= 5). -
File created:
HIPS_FileCreated(anyFILE_CREATEDflag),HIPS_FileCreated_Burst(5+ creates in 60s),HIPS_FileCreated_WithNetwork(creates + network). -
File copy correlation:
HIPS_FileCopy(anyFILE_COPYflag — read + similarly-sized create),HIPS_FileCopy_HighConfidence(confidence ≥80),HIPS_FileCopy_WithNetwork(copy + network).
APK malware signatures auto-generated from the project's ML pipeline. Author HydraDragonAntivirus, GPLv2. Each rule is named after a malware sample's hash (sig_<hash> or the bare SHA-256) and matches a few distinctive fullword ascii strings from that APK. All conditions are structurally identical: uint16(0) == 0x4b50 and all of them — the file must start with PK (ZIP/APK magic) AND all the rule's string literals present.
General malware, heavily Linux/ELF-focused: LinuxDDOS, Hive ransomware (ELF), libprocesshider, RansomExx, Mirai/Hajime, EquationGroup toolset, APT backdoors (REPTILE, VIRTUALSHINE, MOPSLED), P2pinfect, Pumakit, Chaos RAT, BPFDoor, CamaroDragon, SEASPY/UNC4841. Filtered+verified subset of the larger unverified bundle (raw source archived in clean_rules.7z). Uses uint32(0) == 0x464c457f (ELF magic) gating and the elf module for ELF-targeted rules.
VALHALLA demo rule set (Nextron-Systems / Florian Roth & contributors), CC-BY-NC, retrieved 2026-06-27. Covers exploits (DirtyFrag, libSSH auth bypass, OMI RCE CVE-2021-38647), APT malware (UNC4841 SEASPY, BPFDoor/RedMenshen, Penguin/Turla, Academic Camp), Linux/ELF malware (PLAGUE, Xlogin, Perfctl, Saltwater, ReverseShell), ransomware (ELF ESXi), Mirai, supply-chain (xz/CVE-2024-3094, NPM). Rules have a numeric score meta field and minimum_yara = "3.5.0".
Emerging Threats Suricata/Snort-style network IDS signatures converted to YARA-X. Rule names prefixed ET_ET_… cover attack responses, shellcode, NETBIOS/MS08-067, P2P, games, exploits, malware C2. Each rule's meta has sid (Suricata signature ID), rev, classtype, source = "EmergingThreats". The condition is typically one or more hydradragon.network.payload_hex("<hex>") >= 1 calls — the hex patterns are the original Suricata content byte sequences. This is the only rule file besides hips_rules_filtered_verified.yar that imports hydradragon, and the only one that uses the network.payload_hex export. Despite being 25.5 MB source / 13.4 MB compiled, it ships.
Offline compiler: dev-tools/hydradragon_yara_x_compile/ (src/main.rs, 185 lines). Same yara-x features as the on-device crate.
CLI: hydradragon_yara_x_compile [--check] [--filtered] <source_dir> [<output_dir>]
-
--check— validate compilation only, do not write.yrc. -
--filtered— only compile*_filtered.yarfiles. -
<source_dir>— scanned recursively for.yarfiles. -
<output_dir>— defaults toapp/src/main/assets/scanrelative to the source dir's parent.
Filtering logic (the reason only six .yrc ship):
-
Drop
_unverified— any filename ending_unverified.yaris removed (those reference undeclared private rules and fail to compile). -
Drop raw rulesets that have a filtered sibling — a raw
<base>.yaris kept only if no<base>_filtered*.yarexists. Soclean_rules.yaris skipped becauseclean_rules_filtered_verified.yarexists. -
--filteredfurther restricts to filenames ending_filtered.yar.
Per file: output path <out_dir>/<stem>.yrc; incremental skip if the .yrc exists and its mtime ≥ the .yar mtime; otherwise Compiler::new() → add_source → build() → rules.serialize() → fs::write. On any compile error the file is skipped and counted; the tool exits non-zero if any file failed.
| File | Bytes |
|---|---|
hydradragon.yrc |
104,552 |
clean_rules_filtered_verified.yrc |
406,942 |
emerging-all.yrc |
13,378,439 |
hips_rules_filtered_verified.yrc |
27,094 |
machine_learning_apk.yrc |
388,207 |
valhalla-rules_filtered_verified.yrc |
83,105 |
These six .yrc correspond exactly to the six .yar that survived the filter rules. The .yrc format is the pulley-backend serialized ruleset that hydradragonclamav loads on-device via the matching pulley feature. The yara-x crate version pinned in hydradragonandroid/Cargo.lock is 1.19.0 from the fork commit 5a33aebb.
# After editing yara-x/*.yar:
cargo run --release --manifest-path dev-tools/hydradragon_yara_x_compile/Cargo.toml -- yara-x/ app/src/main/assets/scan/
# Then rebuild the .so (the module metadata contract is in hydradragonandroid/src/lib.rs):
cd hydradragonandroid && build-android.cmdIf you change the yara-x fork itself (add/rename a module export), update the pin in both dependent crates so the compiler and the on-device engine stay on the same rev:
cd hydradragonclamav; cargo update -p yara-x
cd ../dev-tools/hydradragon_yara_x_compile; cargo update -p yara-x
cd ../hydradragonandroid; cargo update -p yara-x # the crate that builds the .soAll three must match — the serialized .yrc is backend-specific.