前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Hardware】CrewCTF 2024 Sniff One && Sniff Two

【Hardware】CrewCTF 2024 Sniff One && Sniff Two

作者头像
yichen
发布2024-11-04 13:55:22
750
发布2024-11-04 13:55:22
举报
文章被收录于专栏:陈冠男的游戏人生

CrewCTF 2024 硬件题目

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

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

通过这张图搜索发现是个电子墨水屏:

https://learn.pimoroni.com/article/getting-started-with-inky-phat

通过谷歌搜索键盘上的型号得知通信接口是 IIC:

https://docs.m5stack.com/en/unit/cardkb

Sniff One

首先来看键盘输入的信息,用逻辑分析仪对应的软件打开给的嗅探文件,嚯,这么多,来了个大活...

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

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

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

证据确凿,就是你了

过滤一下没用的信息

得到 flag{717f7532}

Sniff Two

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

https://pinout.vvzero.com/pinout/inky_phat

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

因此按照接线图来看一下

绿色是 BUSY 对应通道 2

黄色是 Reset 对应通道 3

橙色是 Data/Command 对应通道 4

红色是 MOSI 对应通道 5

棕色是 SCLK 对应通道 6

蓝色是 Chip Select 对应通道 7

然而打开逻辑分析仪我傻眼了,这名字不一样啊,MOSI 和 SCLK 肯定一一对应,Chip Select 是片选,可以对应到 Enable 上

一个合理的解释:对于 LCD 控制器来说,MOSI 引脚用于接收数据和命令,并且 D/C 由主机设置低电平时表示写入命令;高电平时表示写入数据/参数

(https://forums.adafruit.com/viewtopic.php?t=51949)

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

这样就能从逻辑分析仪里面解析出来 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

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

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

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

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

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

代码语言:javascript
复制
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 数组

代码语言:javascript
复制
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

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

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

代码语言:javascript
复制
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 将对应的坐标点改为不同的颜色

代码语言:javascript
复制
color_mapping = {
 0:(255,255,255), # 白色
 1:(0,0,0),       # 黑色
 2:(255,0,0)      # 红色  
}

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}

参考

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

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

完整脚本:

代码语言:javascript
复制
import csv
import time
import numpy as np
from PIL import Image

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()



with open("./capture.csv") as file:
 buf_a = []
 buf_b = []
 in_packet = False
 in_buf_a = False
 in_buf_b = False
 my_table = csv.reader(file) 
 header = next(my_table) # 跳过第一行的标题
 for line in my_table:
  MOSI = int(line[2],16)
  D_C = int(line[3],16)
  if D_C == 0x00:    # 如果是command
   if MOSI == 0x01: # command 是 0x01 的话表示是数据包
    in_packet = True # 表示这是一张图片
   elif MOSI == 0x20:  # command 是 0x20 的话表示是图片传输完了
    in_packet = False
    display(buf_a, buf_b) # 展示图片
    buf_a = []
    buf_b = []
   elif MOSI == 0x24:  # 如果是 0x24 的话表示的 buf_a 的内容
    in_buf_a = True
   elif MOSI == 0x26:  # 如果是 0x24 的话表示的 buf_b 的内容
    in_buf_b = True
    in_buf_a = False

  elif D_C == 0Xff:  # 如果是 data
   if in_packet:  # 先判断是不是数据包
    if in_buf_a:  # 是 buf_a 的内容的话就往 buf_a 里面添加
     buf_a.append(MOSI)
    elif in_buf_b: # 否则是 buf_b 的内容的话就往 buf_b 里面添加
     buf_b.append(MOSI)
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-11-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 陈冠男的游戏人生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档