题解作者:cabasky
出题人、验题人、文案设计等:见 Hackergame 2022 幕后工作人员。
-
题目分类:binary
-
题目分值:300
今年,李德川和张修院两位同学报名参加了由淳平粮油店赞助的会员制比赛 RoboGame。
学院为了宣传近期科考领域的重大发现,定今年 RoboGame 的主题是《楼兰古国科考机器人》。
由于现实中楼兰古国的地形地貌漂亮得很啊!需要精细的移动控制,所以这个科考机器人需要使用 0x24 个轮子进行精密操控,这些轮子由下北泽的黑色高级轿车专用智能芯片控制,据说,这张芯片采用的是
8051
架构。另外,这台机器有 114514 个甚至 128 个I2C
端口可以进行数据传输,其中的一些端口与机器人的某些部件相连接,每个I2C
端口都以一个 ASCII 字符作为标识。为了降低操控难度,学院下发的机器中只有前十个轮子需要选手自己调速。可是面对着这台机器,两人面露难色:他们发现,只要固定一个轮子的速度,相邻的两个轮子的速度就会不受控制。
李德川同学不禁发出了“鸭蛋莫鸭蛋”的抱怨。此时,机器内部忽然发出了野兽般的咆哮:
“
0x24
号,是EEPROM
。”“
EEPROM
的话,那么读取和写入
什么的都有在做吗?”“那是当然啦!输入
指令
、端口
和发送字节长度
以后,如果指令是写入
,写入的第一个字节就是之后读写的页面序号,后面跟的字节就会一个一个一个一个地擦写。上电以后默认操作的页面是第 0 页。”“大人,您写入的高电平都白啦!”
“戳啦!
EEPROM
嘛!里面存着固件,上电后写入的1
只能把EEPROM
中的对应位擦成低电平。由于硬件的奇妙性质,你只有甚至九比特的擦除机会。”两人对着这台机器给出的提示摸不着头脑。此时,比赛方发来了信息:“根据赞助方的要求,我们的中期考核会降低难度,在九次调速中,能把前十个轮子的速度调成一样,老爷有赏啊!赏赐就藏在 0x24 个轮子的转速数据之中。”
请点击 此处链接 下载 EEPROM 中存储的固件的 C 语言源代码。
你可以使用 nc 202.38.93.111 11451
来与远程交互,或者点击下面的 “打开/下载题目” 按钮通过网页终端与远程交互。
如果你不知道
nc
是什么,或者在使用上面的命令时遇到了困难,可以参考我们编写的 萌新入门手册:如何使用 nc/ncat?
本题基于 emu8051
的库搭建了一个 Intel 8051
的环境,固件的代码被加载运行,内容详见 firmware.c
。
8051
的编译工具 sdcc
非常容易获得,如果会使用的话可以极大简化本题分析的难度。在 ubuntu
下 sdcc
可以通过 apt
下载得到,其他发行版类似。
根据提示,0x24
端口里会有可以读写的内容,并且存储在 EEPROM
中。那么首先要绕过端口的数字检测来访问这个端口。在源代码中的 port_to_int8
函数中可以看到检测逻辑的漏洞:只检测了输入的端口数字长度是否为一个字符,但是没判断这个字符是不是数字。因此可以输入任意 ASCII 字符来访问 0-127 端口,例如 '\x54'
这个字符就会访问到 0x24
端口。输入:
r \x54 64
得到类似这样的输入返回:
> i2c status: transaction completed / ready
02 00 06 02 04 0B 75 81 33 12 07 F4 E5 82 60 03
02 00 03 79 00 E9 44 00 60 1B 7A 00 90 09 54 78
81 75 A0 02 E4 93 F2 A3 08 B8 00 02 05 A0 D9 F4
DA F2 75 A0 FF E4 78 FF F6 D8 FD 78 00 E8 44 00
这就是刚开始第 0 页的二进制内容。并且通过如下的写入指令,来切换到第 i 页:
w \x54 i
按顺序将所有可以读出来的页面都保存下来拼接起来就是源代码编译完以后的固件。查看题目逻辑,发现每修改一个轮子就会调用 rand
函数,将两边的轮子都改成这个函数的返回值,而这个函数就是通过一个全局的循环下标变量在一个数组中遍历返回数值,范围为 0-7 ,用和 7 取与操作来循环。我们拥有了固件的读写权,所以可以修改固件中的一些比特来修改程序逻辑,但是限制是 9
比特,并且根据硬件特性,只能从 1 擦写成 0 。
用 sdcc
编译 firmware.c
(或者用 IDA
等工具来分析)。在生成的 .rst
文件中,有很详细的源码和汇编的对照帮助解题。可以在 0000B9
偏移处找到 rand
函数。一个简单的做法是,将:
249 ; firmware.c:27: init_rand&=7;
0000BB 53 08 07 [24] 250 anl _init_rand,#0x07
中的 anl
指令的第二个操作数 7 改成 0,总共修改 3 比特。如此一来相当于每次都和 0 取与,不再有随机性,返回值永远为 0x11。
计算好偏移后修改此页上的相应位即可,payload 中将要擦写掉的位置为 1。
最后根据提示,用类似的方法,读取 0-0x23 端口轮子的速度,读到的值转 ASCII
就为 flag。详见 src/exp.py
。
由于 8051 指令集中的 NOP
为 0x00,所以本题可能还有其他的花式修改方法,在此就不再赘述了。以下给出指令集的参考:8051 Instruction Set Manual - Keil。