Ugly USB

After the Good and the Bad here you are the Ugly USB!

Similar projects worth following
We have seen some Digispark based rubber ducks on the net but so far they all suffer from limited amount of memory for scripts. Not anymore!

Ugly USB is a Digispark with a 64Kbyte I2C EEPROM so it can provide plenty of space for the scripts. But hey, this is cool!, What's What's Ugly on that?

Well, besides the shameless reference to the western movie, the price for the lunch is that the uploading of the scripts takes three steps:

1. Upload EEPROM firmware loader
2. Upload the EEPROM contents using PC application
3. Upload the script player firmware

You might think this is not so bad, but this is absolutely not beautiful (and I am not even considering the deadbug style mounting)

** Update **

The project is composed by a hardware and a software part. The hardware consists of a Digispark and a 24LC512 EEPROM whilst the software part is project is composed by two firmware pieces one to playback the scripts and another to load the scripts to the EEPROM. The latter has a companion application that runs on the PC.

The uploader application shall be based on littlewire firmware, thus allowing to use a console tool  to send it to the digispark.

The payload is converted from Duck Script to a meta script which is mostly the tokenized version of Duck Script commands, with is thoroughly based on the behavior of BASIC interpreters.

Right after the littlewire firmware is programmed it starts to run and will wait for the console application to send the payload data that will be recorded on the EEPROM

After the payload is sent to the EEPROM, the digispark is restarted and then the script player starts to read the contents of the EEPROM and send the appropriate keystrokes to the ducked PC. Quack!

Step 1 can be eliminated by using two digispark boards, one with littlewire firmware and another with the player. The connection between both boards is only necessary during the recordingof the payload on the EEPROM.

  • 1 × Digispark
  • 1 × 24LC512 Memory ICs / EEPROMs

  • Not much progress so far

    danjovic12/01/2017 at 03:20 0 comments

    So far I haven't achieved too much success with my project and those have been the pitfalls I have found along the way. I have never imagined that I would face so much trouble for such a simple project.

    - I figured out how to extend the micronucleus bootloader to allow sending packets of data to be recorded on external eeprom instead of flashing it's own flash memory. This would save one step when uploading the scripts, but I couldn't make it work an bricked the AVR several times. At least I am using a DIP8 microcontroller and so it is  relatively easy to recover.

    - Bad documentation/examples: Digispark has a HID library named DigiUSB but it looks that it have been deprecated and it is recommended to use the DigiCDC library which is poorly documented as well. My linux machine can recognize the USB device but can not detect the correspondent serial port. Dead end again!

    - Using a simple keyboard sketch with DigiKeyboard library It was noticed that when the device is plugged on a machine that has no libusb drivers installed the board stalls after the bootloader times out, being not recognized as a HID device.  This is not a minor issue as it will require the bootloader to be modified. 

    At least I  got  some progress installing the Littlewire firmware which does have support for I2C (and other protocols as well) with a relatively easy interface and some few examples on how to use it. 

    Time to design a new architecture for the project....

  • Setting the development environment on Linux

    danjovic06/01/2017 at 01:16 0 comments

    In order to continue the development on my Linux machine, it was necessary to:

    1) Install the Arduino IDE

    Download and unpack the Arduino IDE 1.8.2

    Read the installation guide, but before proceed to the installation it might be necessary to edit the installation script.

    If you get errors during installation try this:

    Edit the script and edit the following line:

    Change it to :


    then run the ./install script (note: without sudo!!)

    2) Install the Digispark support add-on

    3) Create a udev rule to allow the micronucleus bootloader to work without being root.

    This file shall be created as a root. In Ubuntu:

    sudo mousepad /etc/udev/rules.d/49-micronucleus.rules

    mousepad is the name of the text editor. Can be either gedit, vi, emacs, etc...

  • Added block commands

    danjovic04/30/2017 at 20:12 0 comments

    Now it is possible to expand the DuclyScript by repeating blocks of commands.

    For example the following script will open 10 notepads an type in Hello World.

    DELAY 3000      
    GUI r                    
    DELAY 500                
    STRING notepad           
    DELAY 500                
    DELAY 750                
    STRING Hello World!!!
    AGAIN 10   

    The start of a block is marked by BEGIN command and the end of the block is marked by the AGAIN command which receives an integer parameter.

    Sources for the Test Sketch as well as for the Compiler were updated on Github as well.

  • Ugly Player Test Sketch

    danjovic04/30/2017 at 17:38 0 comments

    I have developed a Test Sketch for plain Arduino with the objective of verfiy which codes would be sent through USB while developing the Ugly Token player.

    The code is available on Github and so far all the commands are working as expected.

  • Initial release of the compiler

    danjovic04/30/2017 at 04:35 0 comments

    Initial release of the compiler, based on Python-duckencode by crushedice2000. It generates a .bin file according with Ugly Token version 1.1, except the commands BEGIN and AGAIN

    Available now in github

    Here's and example of a script file aside with the equivalent tokens

  • New Token definition

    danjovic04/30/2017 at 03:59 0 comments

    During the development of the compiler I realized that some of the definitions could be discarded, leaving room for future improvements in Duckyscript and at the same time added some extensions.

    The improvements are:

    • The token for DEFAULT DELAY was changed to 0xDF
    • The token for DELAY was changed to 0xDE

    the modifications above make easier to track the commands while inspecting the binary file (they are mnemonic just in case you haven't noticed)

    • The specific tokens for CONTROL, ALT, SHIFT, GUI (0xA8..0XAB) have been dismissed. Instead the HOLD token (0xAF) is used preceeding the code of the combination key desired.

    The HOLD token allows a combination of several keys like CONTROL + ALT + T to invoke a terminal on linux systems.

    • The STRING token has been dismissed. Instead the HID keycode of each character is directly stored.

    The upper case letters as well as some symbols are preceded by HOLD token plus LSHIFT token in order to produce the desired character, for instance @ is generated by issuing HOLD + LSHIFT + KEY_2 (0xAF, 0xE0, 0x1F)

    • The RELEASE token (0xAE) was created as a safeguard to release all keys that eventually have been held by a command.

    I suspect that further improvements on the compiler might render this command useless.

    • The BEGIN (0xA5) and AGAIN (0xA6) tokens have been created. The objective of such commands is to allow the repetition of blocks of commands.

    The latter two commands are only defined by now and both are yet to be implemented.

    The new Token specification can be seen in the table below:

  • Talk is cheap

    danjovic04/21/2017 at 13:19 0 comments

    Talk is cheap, show me some code...

    Here it is, the skeleton of the interpreter

    // Ugly Token Player
    while ( (EEprom_Addr < FLASH_SIZE) && !End_of_Script )  {
        c = Ext_EEpromRead( EEprom_Addr);
        switch (c) {
            case 0xff:    
                !End_of_Script = TRUE ; break;
            case 0x00:    // end of command
                Last_EEprom_Addr = EEprom_Addr; break;             // save this address for
                                                                   // eveventual return
            case 0xA5: // Set Default Delay    
                Default_Delay = Ext_EEpromRead16 (EEprom_Addr +1); // read value
                if (Default_Delay==0)                              // check range
                    Default_Delay = INIT_DEFAULT_DELAY;            //
                //Last_EEprom_Addr = EEprom_Addr;                    // advance address
                EEprom_Addr +=2;                                   //
            case 0xA6: // Delay
                Time_to_Delay = Ext_EEpromRead16 (EEprom_Addr +1);
                if (Time_to_Delay==0)                              // check range
                    Time_to_Delay = INIT_DEFAULT_DELAY;            //
                //Last_EEprom_Addr = EEprom_Addr;                    // advance address
                EEprom_Addr +=2;                                   //            
            case 0xA7: // Replay last command
                if (Replaying) {
                    EEprom_Addr = Last_EEprom_Addr;
                    if (Replay_Counter ==0) {  // replay is over!
                        EEprom_Addr += 3 ; // skip to the next command
                        Replaying = RALSE;
                } else {
                    Replaying = TRUE;
                    Replay_Counter = Ext_EEpromRead16 (EEprom_Addr +1);
                    if (Replay_Counter==0)                              // check range
                        Replay_Counter = 1;            //
                    EEprom_Addr = Last_EEprom_Addr;                    // rewind address                
            case 0xA8: // Hold Shift
                 0xA9: // Hold ALT
                 0xAA: // Hold CONTROL
                 0xAB: // Hold GUI
                Hold_Next_Key = 1; break ;                         // Just hold the next key
            default:  // Send the character
            if (Hold_Next_Key)                                     // If next key should remain hold
                Hold_Next_Key = 0;                                 // then do nothing
            else {                                                 //
                digiKeyboard.sendKeyPress(0);                      // otherwise, send a release of
                digiKeyboard.delay(Default_Delay);                   // all key presses
            EEprom_Addr++;                                         // advance to the next character
    for (;;);  // do nothing after playing the script

  • ​ Replaying Strategy and String command

    danjovic04/21/2017 at 13:17 0 comments

    The interpreter use a flag named 'Replaying' to differentiate the first time the Replay token is found from the consecutive times.

    Whenever the Replay command is found during the execution the 'Replaying' is check. If its False (zero) then the amount of times to repeat (Replay_Counter) is read from eeprom as an unsigned 16 bit and the flag is set True (not zero) and the 'loop' address is set by attributing to the current address (EEprom_Addr) the last address recorded for this purpose (Last_EEprom_Addr).
    The next iterations, with Relaying flag set, will decrement the variable Replay_Counter until it reaches zero. When it occurs, the current address is added by three so it points right after the Replay command.

    The last address recorded is in practice a Return Address and it is recorded everytime the interpreter gets a 0x00 command. Thus to repeat a command it shall be preceeded by 0x00. This subtle detail makes possible to repeat not only the last command, but rather it allows to repeat a block of commands.

    The way the interpreter is implemented makes the STRING command (0xAC) rather useless, since the HID codes are simply typed in. One remark though, is that shifted characters shall be transmitted as a hold+shift+character

    STRING hello!
    h    e    l    l    o    hold shift 1
    0x0b 0x08 0x0f 0x0f 0x12 0xA8 0xE1 0x1E

  • Tiny Enhancements

    danjovic04/21/2017 at 12:34 0 comments

    I've decided to enhance the mechanism os composing keys. With the original Ducky Scripy it is only possible to compose keys with SHIFT, ALT, CONTROL and GUI.
    The solution to allow several keys to be pushed is use a flag Hold_Next_Key which simply tells the Ugly interpreter to not send a key release after the next valid key.
    With that, the combination keys tokens 0xa8 to 0xab have the same effect that is to set the Hold_Next_Key flag.

    With that in mind a GUI R command shall be sent as:

    hold  gui    r
    0xAB  0xE3  0x15

    Then a CONTROL ALT DEL should be sent as:

    hold  contol  hold   alt   del
    0xAB   0xE0   0xAB   0xE2  0x2A

  • Planning the Tokens

    danjovic04/07/2017 at 19:14 0 comments

    Started to plan the tokens for the translated duckscript file.

    The end of line is 0x00, just like the strings.

    The end of contents is 0xFF, which will tell the player to stop sending commands.

    the commands









    are allocated on the unused hid codes 0xA5 to 0xAC ( 0xAD to 0xAF and 0xDE 0xDF)

    The remaining keys use their own hid code.

    The STRING command parse the following characters up to the next 0x00 (or 0xFF) to differentiate between shifted and unshifted characters.

    The REPLAY command uses the saved EEPROM address from the previous command to point to the next instruction to be performed. There is one boolean variable, though, to know when to stop replaying the commands.

View all 10 project logs

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates