Reverse-Engineering und Verändern der Firmware des „ThinkPad Compact USB Keyboard with TrackPoint“

Ich besitze eine USB-Tastatur, die ich eigentlich sehr mag: Das „Lenovo ThinkPad Compact USB Keyboard with TrackPoint“ (KU-1255). Es hat einen Trackpoint, was sehr praktisch ist, und tippt sich ganz nett. Allerdings sind die werten Leute bei Lenovo übers Ziel hinausgeschossen: Sie haben das Scrollen mit der mittleren Maustaste in der Hardware implementiert, und zwar falsch.

Nerviger Fehler

Die Hardware hat mehrere Modi. So kann man die mittlere Maustaste deaktivieren oder aktivieren. Aktiviert man sie, kann man die mittlere Maustaste normal benutzen. Allerdings bekommt man als Bonus, dass solange man die mittlere Maustaste gedrückt hält, vertikale Trackpoint-Bewegungen in Scroll-Ereignisse übersetzt werden. Aber nur vertikale, keine horizontale. Und dazu so grob, dass man höchstens zeilenweise, aber keineswegs pixelweise scrollen kann.

Hinzu kommt, dass beim Loslassen der Maustaste wieder Ereignis ausgelöst wird. ein mittlerer Maus-Klick.

Das macht das schöne Xorg-Feature, mit der mittleren Maustaste zu scrollen, komplett unbenutzbar.

Linux umgeht das im Kernel, aber unter Windows oder FreeBSD guckt man in die Röhre.

Lenovo erkennt das Problem nicht an, sondern hat mir lieber eine neue Tastatur geschickt, weil sie von einem Hardwareproblem ausgingen…

Reverse-Engineering der Firmware

Wohlan, ans Werk! Gucken wir doch erst einmal, was in der Tastatur drin ist: Ein Sonix SN8F2288FG. Das ist ein kleiner 8-Bit-Mikrocontroller mit 12kW ROM, 512 Bytes RAM und einer schrägen Architektur.

Auf der Lenovo-Webseite gibt es einen Updater für die Tastatur. Dort muss die Firmware drinstecken. Laden wir ihn herunter: tp_compact_usb_kb_with_trackpoint_fw.exe (MD5 2c128084412b917449a2c01da3c49451, SHA256 7116a3819ee094857d21e4671cb6cf953d582372126f0f6728f6b2421eda7bd4) und tun wir ihn in die Gratis-Version von IDA Pro.

Nach etwas Rumspielerei habe ich herausgefunden, dass die Firmware mit dem Byte 0x5A ge-XOR-t ist. Man suche nach Auto .....a..2015051111591234567817EF60478521SN8F2288, um sie zu finden.

XOR gut erkennbar

XOR gut erkennbar

Das Binary tut man in dissn8, einen freien SN8-Disassembler. Als ich anfing, musste ich einige Instruktionen hinzufügen oder ändern, damit sie korrekt dekodiert werden. Die Überprüfung auf Korrektheit habe ich durch Übersetzen in SN8 C Studio sichergestellt.

Danach habe ich mich mit dem ausgedruckten Assembler-Dump hingesetzt und nachvollzogen, wie die Firmware funktioniert.

Nichts geht über Papier und Bleistift

Nichts geht über Papier und Bleistift

Nach einer Weile habe ich die Stelle gefunden und das “Feature”, das mich seit Jahren genervt hat, schlichtweg deaktiviert. Dazu habe ich noch die Firmware-Version erhöht, um die veränderte Firmware erkennen zu können:

--- orig/firmware-annotated-SN8CStudio.asm	2019-06-10 22:06:46.782287000 +0200
+++ patched/firmware.asm	2019-06-10 22:06:54.378983000 +0200
@@ -2188,13 +2188,16 @@
 label_06d5: ; <- 06d3
   CALL   iic_stop
 ; only apply speed factor if !middle_button_pressed (i.e. scrolling):
-  BTS0   flag_middle_button_pressed
-  JMP    tp_skip_apply_speed
+;  BTS0   flag_middle_button_pressed
+;  JMP    tp_skip_apply_speed
+	NOP
+	NOP
   CALL   tp_apply_speed_x
   CALL   tp_apply_speed_y
   CALL   copy_clamp_tp_mouse_pos ; tp_mouse_d? -> mouse_d?
 tp_skip_apply_speed: ; <- 06d7
-  BTS1   flag_middle_button_pressed
+;  BTS1   flag_middle_button_pressed
+  BSET   0x14.6 ; cool, enough space to fit that. so that button 3 is reported reported properly.
   RET
   MOV    A, tp_mouse_dx
   MOV    temp_maybe_tp_dx, A
@@ -2439,8 +2442,10 @@
   BCLR   mouse_buttons.1
   BTS0   tp_mouse_buttons.1
   BSET   mouse_buttons.1
-  BTS0   flag_Thinkpad_preferred_scrolling
-  JMP    label_0792
+;  BTS0   flag_Thinkpad_preferred_scrolling
+;  JMP    label_0792
+  NOP
+  NOP
   BCLR   mouse_buttons.2
   BTS0   tp_mouse_buttons.2
   BSET   mouse_buttons.2
@@ -2462,7 +2467,8 @@
   BSET   flag_middle_button_pressed
   JMP    label_07b6
 label_07a0: ; <- 079b
-  BCLR   mouse_buttons.2
+;  BCLR   mouse_buttons.2
+  NOP
   MOV    A, tp_mouse_buttons
   AND    A, #0x04
   CMPRS  A, 0x78
@@ -4701,7 +4707,8 @@
   DW     0x0017 ; ..
   DW     0x0047 ; .G
   DW     0x0060 ; .`
-  DW     0x0030 ; .0
+  ;DW     0x0030 ; .0
+  DW     0x0033 ; increment version
   DW     0x0003 ; ..
   DW     0x0001 ; ..
   DW     0x0002 ; ..

Updater anpassen

Das ist der einfache Teil: XORen mit 0x5A, an die gleiche Stelle tun, und die eingebettete Versionsnummer anpassen. Dafür reicht ein Hex-Editor. Danach kann man den Updater starten, die Tastatur aktualiseren und sich endlich am Scrollen mit der mittleren Maustaste erfreuen.

Sieht aus wie vorher, nur die Version ist anders

Sieht aus wie vorher, nur die Version ist anders

Leider kann ich den gepatchten Updater nicht anbieten, aber dies ist der Patch, der angewandt werden muss:

--- orig.hex	2019-06-10 22:50:40.986155000 +0200
+++ patched.hex	2019-06-10 22:50:53.745130000 +0200
@@ -21893,7 +21893,7 @@
 00057dc0  0d 0a 0d 0a 0d 0a 46 69  72 6d 77 61 72 65 20 75  |......Firmware u|
 00057dd0  70 64 61 74 65 20 63 6f  6d 70 6c 65 74 65 64 0d  |pdate completed.|
 00057de0  0a 43 75 72 72 65 6e 74  20 46 69 72 6d 77 61 72  |.Current Firmwar|
-00057df0  65 20 56 65 72 73 69 6f  6e 3a 20 56 33 2e 33 30  |e Version: V3.30|
+00057df0  65 20 56 65 72 73 69 6f  6e 3a 20 56 33 2e 33 33  |e Version: V3.33|
 00057e00  0d 0a 50 6c 65 61 73 65  20 72 65 70 6c 75 67 20  |..Please replug |
 00057e10  6b 65 79 62 6f 61 72 64  00 00 00 00 50 72 6f 74  |keyboard....Prot|
 00057e20  6f 63 6f 6c 20 48 61 6e  64 73 68 61 6b 65 20 45  |ocol Handshake E|
@@ -24452,8 +24452,8 @@
 00074200  74 9c 5a 54 7b 9c 0f 77  39 9c 01 9c 17 20 8e dc  |t.ZT{..w9.... ..|
 00074210  d3 9c da 5c 88 dc fe 9c  d3 9c 1f 45 fe 9c dd 9d  |...\.......E....|
 00074220  d3 9c 68 45 fe 9c d3 9c  a5 47 5b 4e 69 45 fe 9c  |..hE.....G[NiE..|
-00074230  d3 9c fd 9c 17 19 8f dc  17 11 74 9c 48 0c 81 dc  |..........t.H...|
-00074240  54 92 6b 92 ed 9d 48 04  5a 54 68 44 6e 45 69 44  |T.k...H.ZThDnEiD|
+00074230  d3 9c fd 9c 17 19 8f dc  17 11 74 9c 5a 5a 5a 5a  |..........t.ZZZZ|
+00074240  54 92 6b 92 ed 9d 4e 14  5a 54 68 44 6e 45 69 44  |T.k...N.ZThDnEiD|
 00074250  6f 45 6e 05 bd dc 6e 44  a5 47 5b 4e 6e 45 dc 38  |oEn...nD.G[NnE.8|
 00074260  6e 53 6f 05 b5 dc 6f 44  a5 47 5b 4e 6f 45 6e 44  |nSo...oD.G[NoEnD|
 00074270  6f 78 dc 28 4b dd 68 44  6e 45 69 44 6f 45 6e 05  |ox.(K.hDnEiDoEn.|
@@ -24475,10 +24475,10 @@
 00074370  6c 2d 2e dd 6c 49 2f dd  6c 45 69 44 6c 49 6c 44  |l-..lI/.lEiDlIlD|
 00074380  48 7e dc 20 5a 54 6c 45  a5 77 6a 49 4e 0f d9 dd  |H~. ZTlE.wjIN...|
 00074390  49 10 49 19 df dd 49 18  49 11 2d dd 5a 54 76 1a  |I.I...I.I.-.ZTv.|
-000743a0  1f 0a 76 12 76 1b 1f 0b  76 13 4e 0f c8 dd 76 18  |..v.v...v.N...v.|
+000743a0  1f 0a 76 12 76 1b 1f 0b  76 13 5a 5a 5a 5a 76 18  |..v.v...v.ZZZZv.|
 000743b0  1f 08 76 10 76 44 77 5d  cd dd 49 18 c0 dd 76 44  |..v.vDw]..I...vD|
 000743c0  77 45 49 10 4e 0f fa dd  48 1c 76 08 48 14 ec dd  |wEI.N...H.v.H...|
-000743d0  76 18 1f 44 5e 70 22 5d  fd dd 49 1c ec dd 1f 44  |v..D^p"]..I....D|
+000743d0  5a 5a 1f 44 5e 70 22 5d  fd dd 49 1c ec dd 1f 44  |ZZ.D^p"]..I....D|
 000743e0  5e 70 22 45 49 14 22 00  e9 dd 5a 77 72 45 5e 77  |^p"EI."...ZwrE^w|
 000743f0  73 45 48 14 ec dd 72 71  73 71 48 1c 5a 54 68 44  |sEH...rqsqH.ZThD|
 00074400  dc 2a 94 dd 49 10 74 0d  9c dd 68 44 74 49 68 0d  |.*..I.t...hDtIh.|
@@ -24728,7 +24728,7 @@
 00075340  5c 74 37 75 2d 35 04 d5  0b 74 2f 45 7a 77 cd 75  |\t7u-5...t/Ezw.u|
 00075350  13 d6 1a 77 cd 75 1c da  48 5a 5b 5a 5a 5a 58 5a  |...w.u..HZ[ZZZXZ|
 00075360  5a 5a 5a 5a 5a 5a 52 5a  b5 5a 4d 5a 1d 5a 3a 5a  |ZZZZZZRZ.ZMZ.Z:Z|
-00075370  6a 5a 59 5a 5b 5a 58 5a  5a 5a 5b 5a 53 5a 58 5a  |jZYZ[ZXZZZ[ZSZXZ|
+00075370  69 5a 59 5a 5b 5a 58 5a  5a 5a 5b 5a 53 5a 58 5a  |iZYZ[ZXZZZ[ZSZXZ|
 00075380  61 5a 5a 5a 58 5a 5b 5a  5a 5a fa 5a 68 5a 53 5a  |aZZZXZ[ZZZ.ZhZSZ|
 00075390  5e 5a 5a 5a 5a 5a 5b 5a  59 5a 5b 5a 5b 5a 5a 5a  |^ZZZZZ[ZYZ[Z[ZZZ|
 000753a0  53 5a 7b 5a 5a 5a 5b 5a  5a 5a 5b 5a 78 5a 0b 5a  |SZ{ZZZ[ZZZ[ZxZ.Z|