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)

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 micronucleus bootloader, thus allowing to use the micronucleus 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 uploader is programmed it starts to run and will wait for the micronucleus console to send data, but this time instead of burning Attiny with the data received it will transfer the payload to 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!

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

  • Initial release of the compiler

    danjovic30 minutes ago 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

    danjovican hour ago 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 6 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