<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Krish Gulati</title>
    <description>The latest articles on DEV Community by Krish Gulati (@krish_gulati_7d8c934b8b1a).</description>
    <link>https://dev.to/krish_gulati_7d8c934b8b1a</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3555982%2F672efc8a-ee1b-487f-bbd3-0c83e2025699.png</url>
      <title>DEV Community: Krish Gulati</title>
      <link>https://dev.to/krish_gulati_7d8c934b8b1a</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/krish_gulati_7d8c934b8b1a"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Krish Gulati</dc:creator>
      <pubDate>Sat, 25 Jul 2026 23:25:29 +0000</pubDate>
      <link>https://dev.to/krish_gulati_7d8c934b8b1a/-2k72</link>
      <guid>https://dev.to/krish_gulati_7d8c934b8b1a/-2k72</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/krish_gulati_7d8c934b8b1a/building-hidguard-part-1-the-problem-and-the-false-start-3lbo" class="crayons-story__hidden-navigation-link"&gt;Building hid_guard, Part 1 — The Problem and the False Start&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/krish_gulati_7d8c934b8b1a" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3555982%2F672efc8a-ee1b-487f-bbd3-0c83e2025699.png" alt="krish_gulati_7d8c934b8b1a profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/krish_gulati_7d8c934b8b1a" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Krish Gulati
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Krish Gulati
                
              
              &lt;div id="story-author-preview-content-4119655" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/krish_gulati_7d8c934b8b1a" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3555982%2F672efc8a-ee1b-487f-bbd3-0c83e2025699.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Krish Gulati&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/krish_gulati_7d8c934b8b1a/building-hidguard-part-1-the-problem-and-the-false-start-3lbo" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jul 23&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/krish_gulati_7d8c934b8b1a/building-hidguard-part-1-the-problem-and-the-false-start-3lbo" id="article-link-4119655"&gt;
          Building hid_guard, Part 1 — The Problem and the False Start
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/security"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;security&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/linux"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;linux&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/kernel"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;kernel&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/cybersecurity"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;cybersecurity&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/krish_gulati_7d8c934b8b1a/building-hidguard-part-1-the-problem-and-the-false-start-3lbo" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;4&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/krish_gulati_7d8c934b8b1a/building-hidguard-part-1-the-problem-and-the-false-start-3lbo#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              2&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            6 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Building hid_guard, Part 1 — The Problem and the False Start</title>
      <dc:creator>Krish Gulati</dc:creator>
      <pubDate>Thu, 23 Jul 2026 11:06:07 +0000</pubDate>
      <link>https://dev.to/krish_gulati_7d8c934b8b1a/building-hidguard-part-1-the-problem-and-the-false-start-3lbo</link>
      <guid>https://dev.to/krish_gulati_7d8c934b8b1a/building-hidguard-part-1-the-problem-and-the-false-start-3lbo</guid>
      <description>&lt;p&gt;If you're just joining, the index post has the full origin story:&lt;/p&gt;

&lt;p&gt;A wireless Rubber Ducky I built out of an ESP32-S2 to prank my roommate, ~60 recruits at a cybersecurity club demo, and the question that showed up a few days late like it always does:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If a ₹500 chip can convince a computer it's a keyboard, what's actually stopping it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Short answer: nothing. Long answer: this post, and the extremely humbling three months that produced it.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;while(naive) { believe(); }&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Here's the thing your computer trusts, no questions asked: when something plugs into a USB port and announces "I'm a keyboard, and here's a keypress," the OS just believes it. No handshake, no proof, no captcha. It's the security equivalent of a bouncer who lets anyone in as long as they're wearing a shirt that says &lt;strong&gt;STAFF&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A Rubber Ducky is that shirt. It's a tiny chip that shows up, claims keyboard-hood, and immediately starts firing a pre-written script of keystrokes at whatever's foolish enough to trust it — which, per the paragraph above, is everything.&lt;/p&gt;

&lt;p&gt;But a script isn't a person, and for anyone trying to catch this, that's good news: it leaks into the numbers. Measure the exact gaps between keystrokes and a real human has noise in there — tiny, involuntary jitter in how long a key is held and how long the pause is before the next one, because fingers are just imprecise machinery. A microcontroller replaying a script has none of that. It fires keys with a mechanical evenness no human has ever produced outside of a &lt;em&gt;Black Mirror&lt;/em&gt; episode. The gap is small in absolute terms — single-digit milliseconds versus tens of milliseconds — but it's there.&lt;/p&gt;

&lt;p&gt;I didn't have to take my own word for it: &lt;a href="https://doi.org/10.1007/978-3-319-95729-6_18" rel="noopener noreferrer"&gt;&lt;em&gt;USBlock: Blocking USB-Based Keypress Injection Attacks&lt;/em&gt;&lt;/a&gt; (Neuner et al., DBSec 2018) shows you can catch injected keystrokes purely by watching timing patterns. So the core idea wasn't mine — it had actual peer review behind it. What I got wrong was &lt;strong&gt;where&lt;/strong&gt;, physically, in the system, to go do the catching.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I actually built
&lt;/h2&gt;

&lt;p&gt;You don't need to know a single thing about the Linux kernel to see why this was doomed. You just need one picture: my code was two separate programs talking to each other through a shared queue called a &lt;strong&gt;BPF ring buffer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of it as a mailbox with one strict rule: one side only ever drops letters in, the other side only ever picks them up, and neither has to stand there waiting on the other.&lt;/p&gt;

&lt;p&gt;The kernel-space half was a tiny piece of code living right up against the hardware, close enough to see every keystroke the instant it happened. Its entire job was to grab the keystroke, timestamp it, drop it in the mailbox, and refuse to elaborate further.&lt;/p&gt;

&lt;p&gt;The userspace half was a completely ordinary program — Python, Rust, C, whatever. It handled figuring out which device had appeared, reading keystrokes back out, running timing analysis, deciding whether something looked malicious, and disconnecting suspicious devices.&lt;/p&gt;

&lt;p&gt;Even device discovery turned into more machinery than any sane person should need. On startup it snapshotted every already-connected input device — a known-good guest list — and then polled every &lt;strong&gt;200 ms&lt;/strong&gt; looking for newcomers, one loop for USB and one loop for Bluetooth. Once it found a new device, it attached its tiny keystroke watcher and started reading events.&lt;/p&gt;

&lt;p&gt;Very proud of myself at the time. Should not have been.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;if (byte_count == 9) { yolo(); }&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Here's the actual logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// "9 bytes? Must be THIS layout."&lt;/span&gt;
&lt;span class="c1"&gt;// future me: it was not.&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;packet_length&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;modifier_byte&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;key_byte&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;modifier_byte&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;key_byte&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// two keys closer together than 5ms?&lt;/span&gt;
&lt;span class="c1"&gt;// get suspicious.&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gap_since_last_key_ms&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;ATTACK_THRESHOLD_MS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;suspicion&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suspicion&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;ALERT_THRESHOLD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;block_this_device&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two crimes committed here. I'll take the stand for both.&lt;/p&gt;

&lt;h3&gt;
  
  
  Crime #1: Magic numbers everywhere
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ATTACK_THRESHOLD_MS&lt;/code&gt; and the alert threshold were just fixed numbers — &lt;code&gt;5 ms&lt;/code&gt; and &lt;code&gt;30 ms&lt;/code&gt; — reverse engineered from exactly two data points: one Rubber Ducky, and my own hands. There was no per-device baseline, no statistical model, no adaptation, no acknowledgement that humans type differently. A fast typist and a scripted attack could easily land on the same side of a hardcoded threshold. I wasn't modeling a distribution. I was drawing a line in the sand and hoping the tide agreed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Crime #2: This isn't parsing. It's vibing.
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;if (packet_length == 9)&lt;/code&gt; is not HID parsing.&lt;/p&gt;

&lt;p&gt;Every HID device ships with something called a &lt;strong&gt;report descriptor&lt;/strong&gt; — think of it as a tiny instruction manual handed to the kernel that says "here's exactly how every packet I send will be formatted." The kernel is supposed to read this manual. My code did not. It simply counted bytes.&lt;/p&gt;

&lt;p&gt;It worked because my ESP32-S2 always emitted exactly the packet shape I'd hardcoded. Anything else; a different keyboard, a different layout, a different report ID arrangement, silently broke it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ivsjk0mamy2avqljvpk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ivsjk0mamy2avqljvpk.png" alt="My ESP32-S2's report descriptor" width="800" height="360"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;My ESP32-S2's actual HID report descriptor. Every device draws this differently.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;None of this crashed. None of it errored. It simply worked on exactly one device forever...which is arguably the most dangerous kind of bug: the kind that looks like success.&lt;/p&gt;

&lt;p&gt;If you want the actual instruction manual instead of my fake success, &lt;a href="https://docs.kernel.org/hid/hidintro.html" rel="noopener noreferrer"&gt;read the kernel docs on HID&lt;/a&gt; before writing HID code. I did not. You're reading the consequences.&lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;code&gt;O(too_slow)&lt;/code&gt;: The latency problem
&lt;/h2&gt;

&lt;p&gt;Suppose the parsing had been perfect. Suppose the thresholds were mathematically divine. There was still a deeper problem baked into the design: detection requires waiting to see a pattern, which means some keystrokes always land before the verdict comes in.&lt;/p&gt;

&lt;p&gt;The pipeline looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Keystroke
     ↓
Kernel program timestamps event
     ↓
Ring buffer
     ↓
Userspace wakes up
     ↓
Timing analysis
     ↓
Threshold check
     ↓
Device disconnect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's not unique to my setup, even USBlock, the paper this whole idea is built on, makes the same trade explicitly. Their own detection rule needs three suspicious keystrokes in a row before it acts, precisely because reacting on the first one alone would flag too many fast human typists. They measured their actual decision loop at under a millisecond, which is fast. But "fast" and "before the first few keystrokes land" are different guarantees, and their own paper is upfront that some earliest keystrokes get through by design.&lt;/p&gt;

&lt;p&gt;Where my prototype actually lost, badly, wasn't kernel-vs-userspace speed; it was everything upstream of the decision. Broken packet parsing, no per-device baseline, and a discovery loop polling every 200ms meant my "three suspicious keystrokes" test could take way longer than a millisecond to even trigger, on top of an already-generous three-keystroke head start for the attacker.&lt;/p&gt;

&lt;p&gt;A Rubber Ducky payload often completes in under a second. My detector needed multiple suspicious keystrokes just to get nervous, then more time on top of that to actually react while carrying all the extra latency my own bad architecture added along the way.&lt;/p&gt;

&lt;p&gt;If the attack runs in &lt;code&gt;O(instant)&lt;/code&gt;, my defense was running in roughly &lt;code&gt;O(please wait, buffering)&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I finally let it go
&lt;/h2&gt;

&lt;p&gt;The more I understood HID report descriptors, composite devices, and Linux input plumbing, the less my architecture made sense. The real solution wasn't "make userspace smarter" — it was "run detection where the events first appear." I just didn't know what that place was called.&lt;/p&gt;

&lt;p&gt;A few months later I found &lt;strong&gt;hid-omg-detect&lt;/strong&gt;, a kernel module attempting similar timing analysis, and then a review of it by Benjamin Tissoires (the person who maintains large parts of Linux HID subsystem). The teardown was brutal: wrong assumptions about report layout, races with other HID drivers, &lt;code&gt;HID_ANY_ID&lt;/code&gt; matching problems, ownership issues. And then the actual verdict — this kind of logic belongs in &lt;strong&gt;HID-BPF&lt;/strong&gt;, not as a standalone module, not fighting every other driver, but running before any driver even sees the raw events.&lt;/p&gt;

&lt;p&gt;He wasn't talking to me. He didn't know I existed. Yet he'd independently arrived at exactly the same conclusion I had spent three months stumbling toward.&lt;/p&gt;

&lt;p&gt;Three months of work, and the honest epitaph is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The prototype proved the signal was real, and then spent every remaining day of its life teaching me everything wrong with it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A perfectly respectable way for a bad prototype to die.&lt;/p&gt;




&lt;h2&gt;
  
  
  Up next
&lt;/h2&gt;

&lt;p&gt;Part 3 is the rebuild: running detection inside the kernel's own HID path, reading each device's actual report descriptor, reimplementing human-vs-script timing analysis from scratch, and learning how to do statistics in an environment that has never once heard of a decimal point.&lt;/p&gt;

&lt;p&gt;The archived prototype, magic numbers and all, is &lt;a href="https://github.com/fusion1110/eBPF-hid_guard" rel="noopener noreferrer"&gt;available on GitHub&lt;/a&gt; if you want to see exactly how bad it was.&lt;/p&gt;




&lt;p&gt;Anyone else built something clever that turned out to be solving the problem in exactly the wrong place? Curious how you found out.&lt;/p&gt;

</description>
      <category>security</category>
      <category>linux</category>
      <category>kernel</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Krish Gulati</dc:creator>
      <pubDate>Tue, 07 Jul 2026 10:51:28 +0000</pubDate>
      <link>https://dev.to/krish_gulati_7d8c934b8b1a/-36a2</link>
      <guid>https://dev.to/krish_gulati_7d8c934b8b1a/-36a2</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/krish_gulati_7d8c934b8b1a/the-duck-stops-here-building-hidguard-catching-keystroke-injection-at-the-kernel-level-4p0o" class="crayons-story__hidden-navigation-link"&gt;The Duck Stops Here: Building hid_guard — Catching Keystroke Injection at the Kernel Level&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/krish_gulati_7d8c934b8b1a" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3555982%2F672efc8a-ee1b-487f-bbd3-0c83e2025699.png" alt="krish_gulati_7d8c934b8b1a profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/krish_gulati_7d8c934b8b1a" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Krish Gulati
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Krish Gulati
                
              
              &lt;div id="story-author-preview-content-4072357" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/krish_gulati_7d8c934b8b1a" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3555982%2F672efc8a-ee1b-487f-bbd3-0c83e2025699.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Krish Gulati&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/krish_gulati_7d8c934b8b1a/the-duck-stops-here-building-hidguard-catching-keystroke-injection-at-the-kernel-level-4p0o" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jul 7&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/krish_gulati_7d8c934b8b1a/the-duck-stops-here-building-hidguard-catching-keystroke-injection-at-the-kernel-level-4p0o" id="article-link-4072357"&gt;
          The Duck Stops Here: Building hid_guard — Catching Keystroke Injection at the Kernel Level
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/security"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;security&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/c"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;c&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/linux"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;linux&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/krish_gulati_7d8c934b8b1a/the-duck-stops-here-building-hidguard-catching-keystroke-injection-at-the-kernel-level-4p0o" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;7&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/krish_gulati_7d8c934b8b1a/the-duck-stops-here-building-hidguard-catching-keystroke-injection-at-the-kernel-level-4p0o#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              4&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>The Duck Stops Here: Building hid_guard — Catching Keystroke Injection at the Kernel Level</title>
      <dc:creator>Krish Gulati</dc:creator>
      <pubDate>Tue, 07 Jul 2026 10:50:55 +0000</pubDate>
      <link>https://dev.to/krish_gulati_7d8c934b8b1a/the-duck-stops-here-building-hidguard-catching-keystroke-injection-at-the-kernel-level-4p0o</link>
      <guid>https://dev.to/krish_gulati_7d8c934b8b1a/the-duck-stops-here-building-hidguard-catching-keystroke-injection-at-the-kernel-level-4p0o</guid>
      <description>&lt;p&gt;This is the index post for a series on building hid_guard, a HID-BPF security monitor for the Linux kernel — from the first embarrassingly bad prototype to an RFC on the linux-input mailing list. I'll update the links below as each part goes live, assuming I don't get distracted rewriting Part 2 for the fourth time.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this actually started
&lt;/h2&gt;

&lt;p&gt;Every "I built a security tool" post claims to start with a researcher noticing a subtle gap in the threat model. Mine starts with me pranking my roommate.&lt;/p&gt;

&lt;p&gt;Our cybersecurity club runs an intro session every fall to reel in new members, and in August–September 2025 it was my turn to make the pitch. The club is only three years old and still fighting the older, flashier clubs for attention, so standing on stage clicking through bullet points was not going to cut it. I had an ESP32-S2 sitting in a drawer doing nothing useful with its life, and I knew it could be flashed to act as a USB HID device; so I built a wireless Rubber Ducky: a 500–600Rs ($5–6) chip that shows up to any computer as "just a keyboard" and fires off whatever script you gave it the second it's connected.&lt;/p&gt;

&lt;p&gt;It landed better than I hoped, roughly 60 sign-ups, the highest in the club's short history. I'd like to think the little wireless duck gets some of the credit.&lt;/p&gt;

&lt;p&gt;Then, because a good prank is a wasted prank if you only use it once, I ran it on my roommate too. It worked on him exactly as well as it worked on a room full of strangers.&lt;/p&gt;

&lt;p&gt;That's when the actual question showed up, a few days later than it probably should have: if this is this easy, what's supposed to stop it?&lt;/p&gt;

&lt;p&gt;I went looking for an answer and found a handful of userspace tools trying:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Device-attribute whitelisting&lt;/strong&gt; — trivially beaten by spoofing a trusted VID/PID&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watching for new keyboard insertions&lt;/strong&gt; and locking the workstation in response&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flagging a mismatch&lt;/strong&gt; between the USB class a device advertises and what it's actually registered for&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All clever, all userspace, and none of them fast enough to matter: by the time a daemon reacts to a device event, a Rubber Ducky payload has usually already finished its PowerShell one-liner and is halfway through exfiltrating your browser cookies.&lt;/p&gt;

&lt;p&gt;So I stopped asking "how do I stop this" and started asking "how does the kernel even get fooled in the first place." How does a HID device advertise itself, how is a keystroke report actually structured on the wire, what is the kernel trusting the moment it decides something is a "keyboard." That thread led me, inevitably, to eBPF.&lt;/p&gt;

&lt;p&gt;I was fairly new to C at the time, and the eBPF tutorials humbled me a bit more than I'd like to admit: verifier errors that read like riddles written by someone who resents you personally, and a mental model I did not yet have for programs that aren't allowed to loop unbounded or touch a pointer without first proving to a paranoid static analyzer that it's safe. (C is my favorite language now, so something clearly stuck, possibly out of trauma bonding.) Eventually I had a genuinely ugly v0.01 and showed it off at my college's project showcase. We didn't win. I don't think the judges had heard of HID-BPF either, which stung less once I realized it meant the gap I'd stumbled into was real and not just badly explained by me.&lt;/p&gt;

&lt;p&gt;After the showcase I sat down and rebuilt it properly — new architecture, a standalone eBPF prototype meant to do the entire job by itself. That phase is archived here: [eBPF-hid_guard]&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/fusion1110" rel="noopener noreferrer"&gt;
        fusion1110
      &lt;/a&gt; / &lt;a href="https://github.com/fusion1110/eBPF-hid_guard" rel="noopener noreferrer"&gt;
        eBPF-hid_guard
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;eBPF-hid_guard&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;See &lt;a href="https://github.com/fusion1110/eBPF-hid_guard#why-this-was-archived" rel="noopener noreferrer"&gt;Why This Was Archived&lt;/a&gt; below.&lt;/p&gt;

&lt;p&gt;A Linux kernel-space security monitor for detecting and blocking HID injection attacks (Rubber Ducky, O.MG Cable, ESP32-based keyboard emulators) using eBPF HID-BPF struct_ops.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What This Does&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Malicious HID devices emulate keyboards and inject keystrokes far faster than any human typist. This tool detects that timing anomaly and unbinds the offending device from the kernel HID driver before the attack completes.&lt;/p&gt;

&lt;p&gt;On startup, the tool snapshots all currently connected HID devices and ignores them — only newly connected devices are monitored. It supports both USB and Bluetooth HID devices.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Branch Structure&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;
&lt;code&gt;main&lt;/code&gt; — Working Proof of Concept&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;Functional end-to-end pipeline, tested against an ESP32-based keyboard emulator:&lt;/p&gt;


&lt;ul&gt;

&lt;li&gt;Snapshots pre-existing devices at startup; ignores them&lt;/li&gt;

&lt;li&gt;Parallel USB and BLE discovery threads — first to detect a new device wins&lt;/li&gt;

&lt;li&gt;Attaches a HID-BPF &lt;code&gt;struct_ops&lt;/code&gt; program to the discovered device&lt;/li&gt;

&lt;li&gt;BPF side captures raw HID report bytes, timestamp…&lt;/li&gt;

&lt;/ul&gt;&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/fusion1110/eBPF-hid_guard" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;
 I got most of the userspace side working, and then, sometime in April, I ran into hid-omg-detect: a kernel module doing almost exactly what I'd been reaching for, scoring HID devices on keystroke timing entropy and plug-and-type latency. Buried in the maintainer discussion was a suggestion from Benjamin Tissoires (the HID subsystem maintainer and the person behind udev-hid-bpf) to build this kind of detection on top of HID-BPF rather than as a standalone kernel module. Watching someone arrive at that conclusion independently, from the inside, while I'd been circling the same idea from the outside with zero credentials and a lot of stubbornness, was about as close to validation as I was going to get.

&lt;p&gt;That sent me down the udev-hid-bpf rabbit hole for real. I'll get into the framework itself (struct_ops, device matching, the attach pipeline) in Part 2. Recently I submitted an RFC patch to the linux-input mailing list and got back an automated report cheerfully pointing out everything wrong with it. Which is, in its own way, a milestone: the project is now real enough to have a bot mad at it.&lt;/p&gt;

&lt;p&gt;A prank, a rabbit hole, a bad prototype, a good rebuild, a maintainer confirming from the outside that I hadn't been chasing nothing. This series is the long version.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Plug a BadUSB or Rubber Ducky device into a machine and, to the operating system, it's just a keyboard. HID has no concept of "this input device is actually a microcontroller replaying a script."&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It types exactly like a person — except it doesn't, not really.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A script fires keystrokes on a schedule; a human's timing has noise in it that's genuinely hard to fake convincingly down at the millisecond level. That gap is small. But it's real, and it's measurable, if you're watching at the right layer instead of trusting the device's word for it.&lt;/p&gt;

&lt;p&gt;hid_guard watches at that layer. It's a HID-BPF program, built on the udev-hid-bpf framework, that models keystroke timing per device and flags input that looks automated rather than typed. This series is the story of building it — including the parts that didn't work the first time, because those are usually the parts worth reading about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a series instead of one post
&lt;/h2&gt;

&lt;p&gt;A single "I built a kernel security tool" post would flatten months of dead ends into a highlight reel — a nice narrative, a nearly useless technical reference. The actual engineering lives in the parts that get thrown away and rebuilt: the framework I abandoned, the bug that only showed up on one specific keyboard, the review that caught five things I'd completely missed before submission. Cram all of that into one post and it's either exhausting to read or quietly dishonest by omission. Splitting it up lets each part get the space it actually needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the series
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Part 1 — The problem and the false start.&lt;/strong&gt;&lt;br&gt;
Why keystroke-timing detection is viable at all, and the standalone eBPF prototype I built first and then abandoned once its limits became embarrassingly clear. &lt;em&gt;(coming soon)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 2 — Pivoting to udev-hid-bpf and designing the detection model.&lt;/strong&gt;&lt;br&gt;
The framework switch — struct_ops, hwdb device matching, the probe/attach pipeline, how HID descriptors encode a keyboard's layout and the detection model built on top of it: Welford's online variance algorithm reimplemented in fixed-point arithmetic because BPF has never heard of a float, and why Post-Enumeration Delay turned out to matter almost as much as keystroke timing itself. &lt;em&gt;(coming soon)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 3 — Where it breaks: 6KRO vs. NKRO.&lt;/strong&gt;&lt;br&gt;
The off-by-one bug that only surfaced on N-key-rollover hardware, and what it taught me about trusting a length value I hadn't actually bothered to define properly. &lt;em&gt;(coming soon)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 4 — From review to RFC.&lt;/strong&gt;&lt;br&gt;
A pre-submission review that caught issues I'd missed, a spinlock clobber risk on device re-enumeration, and what actually went out to the linux-input mailing list — plus what came back. &lt;em&gt;(coming soon)&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>c</category>
      <category>linux</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
