逆向羅技固件升級程序?qū)崿F(xiàn)平刷和降級

0xFF 免責聲明

對設備的刷機操作有風險西雀,使用本文提到任何工具產(chǎn)生的問題艇肴,還請自行承擔豆挽!

0x00 起因

在某天羅技驅(qū)動提示升級后,毫不猶豫選擇了升級膛檀。不巧幾天后咖刃,發(fā)現(xiàn)k780鍵盤的某些組合鍵無法工作憾筏,連Ctrl+C都無法使用氧腰,這讓只會復制黏貼的高級攻城獅情何以堪古拴。接著很容易把鍋甩給羅技的固件升級,想要重刷一下固件紧帕,在羅技官網(wǎng)一頓操作后是嗜,發(fā)現(xiàn)了一個叫做Firmware Update Tool的程序,結(jié)果這工具僅能做升級操作站绪,同版本的固件都無法重新寫入崇众,更不要說用舊版本進行降級。無奈之下端起逆向大旗锰蓬,廢話不多說芹扭。下面以目前次新版本FirmwareUpdateTool_1.2.169_x86為例舱卡。

0x01 尋找界面突破口

我們先在界面上尋找一些特征,首先連接好優(yōu)聯(lián)(unifying)接收器矫钓,然后鍵盤通過優(yōu)聯(lián)與電腦連接新娜。


歡迎界面
尋找設備

如果鍵盤未連接概龄,隨便按個鍵喚醒

喚醒鍵盤

提示優(yōu)聯(lián)接收器可升級私杜,會進入一個叫做YOUR RECEIVER IS READY TO UPDATE的確認窗口衰粹,這里點擊update就會進行升級寄猩,當然如果你的固件版本大于或者等于該升級程序的版本骑疆,會直接彈出沒有設備需要升級的提示。

優(yōu)聯(lián)設備

這里需要注意的是椎镣,優(yōu)聯(lián)接收器和K780鍵盤都是有單獨的固件的状答,升級是分開的惊科。

0x02 逆向分析

FirmwareUpdateTool.exe文件(官網(wǎng)下載的文件為rar自解壓包亮钦,要先進行解壓)拉入IDA進行靜態(tài)分析蜂莉,在函數(shù)窗口能找到一些Q開頭的函數(shù)映穗,很明顯是Qt框架寫的界面程序,從文件中也能發(fā)現(xiàn)Qt的類庫Qt5Core.dll等文件宿接。

函數(shù)列表

嘗試搜索界面上的字符串澄阳,發(fā)現(xiàn)并不能找到完全符合的碎赢,翻一下能看到:/translations/en.qm肮塞,基本可以確定用了Qt框架的多國語言模塊姻锁,還有welcome-header位隶、welcome-text之類就是字符串索引key,雙擊welcome-header查看赋荆,sub_40CD40+115引用了懊昨,直接進入sub_40CD40+115酵颁。

出現(xiàn)的字符串.png

0040CE5B處的call獲取真正的字符串躏惋,接下來的通過QLabel::setText設置label的文本其掂。

image.png

在sub_40CD40函數(shù)是一個稍微復雜的switch case的代碼款熬,按F5生成偽代碼攘乒,可以看到根據(jù)a2的值進行了不同操作则酝,sub_40CD40函數(shù)主要功能是對界面進行操作沽讹。

int __thiscall sub_40CD40(QWidget **this, int a2)
{
  ...

  switch ( a2 )
  {
    case 1:
      v6 = (const struct QString *)sub_40CD10(&v261, "welcome-header", 0, -1);
      v7 = v2[8];
      LOBYTE(v293) = 1;
      QLabel::setText(v7, v6);
      LOBYTE(v293) = 0;
      QString::~QString((QString *)&v261);
      v8 = (const struct QString *)sub_40CD10(&v233, "welcome-text", 0, -1);
      v9 = v2[9];
      LOBYTE(v293) = 2;
      QLabel::setText(v9, v8);
      ...
    case 2:
      v15 = (const struct QString *)sub_40CD10(&v217, "detecting-devices-header", 0, -1);
      v16 = v2[8];
      LOBYTE(v293) = 8;
      QLabel::setText(v16, v15);
      LOBYTE(v293) = 0;
      QString::~QString((QString *)&v217);
      v17 = (const struct QString *)sub_40CD10(&v219, "detecting-devices-text", 0, -1);
      v18 = v2[9];
      ...
    case 3:
      v21 = (const struct QString *)sub_40CD10(&v198, "unplug-receivers-header", 0, -1);
      v22 = v2[8];
      LOBYTE(v293) = 11;
      QLabel::setText(v22, v21);
      LOBYTE(v293) = 0;
      QString::~QString((QString *)&v198);
      v23 = sub_40CD10(&v259, "unplug-receivers-text", 0, -1);
      LOBYTE(v291) = 32;
      LOBYTE(v293) = 12;
      QChar::QChar(&v169, v291);
      ...
    
    case 6:
      v44 = (const struct QString *)sub_40CD10(&v213, "devices-up-to-date-header", 0, -1);
      v45 = v2[8];
      LOBYTE(v293) = 26;
      QLabel::setText(v45, v44);
      LOBYTE(v293) = 0;
      QString::~QString((QString *)&v213);
      v46 = (const struct QString *)sub_40CD10(&v251, "devices-up-to-date-text", 0, -1);
      v47 = v2[9];
      LOBYTE(v293) = 27;
      QLabel::setText(v47, v46);
      LOBYTE(v293) = 0;
      QString::~QString((QString *)&v251);
      v276 = QString::fromAscii_helper(":/Images/options.png", 20);
      LOBYTE(v293) = 28;
      v48 = (const struct QPixmap *)QPixmap::QPixmap(&v175, &v276, 0, 0, v170, v171);
      LOBYTE(v293) = 29;
      QLabel::setPixmap(v290[12], v48);
      LOBYTE(v293) = 28;
      QPixmap::~QPixmap((QPixmap *)&v175);
      LOBYTE(v293) = 0;
      QString::~QString((QString *)&v276);
      v284 = QString::fromAscii_helper(":/Images/tick.png", 17);
      LOBYTE(v293) = 30;
      v49 = (const struct QPixmap *)QPixmap::QPixmap(&v189, &v284, 0, 0, v170, v171);
      v2 = v290;
      LOBYTE(v293) = 31;
      QLabel::setPixmap(v290[13], v49);
      LOBYTE(v293) = 30;
      QPixmap::~QPixmap((QPixmap *)&v189);
      LOBYTE(v293) = 0;
      QString::~QString((QString *)&v284);
      v50 = (const struct QString *)sub_40CD10(&v197, "close-button", 0, -1);
      v51 = v2[10];
      LOBYTE(v293) = 32;
      QAbstractButton::setText(v51, v50);
      v14 = &v197;
      goto LABEL_36;
    case 7:
      v52 = (const struct QString *)sub_40CD10(&v249, "keyboard-update-ready-header", 0, -1);
      v53 = v2[8];
      LOBYTE(v293) = 33;
      QLabel::setText(v53, v52);
      LOBYTE(v293) = 0;
      QString::~QString((QString *)&v249);
      v54 = sub_40CD10(&v247, "keyboard-update-ready-text", 0, -1);
      ...
      ...
    case 12:
      v88 = (const struct QString *)sub_40CD10(&v266, "updating-keyboard-header", 0, -1);
      v89 = v2[8];
      LOBYTE(v293) = 57;
      QLabel::setText(v89, v88);
      LOBYTE(v293) = 0;
      QString::~QString((QString *)&v266);
      v90 = sub_40CD10(&v231, "updating-keyboard-text", 0, -1);
      ...
    case 13:
      ...
    case 16:
      v106 = (const struct QString *)sub_40CD10(&v204, "receiver-update-ready-header", 0, -1);
      v107 = v2[8];
      LOBYTE(v293) = 70;
      QLabel::setText(v107, v106);
      LOBYTE(v293) = 0;
      QString::~QString((QString *)&v204);
      v108 = (const struct QString *)sub_40CD10(&v223, "receiver-update-ready-text", 0, -1);
      v109 = v2[9];
      LOBYTE(v293) = 71;
      QLabel::setText(v109, v108);
      LOBYTE(v293) = 0;
      ...
  }
}

上面對內(nèi)部代碼進行精簡, 重點來關(guān)注一下case 7keyboard-update-ready-header這個是鍵盤準備升級的狀態(tài)叹谁,case 12就是鍵盤正在升級的狀態(tài)焰檩,case 16是接收器準備升級的狀態(tài)析苫。

優(yōu)聯(lián)接收器刷機

我們先來看看case 16是怎么進入的。查看函數(shù)sub_40CD40的引用有:sub_409C90+11国旷、sub_409C90+10C议街,直接進入函數(shù)sub_409C90特漩,也是個充斥著switch case的函數(shù)骨杂,直接F5偽代碼查看搓蚪,定位到關(guān)鍵代碼:

關(guān)鍵代碼

也就是執(zhí)行到state=16就能進入case 16妒潭,載入OD動態(tài)調(diào)試雳灾,將7E9D72位置的指令nop掉,這樣就能達到永遠都能進入接收器升級的界面炒嘲,F(xiàn)9運行起來夫凸。

成功提示該界面夭拌,選擇升級即可刷新固件啼止,這里注意一下兵罢,整個升級過程不需要外網(wǎng)卖词,F(xiàn)irmwareUpdateTool中集成了固件,所以為什么官方需要發(fā)布新版本FirmwareUpdateTool噪生,這也是一個原因东囚,可能也考慮到了離線升級這種場景页藻。

接收器準備升級
接收器升級中
升級成功

這樣簡單的爆破后璃吧,接收器的固件就可以任意刷了畜挨。

鍵盤刷機

根據(jù)上面分析繼續(xù)查看函數(shù)sub_409C90巴元,找到如下關(guān)鍵處:

可以看到v5的值很關(guān)鍵务冕,到這里你肯定想到直接給v5設置個非0值不就行了幻赚,但是事情沒這么簡單落恼,這邊*(_DWORD *)(v3 + 0x88) = v5v5進行了保存操作佳谦,說明這個值可能不是一個簡單的bool類型滋戳,隨便改為非0可能會對升級造成影響(這個也經(jīng)過了測試證實了我們的猜測奸鸯,會導致進入鍵盤升級界面娄涩,但是升級報錯)。繼續(xù)跟入函數(shù)sub_40AAA0扬虚,該函數(shù)內(nèi)有大量的復雜操作努隙,我們從返回值去快速定位到關(guān)鍵代碼:

函數(shù)sub_40AAA0

繼續(xù)跟入函數(shù)sub_40A8D0,這個函數(shù)里面讀取了設備信息辜昵,需配合OD動態(tài)調(diào)試荸镊,不然難以分析,這里就不演示了堪置,現(xiàn)在回看一下躬存,其實也比較容易猜到這邊的代碼的邏輯,分析結(jié)果如下:

函數(shù)sub_40A8D0

找到關(guān)鍵跳轉(zhuǎn)晋柱,將jb直接改為jmp即可。

關(guān)鍵跳轉(zhuǎn)

載入OD雁竞,在OD中修改跳轉(zhuǎn)钦椭,直接運行起來。


修改關(guān)鍵跳轉(zhuǎn)

成功進入k780鍵盤準備更新界面碑诉,選擇update后彪腔,進入升級界面,升級時鍵盤指示燈紅綠交替进栽。

鍵盤準備更新
鍵盤更新中

到這里德挣,你會發(fā)現(xiàn),僅做了關(guān)鍵跳轉(zhuǎn)的修改快毛,優(yōu)聯(lián)接收器也能隨便刷了格嗅,可以說明它們都使用函數(shù)sub_40A8D0進行版本判斷,那就一舉兩得了

0x03 后記

雖然干得熱火朝天唠帝,但是刷完之后屯掖,我的K780鍵盤故障依舊,其實是硬件問題(這就很尷尬)襟衰,后面我會另外介紹我的k780是如何復活的贴铜。

文件

官方FirmwareUpdateTool_1.2.169_x86

FirmwareUpdateTool_1.2.169_x86修改版 可平刷降級
鏈接: https://pan.baidu.com/s/1xGec18il4IkoHm-dJoXRHA 提取碼: g8si

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市瀑晒,隨后出現(xiàn)的幾起案子绍坝,更是在濱河造成了極大的恐慌,老刑警劉巖苔悦,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件轩褐,死亡現(xiàn)場離奇詭異,居然都是意外死亡间坐,警方通過查閱死者的電腦和手機灾挨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門邑退,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人劳澄,你說我怎么就攤上這事地技。” “怎么了秒拔?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵莫矗,是天一觀的道長。 經(jīng)常有香客問我砂缩,道長作谚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任庵芭,我火速辦了婚禮妹懒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘双吆。我一直安慰自己眨唬,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布好乐。 她就那樣靜靜地躺著匾竿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蔚万。 梳的紋絲不亂的頭發(fā)上岭妖,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音反璃,去河邊找鬼昵慌。 笑死,一個胖子當著我的面吹牛淮蜈,可吹牛的內(nèi)容都是我干的废离。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼礁芦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了悼尾?” 一聲冷哼從身側(cè)響起柿扣,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎闺魏,沒想到半個月后未状,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡析桥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年司草,在試婚紗的時候發(fā)現(xiàn)自己被綠了艰垂。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡埋虹,死狀恐怖猜憎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搔课,我是刑警寧澤胰柑,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布屎开,位于F島的核電站胎源,受9級特大地震影響善绎,放射性物質(zhì)發(fā)生泄漏布隔。R本人自食惡果不足惜俊戳,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一吱韭、第九天 我趴在偏房一處隱蔽的房頂上張望瞬逊。 院中可真熱鬧碱呼,春花似錦境输、人聲如沸蔗牡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蛋逾。三九已至,卻和暖如春窗悯,著一層夾襖步出監(jiān)牢的瞬間区匣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工蒋院, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留亏钩,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓欺旧,卻偏偏與公主長得像姑丑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子辞友,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載栅哀,詳見原文:https://yq.aliyun.com/articles/310305前言 本文可作為路由器安...
    andy_shx閱讀 1,438評論 0 1
  • 升級介紹 藍牙固件升級是使用手機給固件進行版本升級,以達到修復bug或者添加新功能的作用称龙。升級的大概流程是:首先留拾,...
    Snow_L閱讀 1,453評論 0 0
  • 1、Throwable接口中的getStackTrace()方法(或者Thread類的getStackTrace(...
    柒黍閱讀 668評論 0 1
  • 16宿命:用概率思維提高你的勝算 以前的我是風險厭惡者鲫尊,不喜歡去冒險痴柔,但是人生放棄了冒險,也就放棄了無數(shù)的可能疫向。 ...
    yichen大刀閱讀 6,033評論 0 4
  • 公元:2019年11月28日19時42分農(nóng)歷:二零一九年 十一月 初三日 戌時干支:己亥乙亥己巳甲戌當月節(jié)氣:立冬...
    石放閱讀 6,870評論 0 2