逆向罗技固件升级程序实现平刷和降级

0xFF 免责声明

对设备的刷机操作有风险,使用本文提到任何工具产生的问题,还请自行承担!

0x00 起因

在某天罗技驱动提示升级后,毫不犹豫选择了升级。不巧几天后,发现k780键盘的某些组合键无法工作,连Ctrl+C都无法使用,这让只会复制黏贴的高级攻城狮情何以堪。接着很容易把锅甩给罗技的固件升级,想要重刷一下固件,在罗技官网一顿操作后,发现了一个叫做Firmware Update Tool的程序,结果这工具仅能做升级操作,同版本的固件都无法重新写入,更不要说用旧版本进行降级。无奈之下端起逆向大旗,废话不多说。下面以目前次新版本FirmwareUpdateTool_1.2.169_x86为例。

0x01 寻找界面突破口

我们先在界面上寻找一些特征,首先连接好优联(unifying)接收器,然后键盘通过优联与电脑连接。


欢迎界面
寻找设备

如果键盘未连接,随便按个键唤醒

唤醒键盘

提示优联接收器可升级,会进入一个叫做YOUR RECEIVER IS READY TO UPDATE的确认窗口,这里点击update就会进行升级,当然如果你的固件版本大于或者等于该升级程序的版本,会直接弹出没有设备需要升级的提示。

优联设备

这里需要注意的是,优联接收器和K780键盘都是有单独的固件的,升级是分开的。

0x02 逆向分析

FirmwareUpdateTool.exe文件(官网下载的文件为rar自解压包,要先进行解压)拉入IDA进行静态分析,在函数窗口能找到一些Q开头的函数,很明显是Qt框架写的界面程序,从文件中也能发现Qt的类库Qt5Core.dll等文件。

函数列表

尝试搜索界面上的字符串,发现并不能找到完全符合的,翻一下能看到:/translations/en.qm,基本可以确定用了Qt框架的多国语言模块,还有welcome-headerwelcome-text之类就是字符串索引key,双击welcome-header查看,sub_40CD40+115引用了,直接进入sub_40CD40+115

出现的字符串.png

0040CE5B处的call获取真正的字符串,接下来的通过QLabel::setText设置label的文本。

image.png

在sub_40CD40函数是一个稍微复杂的switch case的代码,按F5生成伪代码,可以看到根据a2的值进行了不同操作,sub_40CD40函数主要功能是对界面进行操作。

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;
      ...
  }
}

上面对内部代码进行精简, 重点来关注一下case 7keyboard-update-ready-header这个是键盘准备升级的状态,case 12就是键盘正在升级的状态,case 16是接收器准备升级的状态。

优联接收器刷机

我们先来看看case 16是怎么进入的。查看函数sub_40CD40的引用有:sub_409C90+11sub_409C90+10C,直接进入函数sub_409C90,也是个充斥着switch case的函数,直接F5伪代码查看,定位到关键代码:

关键代码

也就是执行到state=16就能进入case 16,载入OD动态调试,将7E9D72位置的指令nop掉,这样就能达到永远都能进入接收器升级的界面,F9运行起来。

成功提示该界面,选择升级即可刷新固件,这里注意一下,整个升级过程不需要外网,FirmwareUpdateTool中集成了固件,所以为什么官方需要发布新版本FirmwareUpdateTool,这也是一个原因,可能也考虑到了离线升级这种场景。

接收器准备升级
接收器升级中
升级成功

这样简单的爆破后,接收器的固件就可以任意刷了。

键盘刷机

根据上面分析继续查看函数sub_409C90,找到如下关键处:

可以看到v5的值很关键,到这里你肯定想到直接给v5设置个非0值不就行了,但是事情没这么简单,这边*(_DWORD *)(v3 + 0x88) = v5v5进行了保存操作,说明这个值可能不是一个简单的bool类型,随便改为非0可能会对升级造成影响(这个也经过了测试证实了我们的猜测,会导致进入键盘升级界面,但是升级报错)。继续跟入函数sub_40AAA0,该函数内有大量的复杂操作,我们从返回值去快速定位到关键代码:

函数sub_40AAA0

继续跟入函数sub_40A8D0,这个函数里面读取了设备信息,需配合OD动态调试,不然难以分析,这里就不演示了,现在回看一下,其实也比较容易猜到这边的代码的逻辑,分析结果如下:

函数sub_40A8D0

找到关键跳转,将jb直接改为jmp即可。

关键跳转

载入OD,在OD中修改跳转,直接运行起来。


修改关键跳转

成功进入k780键盘准备更新界面,选择update后,进入升级界面,升级时键盘指示灯红绿交替。

键盘准备更新
键盘更新中

到这里,你会发现,仅做了关键跳转的修改,优联接收器也能随便刷了,可以说明它们都使用函数sub_40A8D0进行版本判断,那就一举两得了

0x03 后记

虽然干得热火朝天,但是刷完之后,我的K780键盘故障依旧,其实是硬件问题(这就很尴尬),后面我会另外介绍我的k780是如何复活的。

文件

官方FirmwareUpdateTool_1.2.169_x86

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容

  • 转载,详见原文:https://yq.aliyun.com/articles/310305前言 本文可作为路由器安...
    andy_shx阅读 1,469评论 0 1
  • 升级介绍 蓝牙固件升级是使用手机给固件进行版本升级,以达到修复bug或者添加新功能的作用。升级的大概流程是:首先,...
    Snow_L阅读 1,497评论 0 0
  • 1、Throwable接口中的getStackTrace()方法(或者Thread类的getStackTrace(...
    柒黍阅读 679评论 0 1
  • 16宿命:用概率思维提高你的胜算 以前的我是风险厌恶者,不喜欢去冒险,但是人生放弃了冒险,也就放弃了无数的可能。 ...
    yichen大刀阅读 6,054评论 0 4
  • 公元:2019年11月28日19时42分农历:二零一九年 十一月 初三日 戌时干支:己亥乙亥己巳甲戌当月节气:立冬...
    石放阅读 6,885评论 0 2