這個 G300s巨集滑鼠 在下已經使用了一段時間,但要更改其中的巨集功能,就需要安裝官方提供的軟件
然而,官方只提供 Windows 及 Mac OS 版本的更新軟件,因為在下主要使用 Linux,所以無法更新設定
早期在下還接受在 Windows 或 Mac OS 使用官方軟件來更新設定,畢竟在下有很多備用電腦可以使用
但在下對每次都需要使用 Windows 或 Mac OS 來更新設定感到非常不滿
因此,在下決定逆向工程官方的軟件,以了解更新的方法,並嘗試製作一個能在 Linux 上更新 G300s巨集滑鼠 設定的工具
這個專案參考了在下之前對 逆向工程巨集鍵盤修改軟件 的經驗
不過,在下發現修改方法與 巨集鍵盤 略有不同
巨集鍵盤 可以在 終端機(Terminal) 中使用 標準輸出(Standard Output) 將 序列資料(Serial Data) 輸出
並將結果 引導(Redirect) 至序列裝置以更新設定
雖然 G300s巨集滑鼠 都是 USB裝置,但它不是使用 序列通訊 來更新資料,而是使用 USB通訊
因此,無法直接在 終端機 引導資料來傳送,而必須使用 USB通訊 來傳輸資料
由於在不需要將整個軟件逆向工程,只需了解運作原理及更新設定的資料
因此在下同樣使用 Wireshark 擷取資料後進行分析,這與逆向工程 巨集鍵盤 修改軟件的方法相同
外觀
G300s巨集滑鼠 的外觀
(這類面向一般消費者的產品,由於網上有很多基本介紹資料,因此在下不再詳細說明)
更新軟件
官方軟件會自動偵測 G300s巨集滑鼠 是否連接到電腦
官方軟件提供兩種設定方法:
- 內建記憶體,使用滑鼠上儲存的設定檔
- 自動遊戲偵測,使用電腦上儲存的設定檔
由於 自動遊戲偵測 需要依賴官方軟件在背景執行,才能讓 G300s巨集滑鼠 執行電腦中的設定操作,因此需要花更多時間將軟件逆向工程
故此,在下先將 內建記憶體 的運作方式進行逆向工程
相比於 自動遊戲偵測 設定, 內建記憶體 設定沒有太多額外的設定
畢竟是將設定資料保存到 G300s巨集滑鼠 的 內建記憶體,通常不需要保存太多資料
在 內建記憶體 中,可以設定:
- 讀取設定檔
- 更改設定檔
- 更改 LED 燈亮著時的顏色
- 更改更新頻率
- 更改 DPI 值
- 更改 DPI 預設值
- 更改 DPI 轉換
- 更改按鈕功能(滑鼠操作)
- 更改按鈕功能(鍵盤操作)
- 切換設定檔
- 切換 DPI 值
在下主要會嘗試將以上功能逆向工程
逆向工程
同樣使用 Wireshark 逆向工程,由於 G300s巨集滑鼠 是 USB裝置,因此可以讓 Wireshark 監聽 USB訊號 來擷取資料
查詢目標 USB裝置 的 ID,可以在 終端機 輸入:
lsusb
啟動 Wireshark 前,輸入:
sudo modprobe usbmon
來啟用 usbmon,以便讓 Wireshark 監聽 USB訊號
由於監聽 USB訊號 需要 root權限,需要輸入:
sudo wireshark
以 root 或 sudo 身份啟用 Wireshark
然後在 Wireshark 的監聽過濾中輸入:
usb.address_address == ? && usb.src == "host" && usb.data_len > 0
讓 Wireshark 只顯示過濾後的資料,減少因監聽所顯示的資料過多而產生混亂 (? 為指定 USB裝置 的 ID)
由於逆向工程主要需要大量時間、耐心及邏輯推理,並不斷重複測試,比逆向工程巨集鍵盤更花時間
因此在下不詳細展示這些測試的步驟
讀取設定檔
G300s巨集滑鼠 使用 4位元組 USB資料 來讀取設定資料
USB資料 的結構為:
| 偏移 | 位元組長度 | 類型 | 功能 | |
|---|---|---|---|---|
| 十進制 | 十六進制 | |||
| 0 | 0x00 | 1 | 無符號數值 | 用途不明,必定是 0xF0 |
| 1 | 0x01 | 1 | 無符號數值 | 模式,參考 模式 |
| 2 | 0x02 | 1 | 無符號數值 | 用途不明,必定是 0x00 |
| 3 | 0x03 | 1 | 無符號數值 | 用途不明,必定是 0x00 |
模式
| 編號 | 功能 | |
|---|---|---|
| 十進制 | 十六進制 | |
| 128 | 0x80 | 模式1,參考 更改設定檔編號 |
| 144 | 0x90 | 模式2,參考 更改設定檔編號 |
| 160 | 0xA0 | 模式3,參考 更改設定檔編號 |
測試
0xF0, 0x80, 0x00, 0x00
向 G300s巨集滑鼠 發送讀取設定檔的指令會返回 35位元組的資料
而其 USB資料結構 與更新操作的 USB資料結構 相同,參考 更新軟件
更改設定檔
G300s巨集滑鼠 使用 35位元組 USB資料 來更新設定資料
USB資料 的結構為:
| 偏移 | 位元組長度 | 類型 | 功能 | |
|---|---|---|---|---|
| 十進制 | 十六進制 | |||
| 0 | 0x00 | 1 | 無符號數值 | 模式,參考 模式 |
| 1 | 0x01 | 1 | 無符號數值 | LED顏色,參考 LED顏色 |
| 2 | 0x02 | 1 | 無符號數值 | 更新頻率,參考 更新頻率 |
| 3 | 0x03 | 1 | 無符號數值 | DPI值1,參考 DPI值 |
| 4 | 0x04 | 1 | 無符號數值 | DPI值2,參考 DPI值 |
| 5 | 0x05 | 1 | 無符號數值 | DPI值3,參考 DPI值 |
| 6 | 0x06 | 1 | 無符號數值 | DPI值4,參考 DPI值 |
| 7 | 0x07 | 1 | 無符號數值 | DPI轉換,參考 DPI值 |
| 8 | 0x08 | 1 | 無符號數值 | 滑鼠左鍵 改變成 滑鼠操作,參考 滑鼠操作 |
| 9 | 0x09 | 1 | 無符號數值 | 滑鼠左鍵 改變成 鍵盤修飾鍵,參考 修飾鍵 |
| 10 | 0x0A | 1 | 無符號數值 | 滑鼠左鍵 改變成 鍵盤操作,參考 鍵盤操作 |
| 11 | 0x0B | 1 | 無符號數值 | 滑鼠右鍵 改變成 滑鼠操作,參考 滑鼠操作 |
| 12 | 0x0C | 1 | 無符號數值 | 滑鼠右鍵 改變成 鍵盤修飾鍵,參考 修飾鍵 |
| 13 | 0x0D | 1 | 無符號數值 | 滑鼠右鍵 改變成 鍵盤操作,參考 鍵盤操作 |
| 14 | 0x0E | 1 | 無符號數值 | 滑鼠中鍵 改變成 滑鼠操作,參考 滑鼠操作 |
| 15 | 0x0F | 1 | 無符號數值 | 滑鼠中鍵 改變成 鍵盤修飾鍵,參考 修飾鍵 |
| 16 | 0x10 | 1 | 無符號數值 | 滑鼠中鍵 改變成 鍵盤操作,參考 鍵盤操作 |
| 17 | 0x11 | 1 | 無符號數值 | 滑鼠左下鍵 改變成 滑鼠操作,參考 滑鼠操作 |
| 18 | 0x12 | 1 | 無符號數值 | 滑鼠左下鍵 改變成 鍵盤修飾鍵,參考 修飾鍵 |
| 19 | 0x13 | 1 | 無符號數值 | 滑鼠左下鍵 改變成 鍵盤操作,參考 鍵盤操作 |
| 20 | 0x14 | 1 | 無符號數值 | 滑鼠左上鍵 改變成 滑鼠操作,參考 滑鼠操作 |
| 21 | 0x15 | 1 | 無符號數值 | 滑鼠左上鍵 改變成 鍵盤修飾鍵,參考 修飾鍵 |
| 22 | 0x16 | 1 | 無符號數值 | 滑鼠左上鍵 改變成 鍵盤操作,參考 鍵盤操作 |
| 23 | 0x17 | 1 | 無符號數值 | 滑鼠右下鍵 改變成 滑鼠操作,參考 滑鼠操作 |
| 24 | 0x18 | 1 | 無符號數值 | 滑鼠右下鍵 改變成 鍵盤修飾鍵,參考 修飾鍵 |
| 25 | 0x19 | 1 | 無符號數值 | 滑鼠右下鍵 改變成 鍵盤操作,參考 鍵盤操作 |
| 26 | 0x1A | 1 | 無符號數值 | 滑鼠右上鍵 改變成 滑鼠操作,參考 滑鼠操作 |
| 27 | 0x1B | 1 | 無符號數值 | 滑鼠右上鍵 改變成 鍵盤修飾鍵,參考 修飾鍵 |
| 28 | 0x1C | 1 | 無符號數值 | 滑鼠右上鍵 改變成 鍵盤操作,參考 鍵盤操作 |
| 29 | 0x1D | 1 | 無符號數值 | 滑鼠中下鍵 改變成 滑鼠操作,參考 滑鼠操作 |
| 30 | 0x1E | 1 | 無符號數值 | 滑鼠中下鍵 改變成 鍵盤修飾鍵,參考 修飾鍵 |
| 31 | 0x1F | 1 | 無符號數值 | 滑鼠中下鍵 改變成 鍵盤操作,參考 鍵盤操作 |
| 32 | 0x20 | 1 | 無符號數值 | 滑鼠中上鍵 改變成 滑鼠操作,參考 滑鼠操作 |
| 33 | 0x21 | 1 | 無符號數值 | 滑鼠中上鍵 改變成 鍵盤修飾鍵,參考 修飾鍵 |
| 34 | 0x22 | 1 | 無符號數值 | 滑鼠中上鍵 改變成 鍵盤操作,參考 鍵盤操作 |
模式
| 編號 | 功能 | |
|---|---|---|
| 十進制 | 十六進制 | |
| 243 | 0xF3 | 模式1 |
| 244 | 0xF4 | 模式2 |
| 245 | 0xF5 | 模式3 |
LED顏色
| 編號 | 功能 | |
|---|---|---|
| 十進制 | 十六進制 | |
| 0 | 0x00 | 黑色 (#000000) |
| 1 | 0x01 | 紅色 (#FF0000) |
| 2 | 0x02 | 綠色 (#00FF00) |
| 3 | 0x03 | 黃色 (#FFFF00) |
| 4 | 0x04 | 藍色 (#0000FF) |
| 5 | 0x05 | 紫色 (#FF00FF) |
| 6 | 0x06 | 水色 (#00FFFF) |
| 7 | 0x07 | 白色 (#FFFFFF) |
更新頻率
| 編號 | 功能 | |
|---|---|---|
| 十進制 | 十六進制 | |
| 0 | 0x00 | 每秒1000次 |
| 1 | 0x01 | 每秒125次 |
| 2 | 0x02 | 每秒250次 |
| 3 | 0x03 | 每秒500次 |
DPI值
| 編號 | 功能 | |
|---|---|---|
| 十進制 | 十六進制 | |
| x & 128 > 0 | x & 0x80 > 0 | 預設值設定 |
| x & 15 == 1 | x & 0x0F == 0x01 | 設定為 250 DPI |
| x & 15 == 2 | x & 0x0F == 0x02 | 設定為 500 DPI |
| x & 15 == 3 | x & 0x0F == 0x03 | 設定為 750 DPI |
| x & 15 == 4 | x & 0x0F == 0x04 | 設定為 1000 DPI |
| x & 15 == 5 | x & 0x0F == 0x05 | 設定為 1250 DPI |
| x & 15 == 6 | x & 0x0F == 0x06 | 設定為 1500 DPI |
| x & 15 == 7 | x & 0x0F == 0x07 | 設定為 1750 DPI |
| x & 15 == 8 | x & 0x0F == 0x08 | 設定為 2000 DPI |
| x & 15 == 9 | x & 0x0F == 0x09 | 設定為 2250 DPI |
| x & 15 == 10 | x & 0x0F == 0x0A | 設定為 2500 DPI |
滑鼠操作
| 編號 | 功能 | |
|---|---|---|
| 十進制 | 十六進制 | |
| 0 | 0x00 | 沒有操作 |
| 1 | 0x01 | 滑鼠左鍵 |
| 2 | 0x02 | 滑鼠右鍵 |
| 3 | 0x03 | 滑鼠中鍵 |
| 4 | 0x04 | 前進 |
| 5 | 0x05 | 後退 |
| 6 | 0x06 | 保留 |
| 7 | 0x07 | 保留 |
| 8 | 0x08 | 保留 |
| 9 | 0x09 | 保留 |
| 10 | 0x0A | 增加 DPI值 |
| 11 | 0x0B | 減少 DPI值 |
| 12 | 0x0C | 按下按鈕時,臨時切換到 預設DPI值;不按按鈕時,切換回 原本DPI值 |
| 13 | 0x0D | 循環切換 DPI值 |
| 14 | 0x0E | 切換到 預設DPI值 |
| 15 | 0x0F | 循環切換 模式 |
修飾鍵
| 編號 | 功能 | |
|---|---|---|
| 十進制 | 十六進制 | |
| 1 | 0x01 | 左Ctrl鍵 / 左Control鍵 |
| 2 | 0x02 | 左Shift鍵 |
| 4 | 0x04 | 左Alt鍵 / 左Option鍵 |
| 8 | 0x08 | 左Meta鍵 / 左Command鍵 |
| 16 | 0x10 | 右Ctrl鍵 / 右Control鍵 |
| 32 | 0x20 | 右Shift鍵 |
| 64 | 0x40 | 右Alt鍵 / 右Option鍵 |
| 128 | 0x80 | 右Meta鍵 / 右Command鍵 |
0b11000011 ^^^^^^^^ |||||||+- 左Ctrl鍵 / 左Control鍵 ||||||+-- 左Shift鍵 |||||+--- 左Alt鍵 / 左Option鍵 ||||+---- 左Meta鍵 / 左Command鍵 |||+----- 右Ctrl鍵 / 右Control鍵 ||+------ 右Shift鍵 |+------- 右Alt鍵 / 右Option鍵 +-------- 右Meta鍵 / 右Command鍵
修飾鍵能夠同時執行,根據例子 0b11000011 = 0xC3 = 195 表示同時按下:
- 左Ctrl鍵 / 左Control鍵
- 左Shift鍵
- 右Alt鍵 / 右Option鍵
- 右Meta鍵 / 右Command鍵
鍵盤操作
| 編號 | 功能 | |
|---|---|---|
| 十進制 | 十六進制 | |
| 0 | 0x00 | 無效 |
| 1 | 0x01 | 保留 |
| 2 | 0x02 | 保留 |
| 3 | 0x03 | 保留 |
| 4 | 0x04 | 鍵盤小寫 a |
| 5 | 0x05 | 鍵盤小寫 b |
| 6 | 0x06 | 鍵盤小寫 c |
| 7 | 0x07 | 鍵盤小寫 d |
| 8 | 0x08 | 鍵盤小寫 e |
| 9 | 0x09 | 鍵盤小寫 f |
| 10 | 0x0A | 鍵盤小寫 g |
| 11 | 0x0B | 鍵盤小寫 h |
| 12 | 0x0C | 鍵盤小寫 i |
| 13 | 0x0D | 鍵盤小寫 j |
| 14 | 0x0E | 鍵盤小寫 k |
| 15 | 0x0F | 鍵盤小寫 l |
| 16 | 0x10 | 鍵盤小寫 m |
| 17 | 0x11 | 鍵盤小寫 n |
| 18 | 0x12 | 鍵盤小寫 o |
| 19 | 0x13 | 鍵盤小寫 p |
| 20 | 0x14 | 鍵盤小寫 q |
| 21 | 0x15 | 鍵盤小寫 r |
| 22 | 0x16 | 鍵盤小寫 s |
| 23 | 0x17 | 鍵盤小寫 t |
| 24 | 0x18 | 鍵盤小寫 u |
| 25 | 0x19 | 鍵盤小寫 v |
| 26 | 0x1A | 鍵盤小寫 w |
| 27 | 0x1B | 鍵盤小寫 x |
| 28 | 0x1C | 鍵盤小寫 y |
| 29 | 0x1D | 鍵盤小寫 z |
| 30 | 0x1E | 鍵盤數字 1 |
| 31 | 0x1F | 鍵盤數字 2 |
| 32 | 0x20 | 鍵盤數字 3 |
| 33 | 0x21 | 鍵盤數字 4 |
| 34 | 0x22 | 鍵盤數字 5 |
| 35 | 0x23 | 鍵盤數字 6 |
| 36 | 0x24 | 鍵盤數字 7 |
| 37 | 0x25 | 鍵盤數字 8 |
| 38 | 0x26 | 鍵盤數字 9 |
| 39 | 0x27 | 鍵盤數字 0 |
| 40 | 0x28 | 鍵盤 Enter |
| 41 | 0x29 | 鍵盤 Escape |
| 42 | 0x2A | 鍵盤 Backspace |
| 43 | 0x2B | 鍵盤 Tab |
| 44 | 0x2C | 鍵盤 Space |
| 45 | 0x2D | 鍵盤 - |
| 46 | 0x2E | 鍵盤 = |
| 47 | 0x2F | 鍵盤 [ |
| 48 | 0x30 | 鍵盤 ] |
| 49 | 0x31 | 鍵盤 \ |
| 50 | 0x32 | 鍵盤 \ (非US) |
| 51 | 0x33 | 鍵盤 ; |
| 52 | 0x34 | 鍵盤 ' |
| 53 | 0x35 | 鍵盤 ` |
| 54 | 0x36 | 鍵盤 , |
| 55 | 0x37 | 鍵盤 . |
| 56 | 0x38 | 鍵盤 / |
| 57 | 0x39 | 鍵盤 Caps Lock |
| 58 | 0x3A | 鍵盤 F1 |
| 59 | 0x3B | 鍵盤 F2 |
| 60 | 0x3C | 鍵盤 F3 |
| 61 | 0x3D | 鍵盤 F4 |
| 62 | 0x3E | 鍵盤 F5 |
| 63 | 0x3F | 鍵盤 F6 |
| 64 | 0x40 | 鍵盤 F7 |
| 65 | 0x41 | 鍵盤 F8 |
| 66 | 0x42 | 鍵盤 F9 |
| 67 | 0x43 | 鍵盤 F10 |
| 68 | 0x44 | 鍵盤 F11 |
| 69 | 0x45 | 鍵盤 F12 |
| 70 | 0x46 | 鍵盤 Print Screen |
| 71 | 0x47 | 鍵盤 Scroll Lock |
| 72 | 0x48 | 鍵盤 Pause |
| 73 | 0x49 | 鍵盤 Insert |
| 74 | 0x4A | 鍵盤 Home |
| 75 | 0x4B | 鍵盤 Page Up |
| 76 | 0x4C | 鍵盤 Delete |
| 77 | 0x4D | 鍵盤 End |
| 78 | 0x4E | 鍵盤 Page Down |
| 79 | 0x4F | 鍵盤 Right Arrow |
| 80 | 0x50 | 鍵盤 Left Arrow |
| 81 | 0x51 | 鍵盤 Down Arrow |
| 82 | 0x52 | 鍵盤 Up Arrow |
| 83 | 0x53 | 鍵盤 Num Lock |
| 84 | 0x54 | 數字鍵盤 / |
| 85 | 0x55 | 數字鍵盤 * |
| 86 | 0x56 | 數字鍵盤 - |
| 87 | 0x57 | 數字鍵盤 + |
| 88 | 0x58 | 數字鍵盤 Enter |
| 89 | 0x59 | 數字鍵盤 1 |
| 90 | 0x5A | 數字鍵盤 2 |
| 91 | 0x5B | 數字鍵盤 3 |
| 92 | 0x5C | 數字鍵盤 4 |
| 93 | 0x5D | 數字鍵盤 5 |
| 94 | 0x5E | 數字鍵盤 6 |
| 95 | 0x5F | 數字鍵盤 7 |
| 96 | 0x60 | 數字鍵盤 8 |
| 97 | 0x61 | 數字鍵盤 9 |
| 98 | 0x62 | 數字鍵盤 0 |
| 99 | 0x63 | 數字鍵盤 . |
| 100 | 0x64 | 數字鍵盤 < |
| 101 | 0x65 | 鍵盤 Application |
| 102 | 0x66 | 系統 關機 |
| 103 | 0x67 | 數字鍵盤 = |
| 104 | 0x68 | F13 |
| 105 | 0x69 | F14 |
| 106 | 0x6A | F15 |
| 107 | 0x6B | F16 |
| 108 | 0x6C | F17 |
| 109 | 0x6D | F18 |
| 110 | 0x6E | F19 |
| 111 | 0x6F | F20 |
| 112 | 0x70 | F21 |
| 113 | 0x71 | F22 |
| 114 | 0x72 | F23 |
| 115 | 0x73 | F24 |
| 116 | 0x74 | Open |
| 117 | 0x75 | Help |
| 118 | 0x76 | Props |
| 119 | 0x77 | Select |
| 120 | 0x78 | 瀏覽器 Stop |
| 121 | 0x79 | Redo |
| 122 | 0x7A | Undo |
| 123 | 0x7B | Cut |
| 124 | 0x7C | Copy |
| 125 | 0x7D | Paste |
| 126 | 0x7E | Find |
| 127 | 0x7F | 系統 靜音 |
| 128 | 0x80 | 系統 音量增加 |
| 129 | 0x81 | 系統 音量減少 |
| 130 | 0x82 | 保留 |
| 131 | 0x83 | 保留 |
| 132 | 0x84 | 保留 |
| 133 | 0x85 | 數字鍵盤 , |
| 134 | 0x86 | 保留 |
| 135 | 0x87 | International 1 |
| 136 | 0x88 | International 2 |
| 137 | 0x89 | International 3 |
| 138 | 0x8A | Convert |
| 139 | 0x8B | Non Convert |
| 140 | 0x8C | 保留 |
| 141 | 0x8D | 保留 |
| 142 | 0x8E | 保留 |
| 143 | 0x8F | 保留 |
| 144 | 0x90 | Lang 1 |
| 145 | 0x91 | Lang 2 |
| 146 | 0x92 | 保留 |
| 147 | 0x93 | 保留 |
| 148 | 0x94 | 保留 |
| 149 | 0x95 | 保留 |
| 150 | 0x96 | 保留 |
| 151 | 0x97 | 保留 |
| 152 | 0x98 | 保留 |
| 153 | 0x99 | 保留 |
| 154 | 0x9A | 保留 |
| 155 | 0x9B | 保留 |
| 156 | 0x9C | Delete |
| 157 | 0x9D | 保留 |
| 158 | 0x9E | 保留 |
| 159 | 0x9F | 保留 |
| 160 | 0xA0 | 保留 |
| 161 | 0xA1 | 保留 |
| 162 | 0xA2 | 保留 |
| 163 | 0xA3 | 保留 |
| 164 | 0xA4 | 保留 |
| 165 | 0xA5 | 保留 |
| 166 | 0xA6 | 保留 |
| 167 | 0xA7 | 保留 |
| 168 | 0xA8 | 保留 |
| 169 | 0xA9 | 保留 |
| 170 | 0xAA | 保留 |
| 171 | 0xAB | 保留 |
| 172 | 0xAC | 保留 |
| 173 | 0xAD | 保留 |
| 174 | 0xAE | 保留 |
| 175 | 0xAF | 保留 |
| 176 | 0xB0 | 保留 |
| 177 | 0xB1 | 保留 |
| 178 | 0xB2 | 保留 |
| 179 | 0xB3 | 保留 |
| 180 | 0xB4 | 保留 |
| 181 | 0xB5 | 保留 |
| 182 | 0xB6 | 保留 |
| 183 | 0xB7 | 保留 |
| 184 | 0xB8 | 保留 |
| 185 | 0xB9 | 保留 |
| 186 | 0xBA | 保留 |
| 187 | 0xBB | 保留 |
| 188 | 0xBC | 保留 |
| 189 | 0xBD | 保留 |
| 190 | 0xBE | 保留 |
| 191 | 0xBF | 保留 |
| 192 | 0xC0 | 保留 |
| 193 | 0xC1 | 保留 |
| 194 | 0xC2 | 保留 |
| 195 | 0xC3 | 保留 |
| 196 | 0xC4 | 保留 |
| 197 | 0xC5 | 保留 |
| 198 | 0xC6 | 保留 |
| 199 | 0xC7 | 保留 |
| 200 | 0xC8 | 保留 |
| 201 | 0xC9 | 保留 |
| 202 | 0xCA | 保留 |
| 203 | 0xCB | 保留 |
| 204 | 0xCC | 保留 |
| 205 | 0xCD | 保留 |
| 206 | 0xCE | 保留 |
| 207 | 0xCF | 保留 |
| 208 | 0xD0 | 保留 |
| 209 | 0xD1 | 保留 |
| 210 | 0xD2 | 保留 |
| 211 | 0xD3 | 保留 |
| 212 | 0xD4 | 保留 |
| 213 | 0xD5 | 保留 |
| 214 | 0xD6 | 保留 |
| 215 | 0xD7 | 保留 |
| 216 | 0xD8 | 保留 |
| 217 | 0xD9 | 保留 |
| 218 | 0xDA | 保留 |
| 219 | 0xDB | 保留 |
| 220 | 0xDC | 保留 |
| 221 | 0xDD | 保留 |
| 222 | 0xDE | 保留 |
| 223 | 0xDF | 保留 |
| 224 | 0xE0 | 保留 |
| 225 | 0xE1 | 保留 |
| 226 | 0xE2 | 保留 |
| 227 | 0xE3 | 保留 |
| 228 | 0xE4 | 保留 |
| 229 | 0xE5 | 保留 |
| 230 | 0xE6 | 保留 |
| 231 | 0xE7 | 保留 |
| 232 | 0xE8 | 保留 |
| 233 | 0xE9 | 保留 |
| 234 | 0xEA | 保留 |
| 235 | 0xEB | 保留 |
| 236 | 0xEC | 保留 |
| 237 | 0xED | 保留 |
| 238 | 0xEE | 保留 |
| 239 | 0xEF | 保留 |
| 240 | 0xF0 | 保留 |
| 241 | 0xF1 | 保留 |
| 242 | 0xF2 | 保留 |
| 243 | 0xF3 | 保留 |
| 244 | 0xF4 | 保留 |
| 245 | 0xF5 | 保留 |
| 246 | 0xF6 | 保留 |
| 247 | 0xF7 | 保留 |
| 248 | 0xF8 | 保留 |
| 249 | 0xF9 | 保留 |
| 250 | 0xFA | 保留 |
| 251 | 0xFB | 保留 |
| 252 | 0xFC | 保留 |
| 253 | 0xFD | 保留 |
| 254 | 0xFE | 保留 |
| 255 | 0xFF | 保留 |
測試指令
0xF3,\ 0x01,\ 0x00,\ 0x04, 0x86, 0x08, 0x0A, 0x06,\ 0x01, 0x00, 0x00,\ 0x02, 0x00, 0x00,\ 0x03, 0x00, 0x00,\ 0x04, 0x00, 0x00,\ 0x05, 0x00, 0x00,\ 0x00, 0x01, 0x06,\ 0x00, 0x01, 0x19,\ 0x0C, 0x00, 0x00,\ 0x00, 0x01, 0x1B
測試指令內容為:
- 會更新 0xF3 (模式1)
- LED顏色編號 為 0x01 (紅色)
- 更新頻率編號 為 0x00 (每秒1000次)
- DPI值編號1 為 0x04 (1000 DPI)
- DPI值編號2 為 0x86 (1500 DPI) (預設)
- DPI值編號3 為 0x08 (2000 DPI)
- DPI值編號4 為 0x0A (2500 DPI)
- DPI值編號 為 0x06 (1500 DPI)
- 滑鼠左鍵(G1) 為 0x01, 0x00, 0x00 (滑鼠左鍵)
- 滑鼠右鍵(G2) 為 0x02, 0x00, 0x00 (滑鼠右鍵)
- 滑鼠中鍵(G3) 為 0x03, 0x00, 0x00 (滑鼠中鍵)
- 滑鼠左下鍵(G4) 為 0x04, 0x00, 0x00 (後退)
- 滑鼠左上鍵(G5) 為 0x05, 0x00, 0x00 (前進)
- 滑鼠右下鍵(G6) 為 0x00, 0x01, 0x06 (左Ctrl + C)
- 滑鼠右上鍵(G7) 為 0x00, 0x01, 0x19 (左Ctrl + V)
- 滑鼠中下鍵(G8) 為 0x0D, 0x00, 0x00 (循環切換 DPI值)
- 滑鼠中上鍵(G9) 為 0x00, 0x01, 0x1B (左Ctrl + X)
正確更新 G300s巨集滑鼠 的設定資料,會傳回數值 35 (即是位元組長度的數值)
切換設定檔
G300s巨集滑鼠 使用 4位元組 USB資料 來讀取設定資料
USB資料 的結構為:
| 偏移 | 位元組長度 | 類型 | 功能 | |
|---|---|---|---|---|
| 十進制 | 十六進制 | |||
| 0 | 0x00 | 1 | 無符號數值 | 用途不明,必定是 0xF0 |
| 1 | 0x01 | 1 | 無符號數值 | 模式,參考 讀取設定檔模式 |
| 2 | 0x02 | 1 | 無符號數值 | 用途不明,必定是 0x00 |
| 3 | 0x03 | 1 | 無符號數值 | 用途不明,必定是 0x00 |
正確切換 G300s巨集滑鼠 的 設定檔,會傳回數值 4 (即是位元組長度的數值)
切換DPI
| 編號 | 功能 | |
|---|---|---|
| 十進制 | 十六進制 | |
| 64 | 0x40 | DPI值編號1 |
| 66 | 0x42 | DPI值編號2 |
| 68 | 0x44 | DPI值編號3 |
| 70 | 0x46 | DPI值編號4 |
正確切換 G300s巨集滑鼠 的 DPI值,會傳回數值 4 (即是位元組長度的數值)
USB通訊
USB通訊 並不能像一般 序列裝置 能夠直接將資料發送至 USB裝置,但在下這裡不會詳細說明 USB通訊 的 規格及方法
除了 資料片段(Data Fragment) 之外,還需要符合 bmRequest 、 wValue 、 wIndex 及 wLength 這些 USB通訊設定
才能正確地將指令發送給 G300s巨集滑鼠
Python 及 pyusb
雖然已經完成資料的逆向工程和分析,但還需要編寫程式來執行指令,以更新 G300s巨集滑鼠 的設定
在下使用 Python 配合 pyusb 來處理 USB裝置 相關的操作。安裝 pyusb 可以在 終端機 中輸入:
pip install pyusb
比較新版本的 Linux發行版 會將 Python套件 加入到系統套件中,例如在下正在使用的 Linux Mint 22.2 ,需要輸入:
sudo apt install python3-usb
或點擊安裝 python3-usb
由於 pyusb 已經完成了大部分 USB通訊 的初始操作,因此在下可以專注於編寫關於 USB通訊設定及資料的內容,以存取 G300s巨集滑鼠
import usb.core, usb.util
# Finding the USB device filtered with its Vendor ID and Product ID
device = usb.core.find(idVendor = device_vendor_id, idProduct = device_product_id)
if device == None:
print("Device not found")
else:
# Temporarily disconnect the USB device from the kernel driver
device.detach_kernel_driver(interface_index)
# Using the target interface
usb.util.claim_interface(device, interface_index)
# Transfer data to the device
result = device.ctrl_transfer(
# Defining the type, recipient, direction of the request
bmRequestType = usb.util.CTRL_TYPE_CLASS | usb.util.CTRL_RECIPIENT_INTERFACE | usb.util.CTRL_IN,
# Defining the request code
bRequest = 0x01,
# Defining the request parameter
wValue = mode | 0x0300,
# Defining the interface or endpoint
wIndex = 0x01,
# Sending array of bytes to device or retrieving number of dates from device
data_or_wLength = 256,
# Timeout after the request without response
timeout = 1000,
)
# Release the target interface
usb.util.release_interface(device, interface_index)
# Re-disconnect the USB device to the kernel driver
device.attach_kernel_driver(interface_index)
# Clearing the device handle
usb.util.dispose_resources(device)
# Showing the reesult of the request
print(result)
在編寫了一個簡單的 Python腳本 來向 USB裝置 發送指令
需要注意的是,向 USB裝置 傳送資料同樣需要 root 權限,因此執行 Python腳本 時需要以 root 或 sudo 身份運行
# g300s-get-mode-data.py
# get data from mode 1
# python g300s-get-mode-data.py "243"
import sys, usb.core, usb.util
device = usb.core.find(idVendor = 0x046D, idProduct = 0xC246)
if device == None:
print("No G300s")
else:
mode = int(sys.argv[1])
interface_index = 1
device.detach_kernel_driver(interface_index)
usb.util.claim_interface(device, interface_index)
result = device.ctrl_transfer(
bmRequestType = usb.util.CTRL_TYPE_CLASS | usb.util.CTRL_RECIPIENT_INTERFACE | usb.util.CTRL_IN,
bRequest = 0x01,
wValue = mode | 0x0300,
wIndex = 0x01,
data_or_wLength = 256,
timeout = 1000,
)
usb.util.release_interface(device, interface_index)
device.attach_kernel_driver(interface_index)
usb.util.dispose_resources(device)
# returns 35-byte array
print(result)
# g300s-change-mode.py
# change to mode 1
# python g300s-change-mode.py "128"
import sys, usb.core, usb.util
device = usb.core.find(idVendor = 0x046D, idProduct = 0xC246)
if device == None:
print("No G300s")
else:
mode = int(sys.argv[1])
interface_index = 1
usb.util.claim_interface(device, interface_index)
result = device.ctrl_transfer(
bmRequestType = usb.util.CTRL_TYPE_CLASS | usb.util.CTRL_RECIPIENT_INTERFACE | usb.util.CTRL_OUT,
bRequest = 0x09,
wValue = 0x00F0 | 0x0300,
wIndex = 0x01,
data_or_wLength = [0xF0, mode, 0x00, 0x00],
timeout = 1000,
)
usb.util.release_interface(device, interface_index)
usb.util.dispose_resources(device)
# returns length of byte array, always 4
print(result)
# g300-set-mode-data.py
# set mode 1 to
# LED = red
# Rate = 1000
# DPI 1 = 1000
# DPI 2 = 1500 (default)
# DPI 3 = 2000
# DPI 4 = 2500
# DPI Shift = 1500
# Button G1 = Mouse Left
# Button G2 = Mouse Right
# Button G3 = Mouse Middle
# Button G4 = History Backward
# Button G5 = History Forward
# Button G6 = Ctrl C
# Button G7 = Ctrl V
# Button G8 = Mode Switch
# Button G9 = Ctrl X
# python g300-set-mode-data.py "243,1,0,4,134,8,10,6,1,0,0,2,0,0,3,0,0,4,0,0,5,0,0,0,1,6,0,1,25,13,0,0,0,1,27"
import sys, usb.core, usb.util
device = usb.core.find(idVendor = 0x046D, idProduct = 0xC246)
if device == None:
print("No G300s")
else:
payload = []
for arg in sys.argv[1].split(","):
payload.append(int(arg))
interface_index = 1
usb.util.claim_interface(device, interface_index)
result = device.ctrl_transfer(
bmRequestType = usb.util.CTRL_TYPE_CLASS | usb.util.CTRL_RECIPIENT_INTERFACE | usb.util.CTRL_OUT,
bRequest = 0x09,
wValue = payload[0] | 0x0300,
wIndex = 0x01,
data_or_wLength = payload,
timeout = 1000,
)
usb.util.release_interface(device, interface_index)
usb.util.dispose_resources(device)
# returns length of byte array, always 35
print(result)
在下編寫了 3個 Python腳本 來存取 G300s巨集滑鼠 來讀取設定檔、切換設定檔、更新設定檔
測試
測試 切換設定檔
測試更新 LED顏色
雖然使用指令操作很方便,但對於不擅長使用指令的使用者來說並不太方便
因此,在下製作了一個可以經網頁操作的版本,以方便一般使用者使用
補充資料
讀取當前設定檔
發佈這篇文章再次檢視使用網頁更新 G300s巨集滑鼠 的影片,才發現時 G300s巨集滑鼠的LED顏色 與 網頁顯示的LED顏色 不同
在下忘記將將 讀取當前設定檔 的操作逆向工程,因此只好在此補充
# g300s-get-current-mode.py
# python g300s-get-current-mode.py
import sys, usb.core, usb.util
device = usb.core.find(idVendor = 0x046D, idProduct = 0xC246)
if device == None:
print("No G300s")
else:
interface_index = 1
device.detach_kernel_driver(interface_index)
usb.util.claim_interface(device, interface_index)
result = device.ctrl_transfer(
bmRequestType = usb.util.CTRL_TYPE_CLASS | usb.util.CTRL_RECIPIENT_INTERFACE | usb.util.CTRL_IN,
bRequest = 0x01,
wValue = 0x00F0 | 0x0300,
wIndex = 0x01,
data_or_wLength = 256,
timeout = 1000,
)
usb.util.release_interface(device, interface_index)
device.attach_kernel_driver(interface_index)
usb.util.dispose_resources(device)
# returns 4-byte array
print(result)
正確讀取 G300s巨集滑鼠 當前設定檔,會傳回數值 4位元組資料
0xF0, 0x??, 0x00, 0x00
?? 為 當前設定檔的模式
| 編號 | 功能 | |
|---|---|---|
| 十進制 | 十六進制 | |
| 2 | 0x02 | 模式1 |
| 18 | 0x12 | 模式2 |
| 34 | 0x22 | 模式3 |
範型程式
# usb-communication.py
# usb-communication.py vendor-id, product-id, interface-index, bm-request-type, b-request, w-value, windex
# usb-communication.py vendor-id, product-id, interface-index, bm-request-type, b-request, w-value, windex, comma-separated-bytes
# python usb-commumication.py "0x046D" "0xC246" "1" "0xA1" "0x01" "0x03F0" "0x01"
import sys, usb.core, usb.util
def value_string_to_value(value_string):
value = value_string.__str__().strip()
if value.startswith("0x"):
try:
return int(value, 16)
except ValueError:
return 0
elif value.startswith("0b"):
try:
return int(value, 2)
except ValueError:
return 0
else:
try:
return int(value)
except ValueError:
return 0
try:
vendor_id = value_string_to_value(sys.argv[1])
product_id = value_string_to_value(sys.argv[2])
interface_index = value_string_to_value(sys.argv[3])
bmRequestType = value_string_to_value(sys.argv[4])
bRequest = value_string_to_value(sys.argv[5])
wValue = value_string_to_value(sys.argv[6])
wIndex = value_string_to_value(sys.argv[7])
data_or_wLength = 256
if 8 in sys.argv:
data_or_wLength = sys.argv[8].__str__().strip()
if len(data_or_wLength) > 0:
payload = []
for value_string in data_or_wLength.split(","):
payload.append(value_string_to_value(value_string))
data_or_wLength = payload
device = usb.core.find(idVendor = vendor_id, idProduct = product_id)
device.detach_kernel_driver(interface_index)
usb.util.claim_interface(device, interface_index)
data = device.ctrl_transfer(
bmRequestType = bmRequestType,
bRequest = bRequest,
wValue = wValue,
wIndex = wIndex,
data_or_wLength = data_or_wLength,
timeout = 1000,
)
usb.util.release_interface(device, interface_index)
device.attach_kernel_driver(interface_index)
usb.util.dispose_resources(device)
print(data)
except Exception as exception:
print(exception)
另外,由於在下太懶惰,不想編寫多個不同功能的操作
因此編寫了一個範型程式,能夠從參數設定 Vendor ID、Product ID、Interface Index 等資料
總結
由於在下對只有 Windows 和 Mac OS 的更新軟件不滿,因此進行了逆向工程並自行編寫工具
逆向工程後,除了製作指令更新的工具,在下還製作能夠通過網頁更新的版本
在下認為使用網頁更新的方法更符合現代操作方式,像 Python 這類跨平台的編程語言
可以簡單地製作以網頁方式操作的工具,既不受作業系統限制,又能讓其他裝置協助操作
此外,通過 HTTP 請求提交資料,也可以借助 CURL 等指令工具將操作自動化或半自動化,方便管理
尤其這類小型工具不需要佔用太多系統資源,因此根本不應該有這些平台限制的設計
就像許多 Linux 上的 系統管理器 、 路由器(Router) 、 網絡鏡頭(Network Camera) 等工具,早已可以在網頁控制,並不受平台限制
雖然在下有多次逆向工程的經驗,但將原本的工具逆向工程仍需要花費大量時間才能完成
因此,在下更加佩服開源社群的人願意花費這麼多時間,逆向工程更複雜的工具,最後免費讓大家使用
另外,網上也有其他人製作出能在 Linux 上更新 G300s巨集滑鼠 的工具 RatSlap
RatSlap 使用 C 配合 libusb 來編寫,在下最初參考了 RatSlap 同樣使用 C 和 libusb 來編寫比 RatSlap 更簡單的操作方法
不過,在下覺得使用指令操作對一般使用者不方便,因此嘗試使用其他編程語言來實現相同效果
在了解運作原理後,在下發現 Python 配合 pyusb 可以達到相同效果,而且 Python 可以很容易地自建網頁伺服器
因此,在下決定使用 Python 編寫,並通過網頁介面來更新 G300s巨集滑鼠
由於使用網頁更新的原始碼太長,因此在下將原始碼上載到 https://bitbucket.org/hkgoldenmra/linux-g300s-updater
有興趣可以下載、改良、分享
參考資料
文章內容經由 AI 協助訂正









沒有留言 :
張貼留言