-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathuberducky.c
343 lines (285 loc) · 9.78 KB
/
uberducky.c
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/*
* Copyright 2019 Mike Ryan
*
* This file is part of Uberducky and is released under the terms of the
* GPL version 2. Refer to COPYING for more information.
*/
#include "hid.h"
#include "ble.h"
#include "type.h"
#include "ubertooth.h"
#include "usbapi.h"
#include <string.h>
// magic string that must be present in trigger packets
// derived from random UUID:
// fd123ff9-9e30-45b2-af0d-b85b7d2dc80c
uint8_t ble_magic[16] = {
0x0c, 0xc8, 0x2d, 0x7d, 0x5b, 0xb8, 0x0d, 0xaf,
0xb2, 0x45, 0x30, 0x9e, 0xf9, 0x3f, 0x12, 0xfd,
};
// magic string that triggers bootloader mode
// random UUID:
// 344bc7f2-5619-4953-9be8-9888fe29d996
uint8_t bootloader_magic[16] = {
0x96, 0xd9, 0x29, 0xfe, 0x88, 0x98, 0xe8, 0x9b,
0x53, 0x49, 0x19, 0x56, 0xf2, 0xc7, 0x4b, 0x34,
};
extern uint8_t script[]; // auto-generated from duckyscript input
// times in ms
#define LED_PERIOD 600
#define LED_ON_TIME 100
#define INTR_IN_EP 0x81
#define LE_WORD(x) ((x)&0xFF),((x)>>8)
#define READ_LE(x) ((script[x+1] << 8) | script[x])
#define NOW T0TC
// script state
#define ST_IDLE 0
#define ST_READY 1
#define ST_RUNNING 2
// run state
#define R_IDLE 0
#define R_KEY_DOWN 1
#define R_DELAY 2
#define R_STRING 3
// string state
#define S_IDLE 0
#define S_KEY_DOWN 1
int script_state = ST_IDLE;
int run_state = R_IDLE;
int string_state = S_IDLE;
unsigned script_len = 0;
unsigned script_pos = 0;
unsigned string_len = 0;
unsigned string_pos = 0;
uint32_t repeat_counter = 0;
unsigned repeat_pos = 0;
int repeating = 0;
// opcodes
//
// encoding: <op> [<arg> .. ]
//
// NOP - no args
// KEY - key type, modifier, character (each 1 byte)
// DELAY - delay in ms (16 bit little endian)
// STRING - length (16 bit little endian), chars
// REPEAT - repeat previous command
#define OP_NOP 0
#define OP_KEY 1
#define OP_DELAY 2
#define OP_STRING 3
#define OP_REPEAT 4
#define DELAY(X) OP_DELAY, LE_WORD(X)
// demo script - print hello world
/* this is now loaded from an autogenerated .c file
uint8_t script[] = {
LE_WORD(24), // script len
OP_STRING, // string
LE_WORD(6), // string len
'h', 'e', 'l', 'l', 'o', ' ',
DELAY(1000), // delay
OP_STRING, // string
LE_WORD(5),
'w', 'o', 'r', 'l', 'd',
OP_KEY, // key - enter with no modifier
K_ENTER, 0x00, 0x00,
};
*/
static void timer0_start(void) {
T0TCR = TCR_Counter_Reset;
T0PR = 50000 - 1; // 1 ms
T0TCR = TCR_Counter_Enable;
// set up interrupt handler
ISER0 = ISER0_ISE_TIMER0;
}
static void timer0_set_match(uint32_t match) {
T0MR0 = match;
T0MCR |= TMCR_MR0I;
}
static void timer0_clear_match(void) {
T0MCR &= ~TMCR_MR0I;
}
void TIMER0_IRQHandler(void) {
uint8_t report[8] = { 0, };
keystroke_t next_key = { 0, };
if (T0IR & TIR_MR0_Interrupt) {
// ack the interrupt
T0IR = TIR_MR0_Interrupt;
if (script_state == ST_READY) {
script_pos = 0;
script_state = ST_RUNNING;
run_state = R_IDLE;
script_len = READ_LE(script_pos);
script_pos += 2;
// re-enter in 1 ms
timer0_set_match(NOW + 1);
} else if (script_state == ST_RUNNING) {
uint8_t opcode;
uint16_t delay;
switch (run_state) {
// idle -- get next opcode
case R_IDLE:
if (script_pos >= script_len) {
script_state = ST_IDLE;
return;
}
if (repeating) {
if (repeat_counter == 0) {
repeating = 0;
script_pos += 3; // skip repeat opcode
} else {
--repeat_counter;
script_pos = repeat_pos; // jump back to prev op
}
}
opcode = script[script_pos++];
if (opcode != OP_REPEAT)
repeat_pos = script_pos-1;
switch (opcode) {
case OP_NOP:
++script_pos;
timer0_set_match(NOW + 1); // re-enter in 1 ms
return;
case OP_KEY:
// TODO bounds check
next_key.type = script[script_pos++];
next_key.mod = script[script_pos++];
next_key.chr = script[script_pos++];
// encode and inject key
hid_encode(&next_key, report);
USBHwEPWrite(INTR_IN_EP, report, 8);
run_state = R_KEY_DOWN;
timer0_set_match(NOW + DOWN_TIME);
return;
case OP_DELAY:
// TODO bounds check
delay = READ_LE(script_pos);
script_pos += 2;
run_state = R_DELAY;
timer0_set_match(NOW + delay);
return;
case OP_STRING:
// TODO bounds check
string_len = READ_LE(script_pos);
string_pos = 0;
script_pos += 2;
run_state = R_STRING;
string_state = S_IDLE;
timer0_set_match(NOW + 1); // re-enter in 1 ms
return;
case OP_REPEAT:
repeating = 1;
repeat_counter = READ_LE(script_pos);
timer0_set_match(NOW + 1); // re-enter in 1 ms
return;
}
return;
// key down - lift key
case R_KEY_DOWN:
// all keys up
USBHwEPWrite(INTR_IN_EP, report, 8);
timer0_set_match(NOW + DOWN_TIME);
run_state = R_IDLE;
// timer0_set_match(NOW + 1);
return;
case R_DELAY:
run_state = R_IDLE;
timer0_set_match(NOW + 1);
return;
// string - sub state machine
case R_STRING:
switch (string_state) {
// idle - get next key
case S_IDLE:
// end of string, next
if (string_pos >= string_len) {
script_pos += string_len;
run_state = R_IDLE;
timer0_set_match(NOW + 1);
return;
}
next_key.type = K_CHAR;
next_key.mod = 0;
next_key.chr = script[script_pos + string_pos];
++string_pos;
hid_encode(&next_key, report);
USBHwEPWrite(INTR_IN_EP, report, 8);
string_state = S_KEY_DOWN;
timer0_set_match(NOW + DOWN_TIME);
return;
// key down - lift and go back to idle state
case S_KEY_DOWN:
USBHwEPWrite(INTR_IN_EP, report, 8);
timer0_set_match(NOW + DOWN_TIME);
string_state = S_IDLE;
return;
}
return;
}
}
}
// LEDs
if (T0IR & TIR_MR1_Interrupt) {
T0IR = TIR_MR1_Interrupt;
RXLED_CLR;
}
}
int magic_present(uint8_t *packet, uint8_t *magic) {
unsigned i;
for (i = 0; i < BLE_PACKET_SIZE - 16; ++i)
if (memcmp(packet + i, magic, 16) == 0)
return 1;
return 0;
}
// from usb.c
void usb_init(void);
int main() {
uint8_t ble_packet[BLE_PACKET_SIZE];
int led_state = 0;
uint32_t led_next_event = LED_PERIOD - LED_ON_TIME;
ubertooth_init();
timer0_start();
usb_init();
ble_init();
// call USB interrupt handler continuously
while (1) {
USBHwISR();
// fetch BLE packets
if (ble_get_packet(ble_packet)) {
// blink LED - TODO something more interesting
RXLED_SET;
T0MR1 = NOW + 10;
T0MCR |= TMCR_MR1I;
// launch script if magic string is in packet and we're idle
if (script_state == ST_IDLE && magic_present(ble_packet, ble_magic)) {
script_state = ST_READY;
timer0_set_match(NOW + 1);
}
// if the bootloader magic is present, reset to bootloader
else if (magic_present(ble_packet, bootloader_magic)) {
// turn off radio
cc2400_strobe(SRFOFF);
while ((cc2400_status() & FS_LOCK)); // need to wait for unlock?
#ifdef UBERTOOTH_ONE
PAEN_CLR;
HGM_CLR;
#endif
// reset
bootloader_ctrl = DFU_MODE;
reset();
}
}
// blink LED
if (NOW >= led_next_event) {
if (led_state == 0) {
led_state = 1;
TXLED_SET;
led_next_event += LED_ON_TIME;
} else {
led_state = 0;
TXLED_CLR;
led_next_event += LED_PERIOD - LED_ON_TIME;
}
}
}
return 0;
}