diff --git a/Upstream/Inc/build_config.h b/Upstream/Inc/build_config.h index 8ea8457..3757c2b 100644 --- a/Upstream/Inc/build_config.h +++ b/Upstream/Inc/build_config.h @@ -17,7 +17,32 @@ #define CONFIG_MASS_STORAGE_WRITES_PERMITTED #define CONFIG_KEYBOARD_ENABLED +#define CONFIG_KEYBOARD_BOT_DETECT_ENABLED + #define CONFIG_MOUSE_ENABLED +//#define CONFIG_MOUSE_BOT_DETECT_ENABLED + + +//Configure keyboard rate-limiting (bot detection) here: +#ifdef CONFIG_KEYBOARD_BOT_DETECT_ENABLED + #define KEYBOARD_BOTDETECT_ALPHANUM_TIME_MS 170 //Alphanumeric keys (and space), used for high-speed typing. Limit 6 per second, 360 keys per minute + #define KEYBOARD_BOTDETECT_NON_ALPHANUM_DIFFERENT_TIME_MS 250 //Non-alphanumeric keys. Limit 4 per second. + #define KEYBOARD_BOTDETECT_NON_ALPHANUM_REPEATED_TIME_MS 170 //Non-alphanumeric key, pressed repeatedly. Limit 6 keys per second. + #define KEYBOARD_BOTDETECT_MINIMUM_KEYDOWN_TIME_MS 100 //Key pressed for less than 100ms indicates bot. + + #define KEYBOARD_BOTDETECT_TEMPORARY_LOCKOUT_KEY_COUNT 2 //'Burst' = maximum number of keys with active timer before triggering temporary lockout + #define KEYBOARD_BOTDETECT_PERMANENT_LOCKOUT_KEY_COUNT 4 //'Burst' = maximum number of keys with active timer before triggering permanent lockout + #define KEYBOARD_BOTDETECT_PERMANENT_LOCKOUT_SHORT_COUNT 3 //One short keypress causes temporary lockout. 3 short keypresses within the temporary lockout time triggers permanent lockout + + #define KEYBOARD_BOTDETECT_TEMPORARY_LOCKOUT_TIME_MS 5000 //Lockout for 5 seconds when temporary lockout threshold reached + #define KEYBOARD_BOTDETECT_TEMPORARY_LOCKOUT_LED_TIME_MS 60000 //Flash fault LED for 60 seconds after temporary lockout +#endif + + +//Configure mouse rate-limiting (bot detection) here: +#ifdef CONFIG_MOUSE_BOT_DETECT_ENABLED + +#endif #endif /* INC_BUILD_CONFIG_H_ */ diff --git a/Upstream/Inc/upstream_hid_botdetect.h b/Upstream/Inc/upstream_hid_botdetect.h new file mode 100644 index 0000000..fa6217d --- /dev/null +++ b/Upstream/Inc/upstream_hid_botdetect.h @@ -0,0 +1,49 @@ +/* + * upstream_hid_botdetect.h + * + * Created on: Aug 17, 2017 + * Author: Robert Fisk + */ + +#ifndef INC_UPSTREAM_HID_BOTDETECT_H_ +#define INC_UPSTREAM_HID_BOTDETECT_H_ + + +#include "stm32f4xx_hal.h" + + +#define KEY_ROLLOVER 0x01 //Rollover means we hold the last reported key status +#define KEY_A 0x04 +#define KEY_Z 0x1D +#define KEY_1 0x1E +#define KEY_0 0x27 +#define KEY_SPACE 0x2C +#define KEY_PAD_1 0x59 +#define KEY_PAD_0 0x62 +#define KEY_MODIFIER_BASE 0xE0 //First modifier key is L-Ctl + + +typedef enum +{ + LOCKOUT_STATE_INACTIVE = 0, + LOCKOUT_STATE_ACTIVE, + LOCKOUT_STATE_FLASHING +} +LockoutStateTypeDef; + + +typedef struct +{ + uint8_t KeyCode; + uint32_t KeyDownTime; + int32_t KeyActiveTimer; +} +KeyTimerLogTypeDef; + + +void Upstream_HID_BotDetect_Systick(void); +void Upstream_HID_BotDetectKeyboard(uint8_t* keyboardInData); + + + +#endif /* INC_UPSTREAM_HID_BOTDETECT_H_ */ diff --git a/Upstream/Src/interrupts.c b/Upstream/Src/interrupts.c index 34e3f13..9e8f266 100755 --- a/Upstream/Src/interrupts.c +++ b/Upstream/Src/interrupts.c @@ -35,12 +35,13 @@ */ /* Includes ------------------------------------------------------------------*/ -#include -#include +#include "interrupts.h" +#include "upstream_spi.h" #include "stm32f4xx_hal.h" -#include "stm32f4xx.h" #include "board_config.h" +#include "build_config.h" #include "led.h" +#include "upstream_hid_botdetect.h" /* USER CODE BEGIN 0 */ @@ -62,6 +63,11 @@ void SysTick_Handler(void) { HAL_IncTick(); LED_DoBlinks(); + +#if (defined (CONFIG_KEYBOARD_ENABLED) && defined (CONFIG_KEYBOARD_BOT_DETECT_ENABLED)) || \ + (defined (CONFIG_MOUSE_ENABLED) && defined (CONFIG_MOUSE_BOT_DETECT_ENABLED)) + Upstream_HID_BotDetect_Systick(); +#endif } ///////////////////////// diff --git a/Upstream/Src/upstream_hid.c b/Upstream/Src/upstream_hid.c index 4343d13..601aa30 100644 --- a/Upstream/Src/upstream_hid.c +++ b/Upstream/Src/upstream_hid.c @@ -12,6 +12,7 @@ #include "upstream_hid.h" +#include "upstream_hid_botdetect.h" #include "upstream_interface_def.h" #include "build_config.h" @@ -199,7 +200,9 @@ static void Upstream_HID_ReceiveInterruptReportCallback(UpstreamPacketTypeDef* r Upstream_StateMachine_Wakeup(); //Send wakeup signal to host } - //Other keyboard sanity checks here... +#ifdef CONFIG_KEYBOARD_BOT_DETECT_ENABLED + Upstream_HID_BotDetectKeyboard(receivedPacket->Data); +#endif } //Other HID classes go here... diff --git a/Upstream/Src/upstream_hid_botdetect.c b/Upstream/Src/upstream_hid_botdetect.c new file mode 100644 index 0000000..26da5c2 --- /dev/null +++ b/Upstream/Src/upstream_hid_botdetect.c @@ -0,0 +1,313 @@ +/* + * upstream_hid_botdetect.c + * + * Created on: Aug 17, 2017 + * Author: Robert Fisk + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + + +#include "upstream_hid_botdetect.h" +#include "upstream_hid.h" +#include "build_config.h" + + + +//Variables common between keyboard and mouse bot detection: +uint32_t TemporaryLockoutStartTime; +LockoutStateTypeDef PermanentLockoutState = LOCKOUT_STATE_INACTIVE; +volatile LockoutStateTypeDef TemporaryLockoutState = LOCKOUT_STATE_INACTIVE; + + + +//Variables specific to keyboard bot detection: +#if defined (CONFIG_KEYBOARD_ENABLED) && defined (CONFIG_KEYBOARD_BOT_DETECT_ENABLED) + KeyTimerLogTypeDef KeyTimerLog[KEYBOARD_BOTDETECT_PERMANENT_LOCKOUT_KEY_COUNT]; + volatile uint8_t KeyTimerCount = 0; + uint8_t LastKeyCode = 0; + uint8_t ShortKeypressCount = 0; + uint8_t OldKeyboardInData[HID_KEYBOARD_INPUT_DATA_LEN] = {0}; +#endif + + + +//Variables specific to mouse bot detection: +#if defined (CONFIG_MOUSE_ENABLED) && defined (CONFIG_MOUSE_BOT_DETECT_ENABLED) + +#endif + + + +//Code specific to keyboard bot detection: +#if defined (CONFIG_KEYBOARD_ENABLED) && defined (CONFIG_KEYBOARD_BOT_DETECT_ENABLED) + +static uint32_t Upstream_HID_BotDetectKeyboard_RolloverCheck(uint8_t* keyboardInData); +static uint32_t Upstream_HID_BotDetectKeyboard_KeyIsAlphanum(uint8_t keyCode); +static void Upstream_HID_BotDetectKeyboard_KeyDown(uint8_t keyCode); +static void Upstream_HID_BotDetectKeyboard_KeyUp(uint8_t keyCode); + + +//Checks if received keyboard data is from a real human. +//This is not entirely bulletproof as an attacking device may slow its keypresses below the threshold. +void Upstream_HID_BotDetectKeyboard(uint8_t* keyboardInData) +{ + uint32_t i; + uint32_t j; + uint8_t tempByte; + + if (Upstream_HID_BotDetectKeyboard_RolloverCheck(keyboardInData)) return; + + //Process modifier keys in first byte + tempByte = keyboardInData[0]; + for (i = 0; i < 8; i++) + { + if ((tempByte & 1) && !(OldKeyboardInData[0] & 1)) + { + Upstream_HID_BotDetectKeyboard_KeyDown(KEY_MODIFIER_BASE + i); + } + + if (!(tempByte & 1) && (OldKeyboardInData[0] & 1)) + { + Upstream_HID_BotDetectKeyboard_KeyDown(KEY_MODIFIER_BASE + i); + } + + tempByte >>= 1; + OldKeyboardInData[0] >>= 1; + } + + + //Process key array: search for keydowns + for (i = 2; i < HID_KEYBOARD_INPUT_DATA_LEN; i++) + { + if (keyboardInData[i] >= KEY_A) + { + for (j = 2; j < HID_KEYBOARD_INPUT_DATA_LEN; j++) + { + if (keyboardInData[i] == OldKeyboardInData[j]) break; + } + if (j >= HID_KEYBOARD_INPUT_DATA_LEN) + { + Upstream_HID_BotDetectKeyboard_KeyDown(keyboardInData[i]); + } + } + } + + //Process key array: search for keyups + for (i = 2; i < HID_KEYBOARD_INPUT_DATA_LEN; i++) + { + if (OldKeyboardInData[i] >= KEY_A) + { + for (j = 2; j < HID_KEYBOARD_INPUT_DATA_LEN; j++) + { + if (keyboardInData[i] == OldKeyboardInData[j]) break; + } + if (j >= HID_KEYBOARD_INPUT_DATA_LEN) + { + Upstream_HID_BotDetectKeyboard_KeyUp(OldKeyboardInData[i]); + } + } + } + + //Copy new data to old array + for (i = 0; i < HID_KEYBOARD_INPUT_DATA_LEN; i++) + { + OldKeyboardInData[i] = keyboardInData[i]; + } + + //Final checks + if (KeyTimerCount >= KEYBOARD_BOTDETECT_TEMPORARY_LOCKOUT_KEY_COUNT) + { + TemporaryLockoutStartTime = HAL_GetTick(); + TemporaryLockoutState = LOCKOUT_STATE_ACTIVE; + ////////////Set LED state + } + + //Host receives no data if we are locked + if ((TemporaryLockoutState == LOCKOUT_STATE_ACTIVE) || (PermanentLockoutState == LOCKOUT_STATE_ACTIVE)) + { + for (i = 0; i < HID_KEYBOARD_INPUT_DATA_LEN; i++) + { + keyboardInData[i] = 0; + } + } +} + + + +//Keyboard reports a rollover code when there are too many keys to scan/report. +static uint32_t Upstream_HID_BotDetectKeyboard_RolloverCheck(uint8_t* keyboardInData) +{ + uint32_t i; + + for (i = 2; i < HID_KEYBOARD_INPUT_DATA_LEN; i++) + { + if (keyboardInData[i] == KEY_ROLLOVER) break; + } + if (i >= HID_KEYBOARD_INPUT_DATA_LEN) return 0; + + //As I am unclear on the exact usage and interpretation of the rollover code, + //we are going to play it safe by copying the old keyboard data over the new array. + //This ensures the host interprets a rollover event exactly the way we do! + + //Host receives no data if we are locked + if ((TemporaryLockoutState == LOCKOUT_STATE_ACTIVE) || (PermanentLockoutState == LOCKOUT_STATE_ACTIVE)) + { + for (i = 0; i < HID_KEYBOARD_INPUT_DATA_LEN; i++) + { + keyboardInData[i] = 0; + } + } + else + { + for (i = 0; i < HID_KEYBOARD_INPUT_DATA_LEN; i++) + { + keyboardInData[i] = OldKeyboardInData[i]; + } + } + return 1; +} + + + +static void Upstream_HID_BotDetectKeyboard_KeyDown(uint8_t keyCode) +{ + uint32_t tempKeyActiveTime; + + if (KeyTimerCount >= KEYBOARD_BOTDETECT_PERMANENT_LOCKOUT_KEY_COUNT) + { + PermanentLockoutState = LOCKOUT_STATE_ACTIVE; + ////////////Set LED state + return; + } + + if (Upstream_HID_BotDetectKeyboard_KeyIsAlphanum(keyCode)) + { + tempKeyActiveTime = KEYBOARD_BOTDETECT_ALPHANUM_TIME_MS; + } + else + { + if (keyCode == LastKeyCode) + { + tempKeyActiveTime = KEYBOARD_BOTDETECT_NON_ALPHANUM_REPEATED_TIME_MS; + } + else + { + tempKeyActiveTime = KEYBOARD_BOTDETECT_NON_ALPHANUM_DIFFERENT_TIME_MS; + } + } + LastKeyCode = keyCode; + + //Disable systick interrupt while adding key to array + __disable_irq(); + KeyTimerLog[KeyTimerCount].KeyCode = keyCode; + KeyTimerLog[KeyTimerCount].KeyDownTime = HAL_GetTick(); + KeyTimerLog[KeyTimerCount++].KeyActiveTimer = tempKeyActiveTime; + __enable_irq(); +} + + + +static uint32_t Upstream_HID_BotDetectKeyboard_KeyIsAlphanum(uint8_t keyCode) +{ + if ((keyCode >= KEY_A) && (keyCode <= KEY_Z)) return 1; + if ((keyCode >= KEY_1) && (keyCode <= KEY_0)) return 1; + if ((keyCode >= KEY_PAD_1) && (keyCode <= KEY_PAD_0)) return 1; + if (keyCode == KEY_SPACE) return 1; + + //else: + return 0; +} + + + +static void Upstream_HID_BotDetectKeyboard_KeyUp(uint8_t keyCode) +{ + uint32_t i; + + //Search through active key array to see if this key was pressed recently + __disable_irq(); + for (i = 0; i < KeyTimerCount; i++) + { + if (KeyTimerLog[i].KeyCode == keyCode) break; + } + if (i >= KeyTimerCount) + { + __enable_irq(); + return; + } + + //Was this is a short keypress? + if ((int32_t)(HAL_GetTick() - KeyTimerLog[i].KeyDownTime) < KEYBOARD_BOTDETECT_MINIMUM_KEYDOWN_TIME_MS) + { + ShortKeypressCount++; + ////////////Set LED state + if (ShortKeypressCount >= KEYBOARD_BOTDETECT_PERMANENT_LOCKOUT_SHORT_COUNT) + { + PermanentLockoutState = LOCKOUT_STATE_ACTIVE; + } + else + { + TemporaryLockoutStartTime = HAL_GetTick(); + TemporaryLockoutState = LOCKOUT_STATE_ACTIVE; + } + } + __enable_irq(); +} + +#endif //if defined (CONFIG_KEYBOARD_ENABLED) && defined (CONFIG_KEYBOARD_BOT_DETECT_ENABLED) + + + + + +//Called by Systick_Handler every 1ms, at high interrupt priority. +void Upstream_HID_BotDetect_Systick(void) +{ + uint32_t i; + +//Keyboard-specific stuff: +#if defined (CONFIG_KEYBOARD_ENABLED) && defined (CONFIG_KEYBOARD_BOT_DETECT_ENABLED) + if (KeyTimerCount > 0) //Check if current active key has timed out + { + KeyTimerLog[0].KeyActiveTimer--; + if (KeyTimerLog[0].KeyActiveTimer <= 0) + { + KeyTimerCount--; + for (i = 0; i < KeyTimerCount; i++) //Shuffle other keys down + { + KeyTimerLog[i].KeyCode = KeyTimerLog[i + 1].KeyCode; + KeyTimerLog[i].KeyDownTime = KeyTimerLog[i + 1].KeyDownTime; + KeyTimerLog[i].KeyActiveTimer = KeyTimerLog[i + 1].KeyActiveTimer; + } + } + } + + //Check if temporary lockout has expired + if (TemporaryLockoutState == LOCKOUT_STATE_ACTIVE) + { + if ((int32_t)(HAL_GetTick() - TemporaryLockoutStartTime) > KEYBOARD_BOTDETECT_TEMPORARY_LOCKOUT_TIME_MS) + { + TemporaryLockoutState = LOCKOUT_STATE_FLASHING; + } + } + else if (TemporaryLockoutState == LOCKOUT_STATE_FLASHING) + { + if ((int32_t)(HAL_GetTick() - TemporaryLockoutStartTime) > KEYBOARD_BOTDETECT_TEMPORARY_LOCKOUT_LED_TIME_MS) + { + ////////////Set LEDs off + TemporaryLockoutState = LOCKOUT_STATE_INACTIVE; + } + } +#endif + + +#if defined (CONFIG_MOUSE_ENABLED) && defined (CONFIG_MOUSE_BOT_DETECT_ENABLED) + +#endif +} + + +