[翻译]如何将键盘改造成为贪吃蛇游戏(全)

发布者:gjden
发布于:2016-04-14 13:57
如何将键盘改造成为贪吃蛇游戏(硬件分析篇)

作者:spritesmods
翻译:gjden(from the adlab)



 

  译注:本文不完全是按照作者原文翻译,其中加入了我自己的理解和截图。个人感觉这边文章非常好,当然有点啰嗦,增加了翻译量。目前还没有翻译完,就分上下篇吧,请大家先看这个翻译好的上篇,下篇有空了再翻译给大家,不足和错误之处欢迎指正,也请高手出来一起讨论,互相学习。

一、简介

  尽管白天里我的本职工作是做软件开发,但晚上我却是一个喜欢hack各种电子玩意儿的黑客。那意味着我几乎整天都坐在电脑旁敲代码、调试….你可以想象键盘就是我选择征服一切的武器:为了让我的程序能够按照预期运行,我通常乐意一直不停的敲着键盘。 

  当然,大部分的键盘的生命是有限的,(尽管我固执的认为我所使用的IBM Model M是不朽的)。终于,有天我发现我工作键盘的shift键不能工作了,这是使得我的email看起来很不方便并且导致写代码时变得更加恼怒。因此我需要一个新键盘,老键盘是英式的橡胶圆顶键盘,因而这也是一次好机会来更换一个好一点的键盘,比如一个机械键盘。

  因此我便在网上搜索一个中意的键盘,我想要Cherry Brown 的机械按键,因为我是不会和选择声音大的Cherry Blues键盘的人做朋友。无数字键的键盘对于我来说更好,这会减少我手与轨迹球之间的距离。

  满足我所有要求的最便宜的键盘便是Coolermaster Quickfire Rapid-I(译注:看来人家有钱啊,这还便宜,酷冷至尊的键盘7百多元)。这个键盘是一款带有LED光的无数字键的游戏键盘,对于我来说可以省去不少麻烦:仅仅是他的游戏属性便不会出现键盘冲突(译注:键盘冲突是当多个键同时按下时其中一些按键不能响应,一般游戏玩家应该都不陌生,无冲突的键盘是最好的)并且将按键点击率提高到了一个非常惊人的水平,还有就是每一个按键背面都包含有一个白色的LED灯。

  LED背光是一个有趣的功能,键盘用它来实现了照明模式:你既能够设置固定的点亮模式,也可以设置为按键模式(你每次按键时点亮,然后灯光渐渐变暗)。

 
 
  拿到键盘后我带着它去上班,到第二天,我将新键盘给同事看,他们都知道我有一个撕毁保修条的嗜好(意思应该是大家都知道他喜欢hack硬件)。因此,其中一个同事开玩笑的说:”你已经拥有这个键盘24个小时了,这玩意有一堆LEDs灯和箭头键.很遗憾你还差一条蛇在这上面跑。”开始我也只是笑笑,但之后我就不停的想,Coolermaster(著名的键盘厂商)的家伙不是已经自豪的宣布这个键盘里有个ARM Cortex的CPU. 或许有可能.....。

二、硬件分析

  因此,如果我要实现这件事情,我首先需要研究一下硬件。他们说在里面有一个ARM, 但难道只是用来驱动键盘和LEDs吗?那么我需要打开键盘来确认一下。

 
 
  首先,我发现如此优雅的键盘为什么这么重的原因:这按键之下的白色板实际上是金属做成的。

  
  

  这是裸键盘的背部,可用看出PCB连接着按键和LEDs灯。注意为了防止按键冲突,每一个键都含有一个二极管。

  
 

  这是PCB上的一个特写区域,这里有一个控制器芯片(黑色正方形)。在左下部有微型的USB插座。在它的上方挨着主处理器的是电源调节器。处理器周围都是用来驱动LED灯的晶体管,还有一个i2c的EEPROM,可能用于存储灯光设置的。

  

  控制器是HT32F1755,由台湾公司Holtek(盛群半导体)制造。这是一个带有127k flash和32k RAM的72MHz ARM Cortex-M3。

   

  很奇怪这么快速的微处理器现如今便宜到被用来放在键盘上,其比某些插在其上的USB设备的控制器还要快。

  这个控制器有几个不错的额外特性。其拥有一个通用的SWD/JTAG-port可以用来调试运行在控制器中的程序,这只需要几条线和一个JTAG调试器就可以。其次,如果你想读写FLASH的话,键盘有一个基于bootloader的ROM,只需要拉下一个GPIO重置芯片即可,然后使用一个windows程序你便可以通过USB读写FLASH,但是读写FLASH可能会导致FLASH数据遭到破坏。

  芯片也包含有安全保护机制。最明显的是”安全位(security bit)”,它会阻止黑客从flash中获取数据,如果其访问来自于第三方(比如USB bootloader和JTAG)而不是flash内部的话会立刻关闭flash。

  好吧,安全位被设计来阻止了我要做的第一件事情:我需要fash数据来确定键盘工作的原理然后为其加入贪吃蛇功能。如果安全位被设置,我将不能使用SWD端口来获取固件。然而安全被设置了吗?只有一种方法来确认…

  首先,我需要找到一种容易实现的方法来获取JTAG/SWD端口。幸运的是,PCB板上有几个没有填充的标头。调用USB ROM bootloader的GPIO也以同样的方式被导出。

   
   

  译者注:可以清楚的看到有几个孔,上面五个孔是SWD调试接头,下面两个孔是bootloader的接头。下图是HT32F1755的控制器的引脚图。
 
  

  通过对PCB的分析我可以准确的找到SWD接口和Bootloader的接口。如图:

   

  从图中我们可以看出SWD引脚,然后直接按引脚链接线路找到对应的口就行了,如果找不到,可以直接到引脚上进行连接。

    

  这是我的装置,红色板是FT2232 JTAG board.它不能适配SWD,但是有一个单电阻攻击的方法来使其适配。
  译者注:OpenOCD中存在一个配置文件swd-resistor-hack.cfg,只需要其中的配置就可以。

 
 
  我使用OpenOCD来和他建立连接,之后做一些配置就可以dump Flash了.

  

  可以看到dump出来的全是无效数据,看来控制器的安全位被设置了。

  先翻译到此,下一篇抽空翻译后给大家呈上

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

如何将键盘改造成为贪吃蛇游戏(固件逆向与游戏代码的加入)

三、固件更新程序分析

  另外一种获取flash的方法是利用软件更新。幸运的是Coolermaster提供了固件更新功能,更新修改了一些bug,加入了闪光模式。你可以从官网上下载最新版本
http://gaming.coolermaster.com/en/products/keyboards/rapid-i/
官网上有两个版本:EU和US版本。这是因为这两个版本之间的硬件有一点不同:EU版本有额外的按键,因而固件也会不同。

  固件更新程序以某种方式将固件嵌入了其中。再动手之前,我猜这两个版本键盘的UI和更新功能应该是相同的,甚至处理更新的代码也应该差不多,意思是说,所有的不同应该在两个固件执行体中。为了验证的我的想法,我对比分析了两个不同更新文件,其差别都集中在文件末尾的一个16k的数据块儿上。

  

  接下来就是将这段数据分离出来,从内容上看的确应该与固件相关的内容,比如文件开始带有我所下载的固件版本号”1.1.7”(最新版本为1.2.2). 这是一个USB键盘,因此在固件中应该会有USB描述字符串之类的东西存在其中,并且是以UTF-16方式存储的字符串,字符串中包含有设备名和设备厂商的配置信息。但是从我打开的文件来说确什么也没有发现,所有固件要么是加密过的,要么是做过压缩。

  我们具体来看看是怎么回事,如下图,我们可以注意到两个特点,一个是不断重复的'A5 CA 88 A5',没有压缩数据会这样重复出现数据。另外一个是大量的A5出现在了文件中。因此我们猜测这个文件可能是使用了简单的异或方式进行加密,并且异或密钥为单字节A5.

  接下来就直接将解密的文件拖入反汇编器,确实反汇编出来了可以阅读的arm代码,但似乎固件仍有问题,有的跳转跳到了可读代码之外,这些跳转到的位置都是一些不可读的arm代码。总而言之,目前还不具备直接从固件上加入贪吃蛇代码。

  对于可执行文件还有另外一件事情要做,就是需要监控USB数据包,我们需要执行更新程序,使用一个USB嗅探器(USBPcap)来做。在观察了捕获的包后,我发现固件更新时会将数据发送给USB 的端点3,并且通过端点4来接收这些包。其中一个包实际上会将键盘切换到固件模式,更新包格式如下:

•        Byte 1-2: Command/subcommand, tells keyboard what to do
•        Byte 3-4: CRC16 of entire packet
•        Byte 5-8: 32-bit 'from'-address for command
•        Byte 9-12: 32-bit 'to'-address for command
•        Byte 13-63: Data

  这些包具体是如何起作用呢,在固件更新时,我分析并且推测出如下功能:

Cmd     Addr             Data       
1:2     0x2800-0x2808   (kb->pc) "1.1.5"        <- Read version
4:1     0               (Device re-enumerates)  <- Enter flash mode
0:8     0x2800-0x2808   -                       <- Erase version
0:8     0x2c00-0x6ad4   -                       <- Erase firmware
1:1     0x2c00-0x6ad4   Multiple, uploads fw    <- Write fw
1:0     0x2c00-0x6ad4   Multiple, uploads fw    <- Verify fw?
1:1     0x2800-0x2808        (pc->kb) "1.1.7"        <- Write new version
4:0     1              (Device re-enumerates)   <- Exit flash mode

  这里有一些我们感兴趣的东西。擦除和写入一个新的固件版本号,与擦除和更新固件程序本身,更新程序发送的是同一个命令,但是操作的区域不同:分别是以0x2800和0x2c00开始。从这里我可以推断出固件版本存储在flash的一个独立独立扇区里,简而言之,固件不需要实现一个完全不同的命令来管理固件版本号。

  但是,等一下,同样一个0x2800地址被用来读取固件版本号信息。固件版本事实上不会超出flash的预设数据长度,但是版本号读命令实际上和一般的flash读命令没有任何区别的,因此,这对于我来说是非常有用的:通过运行包含整个flash区域的读命令,我应该就能够获取flash中的所有数据内容(包含程序)。
  译者注:从如下地址空间布局图上可以看出128k的flash位于0x0000 0000-0x0002 0000,64kb SRAM空间为0x2000 0000-0x2000 1000。因而读写固件的地址都在地址空间的前128k的空间中。

  

  我通过usblib写了一个小程序来做这件事,程序本身没有什么问题,但是我却没有得到我想要的数据:除了固件版本区的数据外,读取到的其他内容都为0。我的推断(读取版本号命令即是读取flash命令)应该是没有问题,比如说,当我逐个增加地址时,我应该能够一个字节一个字节的读以致获得所有的数据。看起来好像还有其他问题使得我去读取失败。好吧,我以为能够很容易的更新我已经解密后固件的想法泡汤了。之后我在可读的反汇编代码中找到一些蛛丝马迹。

  

  在一些搜寻之后,我发现一些关键的代码,这段代码每被调用一次,就能从flash中读取一个字节。其中基本检查条件是如果一个字节是从一个允许的区域(0x2800-0x2c00)去读,那么它会跳出这段代码并发送一个字节,如果不是,那就用0来替代。
用NOP代码来替换掉最后一行,意味着一个字节的修改。并且在重加密和修正偏移之后,我已经让固件发生了改变,我没有看到任何CRC校验,但也可能错过一个其他方式的校验而被拒绝更新。然而,也有可能仍会接受更新但是会搞死我的键盘。
接下来启动更新程序运行更新,没有异常发生:看起来固件更新,至少来说,是不会自我校验的。过了一会儿,我算是松了口气:键盘在固件更新后被再次点亮了。但是新固件真的起作用了吗?我再次运行了我的程序并dump数据来看:

  

  在运行dump后的镜像文件后,通过反汇编器我可以确认这是一个完整flash dump.没有奇怪的代码出现,也没有跳转到无效代码,完全是我所期望的未加密固件数据。我似乎已经实现了键盘的固件备份!如此,除了能够逆向固件的所有功能外,也意味着如果我在开发贪吃蛇时如果犯了一个错误使得键盘变成板砖,那么我可以使用ROM的bootloader来将原始固件刷回去使其恢复工作状态。也意味着我的JTAG/SWD 工具可以派上用场:在大量的微处理器擦除之后我可以重存备份,我能够禁用保护位以致可以再次单步调试flash代码。

四、写一个基于Linux的固件更新程序

  在我写贪吃蛇之前,我还必须解决最后一个问题:我需要一种干净的方式来上传已经修改过的固件。当我用ROM USB Bootloader时,协助程序是一个基于windows窗口的工具,不适合作为攻击工具发布。但是如果做成基于linux的固件更新工具,那么便不会出现这样的问题。我不必发布一个Coolermaster 版权的代码,我可以让人们获取我做的固件更新和解密程序。

  为了确保固件不会存在其他额外的加密,我通过比较我已经解密的固件程序和我捕获的USB包来做推断:检查固件dump文件表明出错位的确不同,比较两者,我怀疑控制器自身做了一些异或并且可能在将固件烧录到flash中之前做了一些字节的重布局。

  当然,我一直盯着这个不同的字节区直到我发现了这个问题。就像我处理固件加密时一样。我都已经将代码反汇编了,为什么不能找到呢。的确,我在反汇编代码中找到了。

  

  基本上来说,有一个计数器用来记住发送到键盘中的固件包的数量。如果计数在10到100之间,固件将会处理被损坏的固件并且调用函数来修复他。
修复过程也不难,就是和一个52字节的秘钥进行异或。

  

  我快速的用C写了一个加密和解密的函数来模拟这个过程。并将其加入到之前的代码中。

  

  现在也可以解密固件更新程序为要写入flash的数据。生成一个文件并将其发送给flash.

五、加入贪吃蛇程序。

  随着之前工作完成,是时候编写贪吃蛇了。首先,我必须在flash和RAM中找到一个无用的存储区来存储我自己的代码和数据。这并不难:可以很容易的找到一个不少于64k的空闲flash和28k的RAM。为了修改键盘功能,我修改原始固件中的变量:比如PEM值和RAM中按下的键值数组。通过使用JTAG/SWD找到RAM的这种变量并不难。我在固件中加入一些HOOK使得我的代码能够执行。比如原始固件有一个功能是当按键被按下时调用,如果我对这个动作感兴趣,那就可以重定向到我自己函数执行,之后再调用原函数执行。
以下是我加入的Hook函数表。

  

  在这些攻击完成了后,我最终可以在键盘上玩贪吃蛇游戏了。
下面张图是我添加的贪吃蛇的方向控制。

  

六、扩展

  固件中还有一些东西我做了改善,首先,允许键盘这样更新有一个安全问题。当Coolermasters固件更新自身时,会有一个窗口通知固件更新操作,比如我的程序运行linux下默认是root权限的用户,攻击者会在用户不知情的情况下很容易的重新刷入固件。比如在固件中刷入键盘记录器或者进行类似于BadUSB的攻击。为了防止这种情况发生,我在我的固件中加入一个物理条件来开启flash模式。具体来说,你需要在按下组合键fn+f后10秒钟的时候才能进行固件更新。否则,更新会失败。

  

  另外,就是关于按键效果自定义和游戏自定义问题目前还没有,但是我想很多人想要写入自定义效果但是并不想拆开他们的键盘通过bootloader来修复损坏的固件。我发现键盘的USB功能是被禁用了的,因此我决定重用这个功能:在我的固件程序中,通过一定的cmd包可以用来覆盖掉固件中设置的LED信号值。我还写了一个PC上的demo来模拟(这个模拟程序作者好像没有提供)。

七、总结

  在Rapid-I(这次hack的工具包)中,默认固件并不是一成不变的,你可以用keyboad做比Coolermaster考虑得更多的事情。采用同样hack攻击技术,你除了用来玩儿贪吃蛇游戏,任何程序员理论上来说可以让自己想象任何东西运行在键盘上。我甚至希望是它变得更安全谢:加入物理组合键开启更新的功能防止恶意攻击键盘。

  一般来说,我一般写的代码都是开源的:固件分割和更新工具使用是请遵循GPLv3协议,贪吃蛇程序和其它代码请遵循Beer-Ware协议。因为版权法律原因我不得不清除所有Coolermaster的所有代码和二进制程序。但是你可以通过到官方下载更新程序来提取和重建。这次hack,仅仅使用USB键盘作为测试,因为我的键盘就是这个版本。

源码可以在附件下载。

上传的附件:

声明:该文观点仅代表作者本人,转载请注明来自看雪