For years, I owned and liked my USB keyboard, A “Lenovo ThinkPad Compact USB Keyboard with TrackPoint” (KU-1255). It is small and with its trackpoint, you don’t even need to move your hands from the keyboard to move the mouse pointer. Unfortunately, Lenovo went far beyond what’s useful: They implemented middle-mouse button scrolling in hardware, but half-broken.
An annoying bug
The keyboard firmware supports different modes, on of them disables or activates the middle mouse button. If it is activated, you can use the middle mouse button. As a bonus, you get a kind-of middle-mouse scrolling: As long as you press the button, vertical trackpoint movements are translated to scroll events. But just vertical and in a really coarse way, so that you can only scroll line-wise, but not close to pixel-wise.
In addition, releasing the middle mouse button after scrolling triggers a middle mouse click event, ruining the scroll feature totally.
Linux works around this, for this particular keyboard, but on Windows or FreeBSD, you’re lost.
Lenovo didn’t recognise the problem, but sent me a new keyboard instead, assuming it was a hardware issue.
Reverse-engineering the firmware
Let’s start! First, we take the keyboard apart and look what’s inside: A Sonix SN8F2288FG. It’s an 8-bit microcontroller with 12k words ROM, 512 B RAM and a weird architecture.
On Lenovo’s website, you can download the updater for the keyboard. It contains
the firmware binary. Download it first:
tp_compact_usb_kb_with_trackpoint_fw.exe
(MD5
2c128084412b917449a2c01da3c49451
, SHA256
7116a3819ee094857d21e4671cb6cf953d582372126f0f6728f6b2421eda7bd4
), then put it
into the free version of IDA Pro.
After playing around a bit, I found the firmware. It is XORed with 0x5A.
Search for Auto .....a..2015051111591234567817EF60478521SN8F2288
to find it.
Then, put that firmware binary into the free SN8 disassembler dissn8
.
When I started, I had to add a few instructions and patch some others to decode
the binary correctly. To verify, I cross-checked by assembling and disassembling
with SN8 C Studio.
Then, it was time to print the assembly on paper, sit down and understand it.
After a while, I found the place that implements the “feature” I disliked and patched it out. I also bumped the firmware version to be discern my modified version from the original:
--- 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 ; ..
Patch the updater
That was the easy part: XOR it again with 0x5A and put it into the same place. in the binary. A hex editor suffices.
Use the patched updater and enjoy middle-button scrolling work as expected.
Unfortunately, I feel I can’t published the patched updater. But this is the patch you need to apply to it:
--- 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|