Skip to content

YARA X Modules

Emirhan Uçan edited this page Aug 2, 2026 · 6 revisions

YARA-X Modules & Rules Reference

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.


Static APK analysis (the former androguard module, now inside hydradragon)

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).

Metadata flow

set_module_metadata("hydradragon", json_bytes) is called by hydradragonclamav before each scan. In the module's main(ctx, _data):

  1. ctx.get_module_metadata(...) — if None/empty, an empty AndroguardJson is stored thread-local and an empty protobuf returned (no error).
  2. Otherwise parsed with serde_json::from_slice::<schema::AndroguardJson>. On failure, the empty default is stored and a ModuleError::MetadataError returned.
  3. 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-local Rc so 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.

JSON schema (AndroguardJson)

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.

Exported YARA functions

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)

rootkit_behavior() — the composite stealth signal

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:

  1. hidden = main_activity is None or empty. If not hidden, return 0 immediately.
  2. Check the suspicious-permission set ROOTKIT_SUSPICIOUS_PERMS against permissions and new_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.
  3. Return 1 if any suspicious permission is present, else 0.

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.

metadata() — matching manifest <meta-data> entries

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 hydradragon module

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.

Full HydradragonJson schema (17 top-level fields)

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")

Nested event structs

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>.

Exported YARA functions — network

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.

Exported YARA functions — HIPS behavioral

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

Exported YARA functions — static DEX analysis

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.

launcher_change — the newest export

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.


The dex module (standard yara-x, feature enabled)

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.


Rule files

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.

hydradragon.yar — 116 rules

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 : adsall of ($string_*) over banner/adpath literals.
  • BaDoinkhydradragon.app_name("BaDoink") OR obfuscated string literals OR all of ($type_b*).
  • assd_developerhydradragon.certificate.sha1("ED9A…C34A") (cert SHA-1 match).
  • Android_Godlikehydradragon.service(/godlike\.s/i) AND hydradragon.service(/godlike\.g/i) AND hydradragon.receiver(/godlike\.e/i), OR a libgodlikelib.so string.
  • 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_rootkithydradragon.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.

hips_rules_filtered_verified.yar — 60 rules

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 (>= 5 AND miner_memory(/./) >= 64), HIPS_Ransomware_With_MemoryFlag (>= 5 AND miner_memory(/./) >= 1), HIPS_Ransomware_All_Sensors (>= 5 AND miner_memory(/./) >= 64 AND entropy flag).
  • Canary traps: HIPS_Canary_Triggered (canary_triggered(/./) >= 1), HIPS_Canary_And_Flags (canary AND behavior_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(/./) >= 10 AND behavior_flagged(/./) >= 1).
  • Foreground: HIPS_Foreground_Threat (foreground_package(/./) >= 1 AND behavior_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 AND behavior_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…/) >= 1 AND behavior_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 requests BIND_DEVICE_ADMIN and suppresses its own icon.
  • Crypto-miner: HIPS_Miner_BehaviorFlag (miner_count(/./) >= 1), HIPS_Miner_HighCpuAndMemory (miner_cpu(/./) >= 50 AND miner_memory(/./) >= 40), HIPS_Miner_KnownProcess (miner_known_name(/./) >= 1), HIPS_Miner_MultipleFlags (miner_count(/./) >= 1 AND behavior_flagged(/./) >= 1).
  • File read estimation: HIPS_FileRead_Detected (FILE_READ flag present), HIPS_FileRead_HighConfidence (confidence ≥80), HIPS_FileRead_DataExfil (confidence ≥80 AND behavior_flagged(/./) >= 3), HIPS_FileRead_WithNetwork (file read AND network_connections(/./) >= 5).
  • File created: HIPS_FileCreated (any FILE_CREATED flag), HIPS_FileCreated_Burst (5+ creates in 60s), HIPS_FileCreated_WithNetwork (creates + network).
  • File copy correlation: HIPS_FileCopy (any FILE_COPY flag — read + similarly-sized create), HIPS_FileCopy_HighConfidence (confidence ≥80), HIPS_FileCopy_WithNetwork (copy + network).

machine_learning_apk.yar — 330 rules

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.

clean_rules_filtered_verified.yar — 317 rules

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-rules_filtered_verified.yar — 94 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-all.yar — 49,364 rules

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.


The .yar.yrc build chain

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.yar files.
  • <source_dir> — scanned recursively for .yar files.
  • <output_dir> — defaults to app/src/main/assets/scan relative to the source dir's parent.

Filtering logic (the reason only six .yrc ship):

  1. Drop _unverified — any filename ending _unverified.yar is removed (those reference undeclared private rules and fail to compile).
  2. Drop raw rulesets that have a filtered sibling — a raw <base>.yar is kept only if no <base>_filtered*.yar exists. So clean_rules.yar is skipped because clean_rules_filtered_verified.yar exists.
  3. --filtered further 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_sourcebuild()rules.serialize()fs::write. On any compile error the file is skipped and counted; the tool exits non-zero if any file failed.

Shipped .yrc set (app/src/main/assets/scan/)

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.

Recompiling after a rule or module change

# 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.cmd

If 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 .so

All three must match — the serialized .yrc is backend-specific.

Clone this wiki locally