Last active
October 18, 2025 21:06
-
-
Save fer/2e638e1e7194a96ca525bdb9c58ff7c4 to your computer and use it in GitHub Desktop.
USB Password Injector with ATmega32U4 (CJMCU / Pro Micro / Leonardo)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| USB Password Injector with ATmega32U4 (CJMCU / Pro Micro / Leonardo) | |
| ------------------------------------------------------------------- | |
| HOW TO CREATE AND USE THIS PROJECT WITH ARDUINO-CLI | |
| =================================================== | |
| 1. Create a new sketch folder: | |
| arduino-cli sketch new usb_password | |
| cd usb_password | |
| 2. Replace the generated usb_password.ino with this file. | |
| (You can copy-paste this content into usb_password/usb_password.ino) | |
| 3. Compile the sketch: | |
| arduino-cli compile --fqbn arduino:avr:leonardo usb_password | |
| 4. Upload the sketch to your board: | |
| arduino-cli upload -p /dev/ttyACM0 --fqbn arduino:avr:leonardo usb_password | |
| ⚠️ If you get errors, press the reset button twice quickly and retry. | |
| Sometimes the bootloader uses /dev/ttyACM1 instead of /dev/ttyACM0. | |
| Confirm the port with: | |
| arduino-cli board list | |
| 5. Optional: open serial monitor (for debug if you add Serial.print()): | |
| arduino-cli monitor -p /dev/ttyACM0 -c baudrate=9600 | |
| BEHAVIOR | |
| -------- | |
| - Single reset / power-on: | |
| → Device enters a "waiting window" (default 4 seconds). | |
| → If no second reset occurs within that time, it types PASSWORD1. | |
| → LED on pin 13 blinks during waiting and lights up when typing PASSWORD1. | |
| - Double reset (second reset within the window): | |
| → On the next boot, the device immediately types PASSWORD2. | |
| → LED on pin 8 lights up while typing PASSWORD2. | |
| - Triple reset (third reset within the window): | |
| → Wipes EEPROM (all 0s) | |
| → Both LEDs blink to confirm clear. | |
| → No password is sent. | |
| NOTES | |
| ----- | |
| - EEPROM stores the reset flag between reboots. | |
| - USB HID requires a short delay (HOST_DETECT_MS) before sending keystrokes, | |
| so the host OS can enumerate the device as a keyboard. | |
| - Adjust WINDOW_MS if you need a longer/shorter double reset detection window. | |
| */ | |
| #include <Keyboard.h> | |
| #include <EEPROM.h> | |
| // --- Passwords --- | |
| const char PASSWORD1[] = "PasswordOnePress!"; | |
| const char PASSWORD2[] = "PasswordTwoPresses!"; | |
| // --- LED pins --- | |
| const int LED_SINGLE = 13; // feedback for PASSWORD1 | |
| const int LED_DOUBLE = 8; // feedback for PASSWORD2 | |
| // --- EEPROM --- | |
| const int COUNT_ADDR = 0; // counter of resets | |
| // --- Timing --- | |
| const unsigned long WINDOW_MS = 4000; // max time window for multiple resets | |
| const unsigned long HOST_DETECT_MS = 2000; // delay before typing | |
| void clearEEPROM() { | |
| for (int i = 0; i < EEPROM.length(); i++) { | |
| EEPROM.write(i, 0); | |
| } | |
| // Blink both LEDs to confirm | |
| for (int i = 0; i < 6; i++) { | |
| digitalWrite(LED_SINGLE, HIGH); | |
| digitalWrite(LED_DOUBLE, HIGH); | |
| delay(200); | |
| digitalWrite(LED_SINGLE, LOW); | |
| digitalWrite(LED_DOUBLE, LOW); | |
| delay(200); | |
| } | |
| } | |
| void setup() { | |
| pinMode(LED_SINGLE, OUTPUT); | |
| pinMode(LED_DOUBLE, OUTPUT); | |
| digitalWrite(LED_SINGLE, LOW); | |
| digitalWrite(LED_DOUBLE, LOW); | |
| // Read counter | |
| byte cnt = EEPROM.read(COUNT_ADDR); | |
| if (cnt == 255) cnt = 0; // safeguard | |
| // Increment counter on each reset | |
| cnt++; | |
| EEPROM.write(COUNT_ADDR, cnt); | |
| // Wait for possible more resets | |
| unsigned long start = millis(); | |
| while (millis() - start < WINDOW_MS) { | |
| delay(50); | |
| // If reset happens here, MCU restarts and counter increments again | |
| } | |
| // Final count | |
| byte finalCnt = EEPROM.read(COUNT_ADDR); | |
| if (finalCnt == 1) { | |
| // Single reset → PASSWORD1 | |
| delay(HOST_DETECT_MS); | |
| Keyboard.begin(); | |
| digitalWrite(LED_SINGLE, HIGH); | |
| Keyboard.print(PASSWORD1); | |
| Keyboard.write(KEY_RETURN); | |
| Keyboard.end(); | |
| digitalWrite(LED_SINGLE, LOW); | |
| } | |
| else if (finalCnt == 2) { | |
| // Double reset → PASSWORD2 | |
| delay(HOST_DETECT_MS); | |
| Keyboard.begin(); | |
| digitalWrite(LED_DOUBLE, HIGH); | |
| Keyboard.print(PASSWORD2); | |
| Keyboard.write(KEY_RETURN); | |
| Keyboard.end(); | |
| digitalWrite(LED_DOUBLE, LOW); | |
| } | |
| else if (finalCnt >= 3) { | |
| // Triple reset or more → clear EEPROM | |
| clearEEPROM(); | |
| return; // stop here | |
| } | |
| // Reset counter for next time | |
| EEPROM.write(COUNT_ADDR, 0); | |
| } | |
| void loop() { | |
| // nothing | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment