prizrak.me blog
GitHub
← Main Page
Table of Contents
Build a crappy ring-0 toy antivirus in eBPF with the IMA LSM
Estimated reading time: 4 minutes
Aug 6, 2026
Keywords:
ebpf
lsm
dev
security
Table of Contents
Build a crappy ring-0 toy antivirus in eBPF with the IMA LSM
In significant boredom, I wandered into the eBPF documentation and stumbled across<br>this sick function:
long bpf_ima_file_hash(struct file *file, void *dst, u32 size)
Searching for references in github code search returns only a few results,<br>besides the man pages and the gazillion forks of “torvalds/linux” , but after<br>digging into what this actually does I think this deserves a little more spotlight…
WTF is eBPF?
eBPF are basically little bytecode programs that you can load into the kernel.<br>You write them in C (or Rust if you fancy) and<br>they get verified for correctness. Anyway, they can hook into the kernel in many<br>ways and basically extend the kernel much like loadable kernel modules (with limitations).
There are better resources than this unserious<br>blog for this…
WTF is IMA?
The Linux Integrity Measurement Architecture (IMA) is a Linux Security Module<br>designed to check and secure file integrity. It’s technically in the same<br>class of as SELinux / AppArmor, but basically it’s main job is to hash files<br>that get loaded into kernel space for verification against things like the TPM<br>(mitigating evil-maid attacks). You don’t need a TPM to use it, and in fact<br>it gets enabled on most modern linux distros:
# make sure you have bpf and ima in the list.<br>cat /sys/kernel/security/lsm
What can bpf_ima_file_hash do?
bpf_ima_file_hash is an eBPF function (technically a “helper” but whatever) that<br>lets you ask the kernel for a file (struct file*) hash.
It’s on-demand too. So the hash isn’t computed until you actually need it.<br>(Without an IMA policy this re-hashes on every exec. Boot with ima_policy=tcb to<br>get real caching)
Because you can call this from eBPF, you can hook into the kernel at interesting points,<br>like bprm_check_security (which runs when any binary is executed), which gives you<br>access to a struct file* through the struct linux_binprm* param.
Here’s a simple example:
SEC("lsm.s/bprm_check_security") /* must be the lsm.s (sleepable) not just lsm */<br>int BPF_PROG(my_l337_ant1v1rus, struct linux_binprm *bprm, int ret)<br>__u8 digest[IMA_MAX_DIGEST_SIZE] = {};<br>struct file *file = bprm->file;<br>int algo = 0;<br>if (ret)<br>return ret;
algo = bpf_ima_file_hash(file, digest, sizeof(digest));
/* default hash is sha1. yours may vary... see ima_hash boot param */<br>if (algo != HASH_ALGO_SHA1) {<br>return 0;<br>/* check if our hash is in the naughty list.<br>* denied_hashes is our map, defined elsewhere, with<br>* our forbidden hashes.<br>* Add a "!" do do whitelisting if you're a security psychopath...<br>*/<br>if (bpf_map_lookup_elem(&denied_hashes, digest))<br>return -EPERM;<br>return 0;
Ignoring the funky BPF syntax (the SEC part is just the function we’re attaching to,<br>the sleepable lsm hook “lsm.s” named “bprm_check_security”), we can use this to<br>block any executable that’s in our naughty (hash) list (our bpf_map_lookup_elem<br>looks it up).
The effect of this: we can block any executable in the naughty list from ever<br>running systemwide. And do it BlAzInGlY FaST (🤡) in ring 0.
IMA will do the heaving lifting and caching for you. In practice it’s pretty fast.<br>I loaded it up with 1,116,433 hashes (the entire MalwareBazaar database) without a sweat.<br>It’s smart about lazily calculating the hashes and will avoid recalculating it unless<br>the pages are dirty. It’s almost certainly faster than a userspace implementation of<br>the same thing… no userspace round-trip cost or TOCTOU (cough Windows Defender<br>cough) issues.
I’ll spare you the rest of my garbage code. But basically this is all you need to<br>make a ring-0 toy little “antivirus” thingy. You can load your map up with a<br>gazillion malware hashes from some Kaggle dataset and block all those hashes<br>pretty trivially. Not saying that it’s an effective solution, but it’s certainly<br>a building block for a proper implementation… a real antivirus should be a bit<br>more than just a file hash blocker. But I’ll take this anyday before Norton and<br>Kapersky shitware.
Hope you found this interesting.
This post was made without AI slop. If I don’t take the time to write, I don’t expect<br>you to read it