20 Commits

Author SHA1 Message Date
cecio
b8a0baa9f2 added 0.9.1 firmware 2023-05-13 21:51:03 +02:00
cecio
32a4a2d895 New scroll mode and bump to release 0.9.1 2023-05-13 13:19:41 +02:00
cecio
1faf7e440c bump to release 0.9.0 2023-05-10 22:48:14 +02:00
cecio
5bca0f9370 added Mouse report 2023-05-06 23:28:10 +02:00
cecio
f802176d70 beta of version 0.9.0 2023-05-04 22:41:07 +02:00
cecio
61acbde028 minor update to PCB (ground connections) 2023-05-01 11:50:05 +02:00
cecio
881536db0c Update README.md 2023-04-25 22:41:59 +02:00
cecio
bbb9b4e58e Update README.md 2023-04-25 21:52:52 +02:00
cecio
42c4ba357a Update README.md 2023-04-25 21:30:25 +02:00
cecio
e71e9b60a9 update 2023-04-22 22:48:20 +02:00
cecio
41360fc26b Update README.md 2023-04-22 22:38:48 +02:00
cecio
50178b2272 renamed STL files 2023-04-22 22:33:49 +02:00
cecio
41ba557583 Merge branch 'main' of https://github.com/cecio/USBvalve 2023-04-22 22:32:53 +02:00
cecio
b91effbbba new release 0.8.x 2023-04-22 22:32:08 +02:00
WM
ac51d0884a Updated STL files (#3)
* Create .gitkeep

* STL files

STL files for a box and a cover for the 128x32 screen

* Delete .gitkeep

* Updated STL files

Updated STL files for the lid with more grip
2023-04-13 22:35:04 +02:00
cecio
c02516a815 Merge branch 'main' of https://github.com/cecio/USBvalve 2023-04-13 13:17:04 +02:00
WM
181bf1ba0d Add STL files (#2)
* Create .gitkeep

* STL files

STL files for a box and a cover for the 128x32 screen

* Delete .gitkeep
2023-04-13 13:15:05 +02:00
cecio
121742fb6f bump to release 0.8.0 2023-04-04 00:13:04 +02:00
cecio
3d890ff1ac Gerber file renamed 2023-04-03 22:54:24 +02:00
cecio
4318641722 first version with HID detector working 2023-03-30 23:28:12 +02:00
13 changed files with 330 additions and 32 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -14,6 +14,20 @@ With **USBvalve** you can have an immediate feedback about what happen to the dr
<img src="https://github.com/cecio/USBvalve/blob/main/pictures/readme.png" alt="Readme" width="15%" height="15%" />
</p>
and from version `0.8.0` you can also use it as USB Host to detect *BADUSB* devices:
<p float="left">
<img src="https://github.com/cecio/USBvalve/blob/main/pictures/hid.png" alt="HID" width="15%" height="15%" />
</p>
This is an example of the *BADUSB* debugger available on serial port:
<p float="left">
<a href="https://asciinema.org/a/NWfC9Mvzzpj3eZfsC7s5Dz1sJ" target="_blank"><img src="https://asciinema.org/a/NWfC9Mvzzpj3eZfsC7s5Dz1sJ.svg" width="65%" height="65%" /></a>
</p>
If you prefer videos, you can also have a look to my [Insomni'hack Presentation](https://www.youtube.com/watch?v=jy1filtQY4w)
## Repository Structure
`docs`: documentation about the project, with a presentation where you can have a look to all the features
@@ -28,6 +42,8 @@ With **USBvalve** you can have an immediate feedback about what happen to the dr
`pictures`: images and resources used in this doc
`STL`: STL files for enclosure
## Build USBvalve
### Part list
@@ -41,21 +57,27 @@ If you want to build your own, you need:
### Building instructions
Almost all the job is done directly on the board by the software, so you just need to arrange the connection with the OLED for output
Almost all the job is done directly on the board by the software, so you just need to arrange the connection with the OLED for output.
Starting from version 0.8.0 of the firmware, **USBvalve** can detect HID devices (used to detect *BADUSB*). This require an additional USB port behaving as Host. If you are not interested in this, you can use the old instructions [in docs folder](https://github.com/cecio/USBvalve/blob/main/docs/BUILDING-1.1.md) and use PCB version `1.1`. Otherwise go ahead with PCB version `1.2` (we have version for USB-A or USB-B, see folder).
#### With USBvalve PCB
<p float="left">
<img src="https://github.com/cecio/USBvalve/blob/main/pictures/USB_valve_front.png" width="25%" height="25%" />
<img src="https://github.com/cecio/USBvalve/blob/main/pictures/USB_valve_back.png" width="25%" height="25%" />
<img src="https://github.com/cecio/USBvalve/blob/main/pictures/USB_valve_1-2_front.png" width="25%" height="25%" />
<img src="https://github.com/cecio/USBvalve/blob/main/pictures/USB_valve_1-2_back.png" width="24%" height="24%" />
</p>
- solder a USB female port in `USBH` area. This is for version `A`, but there is a version for USB `Micro-B` as well if you prefer
- place the Raspberry Pi Pico on the silk screen on the front
- you don't need to solder all the PINs. Just the following:
- D4 and D5 (left side)
- GND (right side)
- D14 and D15 (left side)
- GND (right side, third pin from the top)
- GND (right side, third pin from the bottom)
- 3v3_OUT (right side)
- VBUS (right side)
- the 3 DEBUG pin on the bottom: SWCLK, GND and SWDIO
- place the 3D printer spacer or a piece of tape on the parts of the OLED that my touch the Raspberry
- solder the OLED (with a header) on the 4 PIN space
@@ -78,14 +100,18 @@ Otherwise you should the opposite and place the solder on the other PADs:
<img src="https://github.com/cecio/USBvalve/blob/main/pictures/pico-pinout.svg" alt="Pico Pi" width="85%" height="85%" />
If you are using a breadboard or just wiring, all you have to do is to ensure to connect the proper PINs at the OLED screen.
If you are using a breadboard or just wiring, all you have to do is to ensure to connect the proper PINs at the OLED screen and to the Host USB port.
The mapping is the following:
- PIN6 of Pi --> OLED SDA
- PIN7 of Pi --> OLED SCL
- PIN19 of Pi --> D+ of USB Host
- PIN20 of Pi --> D- of USB Host
- PIN23 (GND) of Pi --> GND of USB Host
- PIN38 (GND) of Pi --> OLED GND
- PIN36 (3V3OUT) of Pi --> OLED VCC
- PIN40 (VBUS) of Pi --> VCC of USB Host
If you want to use the DEBUG functions, you can also place a header on the 3 SWD PINs at the bottom of the board.
@@ -105,13 +131,12 @@ It's done!
Obviously you can also build your own firmware. To build the *standard* one I used:
- Arduino IDE 2.0.4
- Arduino IDE 2.1.0
- ~~as board I used `Raspberry Pi Pico - Arduino MBED OS RP2040` version `4.0.2`~~
- ~~`Adafruit TinyUSB Library` version `1.14.4`. Newer versions are not working because the RPI SDK of the board is stick to an older version. May be migrate the entire project directly on Raspberry Pi Pico SDK is the solution here.~~
- from version `0.7.0` I used `Adafruit TinyUSB Library` version `2.0.1` and Board `Raspberry Pi RP2040 (2.7.0)`
- `Adafruit TinyUSB Library` version `2.2.0` and Board `Raspberry Pi RP2040 (2.7.0)` setting clock at 240MHz (overclock)
- `ssd1306` OLED library version `1.8.3`
If you want to re-create a new fake filesystem, you may want to have a look to the `utils` folder, where I placed some utilities to build a new one.
**NOTE**: if you have ideas or improvements in your mind, I encourage you to open an issue so that we can improve the project together! Thanks!

BIN
STL/Box_USBvalve_1.1.stl Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -2,7 +2,7 @@
USBvalve
written by Cesare Pizzi
This project extensively reuse code done by Adafruit. Please support them!
This project extensively reuse code done by Adafruit and TinyUSB. Please support them!
*/
/*********************************************************************
@@ -16,13 +16,33 @@
any redistribution
*********************************************************************/
#include <pio_usb.h>
#include "Adafruit_TinyUSB.h"
#include "SSD1306AsciiWire.h"
//
// BADUSB detector section
//
/*
* Requirements:
* - [Pico-PIO-USB](https://github.com/sekigon-gonnoc/Pico-PIO-USB) library
* - 2 consecutive GPIOs: D+ is defined by HOST_PIN_DP (gpio2), D- = D+ +1 (gpio3)
* - CPU Speed must be either 120 or 240 Mhz. Selected via "Menu -> CPU Speed"
*/
#define HOST_PIN_DP 14 // Pin used as D+ for host, D- = D+ + 1
#define LANGUAGE_ID 0x0409 // English
// USB Host object
Adafruit_USBH_Host USBHost;
// END of BADUSB detector section
// Define vars for OLED screen
#define I2C_ADDRESS 0x3C // 0X3C+SA0 - 0x3C or 0x3D
#define RST_PIN -1 // Define proper RST_PIN if required.
#define OLED_HEIGHT 64 // 64 or 32 depending on the OLED
#define OLED_HEIGHT 32 // 64 or 32 depending on the OLED
#define OLED_LINES (OLED_HEIGHT / 8)
SSD1306AsciiWire oled;
@@ -54,18 +74,17 @@ bool activeState = false;
//
// USBvalve globals
//
#define VERSION "USBvalve - 0.7.0"
#define VERSION "USBvalve - 0.9.1"
boolean readme = false;
boolean autorun = false;
boolean written = false;
boolean written_reported = false;
int x = 2;
#define BLOCK_AUTORUN 102 // Block where Autorun.inf file is saved
#define BLOCK_README 100 // Block where README.txt file is saved
#define MAX_DUMP_BYTES 16 // Used by the dump of the debug facility: do not increase this too much
#define BYTES_TO_HASH 512 * 2 // Number of bytes of the RAM disk used to check consistency
#define BYTES_TO_HASH_OFFSET 7 // Starting sector to check for consistency (FAT_DIRECTORY is 7)
#define BYTES_TO_HASH_OFFSET 7 // Starting sector to check for consistency (FAT_DIRECTORY is 7)
// Burned hash to check consistency
u8 valid_hash[WIDTH] = {
@@ -79,7 +98,7 @@ u8 valid_hash[WIDTH] = {
u8 computed_hash[WIDTH] = { 0x00 };
// Core 0 Setup
// Core 0 Setup: will be used for the USB mass device functions
void setup() {
// Check consistency of RAM FS
quark(computed_hash, msc_disk[BYTES_TO_HASH_OFFSET], BYTES_TO_HASH);
@@ -127,46 +146,59 @@ void setup() {
#endif
oled.setFont(Adafruit5x7);
oled.setScrollMode(SCROLL_MODE_AUTO);
cls(); // Clear display
if (memcmp(computed_hash, valid_hash, WIDTH) == 0) {
oled.println("[+] Selftest: OK");
x++;
oled.print("\n[+] Selftest: OK");
} else {
oled.println("[!] Selftest: KO");
oled.println("[!] Stopping...");
oled.print("\n[!] Selftest: KO");
oled.print("\n[!] Stopping...");
while (1) {
delay(1000); // Loop forever
}
}
}
// Main Core0 loop, managing display
// Core 1 Setup: will be used for the USB host functions for BADUSB detector
void setup1() {
//while ( !Serial ) delay(10); // wait for native usb
pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
pio_cfg.pin_dp = HOST_PIN_DP;
USBHost.configure_pio_usb(1, &pio_cfg);
// run host stack on controller (rhport) 1
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
// host bit-banging processing works done in core1
USBHost.begin(1);
}
// Main Core0 loop: managing display
void loop() {
if (readme == true) {
if (x == OLED_LINES) cls();
oled.println("[!] README (R)");
x++;
oled.print("\n[!] README (R)");
readme = false;
}
if (autorun == true) {
if (x == OLED_LINES) cls();
oled.println("[+] AUTORUN (R)");
x++;
oled.print("\n[+] AUTORUN (R)");
autorun = false;
}
if (written == true && written_reported == false) {
if (x == OLED_LINES) cls();
oled.println("[!] WRITING");
x++;
oled.print("\n[!] WRITING");
written = false;
written_reported = true;
}
}
// Main Core1 loop: managing USB Host
void loop1() {
USBHost.task();
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and
// return number of copied bytes (must be multiple of block size).
@@ -250,9 +282,8 @@ bool msc_ready_callback(void) {
// Clear display
void cls(void) {
oled.clear();
oled.println(VERSION);
oled.println("----------------");
x = 2;
oled.print(VERSION);
oled.print("\n----------------");
}
// HexDump
@@ -289,4 +320,188 @@ void hexDump(unsigned char* data, size_t size) {
}
}
SerialTinyUSB.println();
}
}
//
// BADUSB detector section
//
static uint8_t const keycode2ascii[128][2] = { HID_KEYCODE_TO_ASCII };
// Invoked when device with hid interface is mounted
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) {
uint16_t vid, pid;
const char* protocol_str[] = { "None", "Keyboard", "Mouse" };
// Read the HID protocol
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
tuh_vid_pid_get(dev_addr, &vid, &pid);
oled.print("\n[!!] HID Device");
SerialTinyUSB.printf("HID device address = %d, instance = %d mounted\r\n", dev_addr, instance);
SerialTinyUSB.printf("VID = %04x, PID = %04x\r\n", vid, pid);
SerialTinyUSB.printf("HID Interface Protocol = %s\r\n", protocol_str[itf_protocol]);
if (!tuh_hid_receive_report(dev_addr, instance)) {
SerialTinyUSB.printf("Error: cannot request to receive report\r\n");
}
}
// Invoked when device with hid interface is un-mounted
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
SerialTinyUSB.printf("HID device address = %d, instance = %d unmounted\r\n", dev_addr, instance);
}
// Invoked when received report from device
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) {
static bool kbd_printed = false;
static bool mouse_printed = false;
oled.print("\n[!!] HID Sending data");
// Read the HID protocol
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
switch (itf_protocol) {
case HID_ITF_PROTOCOL_KEYBOARD:
if (kbd_printed == false) {
SerialTinyUSB.println("HID received keyboard report");
kbd_printed = true;
mouse_printed = false;
}
process_kbd_report((hid_keyboard_report_t const*)report);
break;
case HID_ITF_PROTOCOL_MOUSE:
if (kbd_printed == false) {
SerialTinyUSB.println("HID receive mouse report");
mouse_printed = true;
kbd_printed = false;
}
process_mouse_report((hid_mouse_report_t const*)report);
break;
default:
// Generic report: for the time being we use kbd for this as well
process_kbd_report((hid_keyboard_report_t const*)report);
break;
}
if (!tuh_hid_receive_report(dev_addr, instance)) {
SerialTinyUSB.println("Error: cannot request to receive report");
}
}
static inline bool find_key_in_report(hid_keyboard_report_t const* report, uint8_t keycode) {
for (uint8_t i = 0; i < 6; i++) {
if (report->keycode[i] == keycode) return true;
}
return false;
}
static void process_kbd_report(hid_keyboard_report_t const* report) {
// Previous report to check key released
static hid_keyboard_report_t prev_report = { 0, 0, { 0 } };
for (uint8_t i = 0; i < 6; i++) {
if (report->keycode[i]) {
if (find_key_in_report(&prev_report, report->keycode[i])) {
// Exist in previous report means the current key is holding
} else {
// Not existed in previous report means the current key is pressed
// Check for modifiers. It looks that in specific cases, they are not correctly recognized (probably
// for timing issues in fast input)
bool const is_shift = report->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT);
uint8_t ch = keycode2ascii[report->keycode[i]][is_shift ? 1 : 0];
bool const is_gui = report->modifier & (KEYBOARD_MODIFIER_LEFTGUI | KEYBOARD_MODIFIER_RIGHTGUI);
if (is_gui == true) SerialTinyUSB.printf("GUI+");
bool const is_alt = report->modifier & (KEYBOARD_MODIFIER_LEFTALT | KEYBOARD_MODIFIER_RIGHTALT);
if (is_alt == true) SerialTinyUSB.printf("ALT+");
// Check for "special" keys
check_special_key(report->keycode[i]);
// Finally, print out the decoded char
SerialTinyUSB.printf("%c", ch);
if (ch == '\r') SerialTinyUSB.print("\n"); // New line for enter
fflush(stdout); // flush right away, else nanolib will wait for newline
}
}
}
prev_report = *report;
}
static void check_special_key(uint8_t code) {
if (code == HID_KEY_ARROW_RIGHT) SerialTinyUSB.print("<ARROWRIGHT>");
if (code == HID_KEY_ARROW_LEFT) SerialTinyUSB.print("<ARROWLEFT>");
if (code == HID_KEY_ARROW_DOWN) SerialTinyUSB.print("<ARROWDOWN>");
if (code == HID_KEY_ARROW_UP) SerialTinyUSB.print("<ARROWUP>");
if (code == HID_KEY_HOME) SerialTinyUSB.print("<HOME>");
if (code == HID_KEY_KEYPAD_1) SerialTinyUSB.print("<KEYPAD_1>");
if (code == HID_KEY_KEYPAD_2) SerialTinyUSB.print("<KEYPAD_2>");
if (code == HID_KEY_KEYPAD_3) SerialTinyUSB.print("<KEYPAD_3>");
if (code == HID_KEY_KEYPAD_4) SerialTinyUSB.print("<KEYPAD_4>");
if (code == HID_KEY_KEYPAD_5) SerialTinyUSB.print("<KEYPAD_5>");
if (code == HID_KEY_KEYPAD_6) SerialTinyUSB.print("<KEYPAD_6>");
if (code == HID_KEY_KEYPAD_7) SerialTinyUSB.print("<KEYPAD_7>");
if (code == HID_KEY_KEYPAD_8) SerialTinyUSB.print("<KEYPAD_8>");
if (code == HID_KEY_KEYPAD_9) SerialTinyUSB.print("<KEYPAD_9>");
if (code == HID_KEY_KEYPAD_0) SerialTinyUSB.print("<KEYPAD_0>");
if (code == HID_KEY_F1) SerialTinyUSB.print("<F1>");
if (code == HID_KEY_F2) SerialTinyUSB.print("<F2>");
if (code == HID_KEY_F3) SerialTinyUSB.print("<F3>");
if (code == HID_KEY_F4) SerialTinyUSB.print("<F4>");
if (code == HID_KEY_F5) SerialTinyUSB.print("<F5>");
if (code == HID_KEY_F6) SerialTinyUSB.print("<F6>");
if (code == HID_KEY_F7) SerialTinyUSB.print("<F7>");
if (code == HID_KEY_F8) SerialTinyUSB.print("<F8>");
if (code == HID_KEY_F9) SerialTinyUSB.print("<F9>");
if (code == HID_KEY_F10) SerialTinyUSB.print("<F10>");
if (code == HID_KEY_F11) SerialTinyUSB.print("<F11>");
if (code == HID_KEY_F12) SerialTinyUSB.print("<F12>");
if (code == HID_KEY_PRINT_SCREEN) SerialTinyUSB.print("<PRNT>");
if (code == HID_KEY_SCROLL_LOCK) SerialTinyUSB.print("<SCRLL>");
if (code == HID_KEY_PAUSE) SerialTinyUSB.print("<PAUSE>");
if (code == HID_KEY_INSERT) SerialTinyUSB.print("<INSERT>");
if (code == HID_KEY_PAGE_UP) SerialTinyUSB.print("<PAGEUP>");
if (code == HID_KEY_DELETE) SerialTinyUSB.print("<DEL>");
if (code == HID_KEY_END) SerialTinyUSB.print("<END>");
if (code == HID_KEY_PAGE_DOWN) SerialTinyUSB.print("<PAGEDOWN>");
if (code == HID_KEY_NUM_LOCK) SerialTinyUSB.print("<ARROWRIGHT>");
if (code == HID_KEY_KEYPAD_DIVIDE) SerialTinyUSB.print("<KEYPAD_DIV>");
if (code == HID_KEY_KEYPAD_MULTIPLY) SerialTinyUSB.print("<KEYPAD_MUL>");
if (code == HID_KEY_KEYPAD_SUBTRACT) SerialTinyUSB.print("<KEYPAD_SUB>");
if (code == HID_KEY_KEYPAD_ADD) SerialTinyUSB.print("<KEYPAD_ADD>");
if (code == HID_KEY_KEYPAD_DECIMAL) SerialTinyUSB.print("<KEYPAD_DECIMAL>");
}
static void process_mouse_report(hid_mouse_report_t const* report) {
static hid_mouse_report_t prev_report = { 0 };
//------------- button state -------------//
uint8_t button_changed_mask = report->buttons ^ prev_report.buttons;
if (button_changed_mask & report->buttons) {
SerialTinyUSB.printf("MOUSE: %c%c%c ",
report->buttons & MOUSE_BUTTON_LEFT ? 'L' : '-',
report->buttons & MOUSE_BUTTON_MIDDLE ? 'M' : '-',
report->buttons & MOUSE_BUTTON_RIGHT ? 'R' : '-');
}
cursor_movement(report->x, report->y, report->wheel);
}
void cursor_movement(int8_t x, int8_t y, int8_t wheel) {
SerialTinyUSB.printf("(%d %d %d)\r\n", x, y, wheel);
}
// END of BADUSB detector section

58
docs/BUILDING-1.1.md Normal file
View File

@@ -0,0 +1,58 @@
#### With USBvalve PCB
<p float="left">
<img src="https://github.com/cecio/USBvalve/blob/main/pictures/USB_valve_front.png" width="25%" height="25%" />
<img src="https://github.com/cecio/USBvalve/blob/main/pictures/USB_valve_back.png" width="25%" height="25%" />
</p>
- place the Raspberry Pi Pico on the silk screen on the front
- you don't need to solder all the PINs. Just the following:
- D4 and D5 (left side)
- GND (right side)
- 3v3_OUT (right side)
- the 3 DEBUG pin on the bottom: SWCLK, GND and SWDIO
- place the 3D printer spacer or a piece of tape on the parts of the OLED that my touch the Raspberry
- solder the OLED (with a header) on the 4 PIN space
Some of the OLEDs have the GND and VCC PINs swapped, so I built the PCB to be compatible with both versions:
For example if your OLED has GND on PIN1 and VCC on PIN2 like this:
<img src="https://github.com/cecio/USBvalve/blob/main/pictures/usb_valve_oled.png" width="35%" height="35%" />
You have to place a blob of solder on these two pads on the back of the PCB:
<img src="https://github.com/cecio/USBvalve/blob/main/pictures/usb_valve_pads.png" width="15%" height="15%" />
Otherwise you should the opposite and place the solder on the other PADs:
<img src="https://github.com/cecio/USBvalve/blob/main/pictures/usb_valve_pads_2.png" width="15%" height="15%" />
#### Without USBvalve PCB
<img src="https://github.com/cecio/USBvalve/blob/main/pictures/pico-pinout.svg" alt="Pico Pi" width="85%" height="85%" />
If you are using a breadboard or just wiring, all you have to do is to ensure to connect the proper PINs at the OLED screen.
The mapping is the following:
- PIN6 of Pi --> OLED SDA
- PIN7 of Pi --> OLED SCL
- PIN38 (GND) of Pi --> OLED GND
- PIN36 (3V3OUT) of Pi --> OLED VCC
If you want to use the DEBUG functions, you can also place a header on the 3 SWD PINs at the bottom of the board.
### Flash Firmware
To flash the firmware, follow these steps:
- Connect the Raspberry Pi Pico with the USB cable, by keeping the *BOOTSEL* button pressed (the big white button on the board)
- release the button
- you will see a new drive on the system, named `RPI-RP2` (in Linux envs you may have to manually mount it)
- copy the proper firmware file (with extension `uf2`) in the folder, depending on the OLED you used
- wait few seconds until the mounted folder disappear
It's done!

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
pictures/hid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB