跳转至

硬件CTF题目汇总

硬件CTF题目汇总

题目附件备份地址:https://github.com/yichen115/HardWare-CTF

CrewCTF 2024

参考:

https://mwlik.github.io/2024-08-05-crewctf-2024-sniff-challenge/

https://xz.aliyun.com/t/15357

1728799152243-85f980d9-05bf-4d29-8dfc-92b0d7b8e318.png

这两道题共用一个压缩包附件,需要找到在键盘上输入的密码和输完密码在屏幕上显示的内容

通过阅读 README 得到的信息如下:

他用逻辑分析仪嗅探了设备的通信过程,来看一张全局的图,里面除了树莓派和逻辑分析仪其他两个不知道是啥,看看别的图找线索

1728799680850-18e903cd-2b80-4d11-b32e-2d42cb757c00.jpeg

通过这张图搜索发现是个电子墨水屏:https://learn.pimoroni.com/article/getting-started-with-inky-phat

1728799712199-0bd9bab0-6245-4892-9937-f81e109eccd7.jpeg

通过谷歌搜索键盘上的型号得知通信接口是 IIC:https://docs.m5stack.com/en/unit/cardkb

1728800073769-54c62d0e-bdb9-4f87-9d12-4ff84c422a77.png

Sniff One

首先来看键盘输入的信息

用逻辑分析仪对应的软件打开给的嗅探文件,嚯,这么多,来了个大活...

1728801908189-b8836d19-800d-4696-ae89-3b71bb18cc45.png

这得确认一下哪根线是哪个啊,在 README 中已经给出了,根据这个描述去找接线图,键盘的黑是 GND,红是供电,那么白色和黄色是 IIC,对应了其他地方的灰色和紫色

1728800368224-aac18147-5823-4f09-a0d3-2c1f887b4887.png

找到逻辑分析仪上的灰色和紫色,通过这张图可以看出来是通道 0 和通道 1

1728800994726-ca5d77d8-c341-4f99-8290-d957dd3965cf.jpeg

那么在逻辑分析仪软件中把通道 0 和通道 1 设置成 IIC 解析一下,这软件搜索功能有点不太行啊,导出来搜索一下

1728802075633-05ee21e1-296b-4dd8-ab38-c3ed7dbf6910.png

证据确凿,就是你了

1728802306207-ac0ba862-1e74-4783-ac9b-90917e76c5ee.png

过滤一下没用的信息

1728804835535-a67567d0-f007-4c1b-883e-74d9b2a61a29.png

得到 flag{717f7532}

1728802649532-fa26a53a-8066-4518-ade9-178f7836257b.png

Sniff Two

这个屏和树莓派之间的关系应该是树莓派上装个上位机软件,就能控制这个屏显示图像,然后搜了搜这个屏的引脚定义如下图:https://pinout.vvzero.com/pinout/inky_phat,估计要用剩下的 SPI 的数据了(后面看源码也能确定是 SPI)

1728803959359-9d97edba-8344-4666-861d-453155f7212d.png

但是正常使用应该是要盖在树莓派上的,因此引脚定义和实际接线图是对着的,要自己理解一下这个关系

1729341628192-aed705d6-94b9-41e8-a785-bae7bd285393.png

因此按照接线图来看一下

绿色是 BUSY 对应通道 2

黄色是 Reset 对应通道 3

橙色是 Data/Command 对应通道 4

红色是 MOSI 对应通道 5

棕色是 SCLK 对应通道 6

蓝色是 Chip Select 对应通道 7

然而打开逻辑分析仪我傻眼了,这名字不一样啊,感觉 SPI 的引脚定义各家叫有些不统一?MOSI 和 SCLK 肯定一一对应,Chip Select 应该是片选,可以对应到 Enable 上

一个合理的解释:对于 LCD 控制器来说,MOSI 引脚用于接收数据和命令,并且 D/C 由主机设置低电平时表示写入命令;高电平时表示写入数据/参数(https://forums.adafruit.com/viewtopic.php?t=51949)

因此可以直接将 D/C 引脚设置为 MISO,这样解析为 0x00 就表示是低电平写入命令,解析为 0xFF 就表示高电平写入数据

1730292776295-529957db-941a-42b5-bd23-19011d075dfa.png

这样就能从逻辑分析仪里面解析出来 SPI 发了啥,但是怎么从发出去的数据中恢复图像,还得分析源码...

这是它的源码:https://github.com/pimoroni/inky/blob/main/inky/inky.py

set_image 函数用来拷贝要显示的图像到 buffer 中

show 函数根据 buf 进行拆分,拆分成黑色的 buf_a 和红色的 buf_b

最后调用 _update 函数在屏幕上显示像素,里面有个 for 循环,0x24 时表示发送的是 buf_a,0x26 时发送的是 buf_b

1730523353198-621869a9-5dc7-4b1c-b538-54c617cc44ad.png

在逻辑分析仪里面搜索一下,发现有两个 0x24 和 0x26,可能是传输了两张图片,正好 Data/Command 也有两段不同的波动

1730425220801-cd456482-186b-412a-b422-9fcc4581bd2f.png

放大细节看一下,还是可以很好的区分 cmd 和 data 的

1730425326761-890d9f41-c003-480d-a054-486200cf81c4.png

接下来就是对这些数据进行处理了,可以用逻辑分析仪将这段导出为 csv

1730425440186-8fbc00ca-2af2-40ab-b940-dda2dbf45ac5.png

然后根据 0x24 和 0x26 分割处理一下数据,分别保存到 buf_a 和 buf_b 中,再根据他源码中的 set_image 和 show 函数来往回倒腾一下数据,先看看源码里都做了啥

show 函数把黑色像素点设置为0其余为1,红色像素点设置为1其余为0,然后将 bit 打包成NumPy 数组,转成 Python 列表

buf_a = numpy.packbits(numpy.where(region == BLACK, 0, 1)).tolist()
buf_b = numpy.packbits(numpy.where(region == RED, 1, 0)).tolist()

对应的,我们已经有 buf_a 和 buf_b 的列表了,只需要用 unpackbits 把它先还原回 NumPy 数组

buf_a_unpacked = np.unpackbits(np.array(buf_a, dtype=np.uint8))
buf_b_unpacked = np.unpackbits(np.array(buf_b, dtype=np.uint8))

set_image 函数是根据图像的大小存到了 numpy 的数组中,因此接下来需要考虑一个对于图像来说比较重要的参数:长和宽,根据源码中的定义,设置这些长宽时的 command 分别为 0x44 和 0x45

1730511067111-2e78ebaa-ec27-41ea-8b41-47697874958f.png

因此在逻辑分析仪中找到这些值,宽就是 (0x10+1) * 8 = 136,计算高的时候要注意小端序计数,因此高为 0x00F9 = 249

1730511131217-93e333f9-0859-4328-a883-46b53b48794b.png

使用 reshape 函数对长宽进行调整

width = 136
height = 249
buf_a = buf_a_unpacked[:height * width].reshape((height, width))
buf_b = buf_b_unpacked[:height * width].reshape((height, width))

最后再根据颜色往一个新的 buf 里面填充数值,最后创建一个图像数组根据 buf 将对应的坐标点改为不同的颜色

def display(buf_a, buf_b):
    width = 136
    height = 249

    color_mapping = {
        0:(255,255,255), # 白色
        1:(0,0,0),       # 黑色
        2:(255,0,0)      # 红色  
    }
    buf_a_unpacked = np.unpackbits(np.array(buf_a, dtype=np.uint8))
    buf_b_unpacked = np.unpackbits(np.array(buf_b, dtype=np.uint8))

    buf_a = buf_a_unpacked[:height*width].reshape((height,width))
    buf_b = buf_b_unpacked[:height*width].reshape((height,width))

    buf = np.zeros((height,width), dtype=np.uint8)

    for i in range(height):
        for j in range(width):
            if buf_a[i,j] == 0:   # 黑色
                buf[i,j] = 1
            elif buf_b[i,j] == 1: # 红色
                buf[i,j] = 2
            else:
                buf[i,j] = 0      # 白色

    image_array = np.zeros((height, width, 3), dtype=np.uint8)

    for y in range(height):
        for x in range(width):
            image_array[y,x] = color_mapping[buf[y,x]] # 修改对应位置的颜色

    image = Image.fromarray(image_array,'RGB')
    image.show()

最终可以打印出来两张图片,得到 flag{ec9cf2b7}

1730513323127-9d995881-500f-40c2-a622-4b12175d1975.png

原文: https://www.yuque.com/hxfqg9/iot/np22fh9shfn586ad