docs · activation-triggers(3)
Activation triggers
How the in-tree triggers detect activation, how they are configured and the limitations of each mechanism.
Wrong8007 can be activated through multiple independent trigger mechanisms. Each trigger is configured separately and can be enabled or disabled at module load time.
A trigger is a struct wrong8007_trigger with a name and an
init/exit pair, registered in a static array in the core:
struct wrong8007_trigger {
const char *name;
int (*init)(void);
void (*exit)(void);
};
They call wrong8007_activate() and return. Three ship in-tree today:
Built-in triggers
| trigger | hook & mechanism | execution context | configuration | matches on |
|---|---|---|---|---|
keyboard |
register_keyboard_notifier(), matches a configured character sequence |
keyboard notifier chain (atomic) | PHRASE |
an exact phrase, typed in sequence, US keymap |
usb |
usb_register_notify(), matches configured VID/PID and event |
USB notifier callback | USB_DEVICES, WHITELIST |
VID:PID:EVENT rules, whitelist or blacklist |
network |
nf_hook_ops at NF_INET_PRE_ROUTING, priority NF_IP_PRI_FIRST |
softirq (cannot sleep) | MATCH_MAC, MATCH_IP, MATCH_PORT, MATCH_PAYLOAD, HEARTBEAT_HOST, HEARTBEAT_INTERVAL, HEARTBEAT_TIMEOUT |
MAC, IP, magic packet, or heartbeat timeout |
Detection latency
| trigger | typical detection latency |
|---|---|
keyboard |
Effectively immediate on key press; bounded by notifier dispatch |
usb |
typically a few to tens of milliseconds, depending on USB enumeration and hub event delivery |
network |
sub-millisecond for packet matches; heartbeat detection is bounded by the configured polling interval |
Detection latency is separate from activation latency. All built-in triggers perform
detection only: on a successful match they invoke wrong8007_activate() and
return immediately. Any subsequent work occurs outside the trigger callback.
keyboard
Registers a notifier_block via
register_keyboard_notifier(). On every keydown, the scancode is
mapped through a fixed US-keymap table; if the resulting character equals the next
expected character of PHRASE, the match counter advances. A full match
calls wrong8007_activate() and resets the counter. A mismatch resets
the counter to 0, or to 1 if the mismatched key happens to equal the phrase's first
character.
make load PHRASE="nuke" EXEC="/path/to/script"
Limitations:
- US keymap only: The mapping table is fixed at compile time.
- Case-sensitive, exact-sequence matching. No typo tolerance: any wrong key resets progress (except where it restarts the match, as above).
- Only printable single-character keys are matched; modifier keys like Shift or Ctrl do not themselves advance or reset the match, but do affect which character a subsequent key maps to.
- Does not see input from virtual keyboards or remote sessions, it hooks the kernel's own input notifier chain, which those bypass.
usb
Registers a notifier_block via usb_register_notify().
Rules are supplied as VID:PID:EVENT strings and parsed into a
fixed-size array at load time (16 entries maximum); EVENT is one of
insert, eject, or any.
# single device, default event (insert or eject)
make load USB_DEVICES="1234:5678" EXEC="/path/to/script"
# trigger only on removal
make load USB_DEVICES="1234:5678:eject" EXEC="/path/to/script"
# multiple devices in one load
make load USB_DEVICES="1234:5678:insert,abcd:ef00:any" EXEC="/path/to/script"
By default the listed devices are treated as a blacklist: any
device not in the list matches. Set WHITELIST=1 to invert
this, so only listed devices match:
make load USB_DEVICES="1234:5678:any" WHITELIST=1 EXEC="/path/to/script"
Find a device's VID/PID with lsusb.
Rules are validated strictly at load time e.g. malformed VID/PID hex or an
unrecognized EVENT string aborts module load with a specific error.
If no valid rules are supplied, the USB trigger disables itself (logged, not
silent) and does not register for USB notifications at all. The notifier callback
schedules work once per matching USB event without deduplication; correctness
doesn't require it, since wrong8007_activate()'s latch already
absorbs repeated calls.
Limitations:
- Fixed limit of 16 device rules per load; excess rules are truncated with a warning.
- Whitelist/blacklist is a single global mode per module load, not per-rule.
network
Registers an nf_hook_ops at NF_INET_PRE_ROUTING,
priority NF_IP_PRI_FIRST, IPv4 only.
The hook performs only bounded packet parsing and matching; it does not dynamically allocate or sleep in the softirq context.
Header accesses are guarded by pskb_may_pull() before each dereference; after a successful pull, packet-header pointers are refreshed rather than assumed to remain stable.
Supports four independent matching modes, usable alone or in combination depending on which parameters are set:
MAC address match
make load MATCH_MAC='aa:bb:cc:dd:ee:ff' EXEC="/path/to/script"
Fires on any frame carrying the given source MAC e.g. ARP, broadcast, or simple presence on the segment is enough. No IP-layer traffic is required.
IP address match
make load MATCH_IP='192.168.1.1' EXEC="/path/to/script"
Fires only on a valid IPv4 packet from the given source address. More restrictive than a MAC match: a device that hasn't sent anything at the IP layer yet won't trigger it.
Port + payload (magic packet)
make load MATCH_PORT=1234 MATCH_PAYLOAD='MAGIC' EXEC="/path/to/script"
Matches a TCP or UDP packet on the given source or destination port whose payload contains the given string, found with a bounded linear scan (k_memmem) rather than a general string-search algorithm: payloads are MTU-bounded so the added complexity of KMP or Boyer–Moore buys nothing here.
Send one with the bundled helper:
python3 scripts/whisperer.py 192.168.1.1 1234 "MAGIC"
Heartbeat timeout
make load HEARTBEAT_HOST='192.168.1.1' HEARTBEAT_INTERVAL=10 \
HEARTBEAT_TIMEOUT=30 EXEC="/path/to/script"
Fires if no packet from the given host is observed within
HEARTBEAT_TIMEOUT seconds. A timer_list checks elapsed
time every HEARTBEAT_INTERVAL seconds against a
spinlock-guarded last_seen_jiffies, updated on every matching packet.
Send periodic heartbeats with:
python3 scripts/heartbeat.py 192.168.1.1 1234
MAC- and IP-only triggers can fire immediately on load if
the target address is already active on the same network segment including your own machine.
No IP traffic is required for a MAC match; even ARP or broadcast traffic is enough. If you're testing on a live network,
prefer a magic packet (MATCH_PORT + MATCH_PAYLOAD) for
precision, or make sure the trigger source only joins the network when you
intend it to.
MAC/IP-only triggers are still useful where the target device is not always connected and you want activation specifically on its appearance e.g. air-gapped or controlled environments, proximity-based activation, or detecting a trusted device joining the network.