You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何识别Wireshark捕获中数据迭代方式?Epomaker RT100屏幕逆向求助

Epomaker RT100 屏幕图像上传逆向工程排查问题

我正在逆向Epomaker RT100键盘的屏幕图像上传功能,用Wireshark抓取USB传输数据包分析。已确认数据采用RGB565格式发送,且存在两个8位值用于像素计数,但数据的迭代顺序并非简单的按行或列传输。目前我上传的图像能显示,但画面混乱,对比自制代码生成的数据包与官方软件的数据包,二者存在差异。

请问如何排查数据的传输逻辑?

以下是我编写的代码:

from PIL import Image
import struct
import usb.util
import usb.core
import time

def convert_rgb888_to_rgb565(rgb):
    r, g, b, a = rgb
    r = (r >> 3) & 0x1F
    g = (g >> 2) & 0x3F
    b = (b >> 3) & 0x1F
    return (r << 11) | (g << 5) | b

def encode_image_to_rgb565(image_path):
    img = Image.open(image_path)
    img = img.rotate(0)
    pixels = list(img.getdata())
    encoded_data = []
    for pixel in pixels:
        encoded = convert_rgb888_to_rgb565(pixel)
        encoded_data.append(struct.pack('!H', encoded))
    return b''.join(encoded_data)

encoded_image = encode_image_to_rgb565('rainbow.png')

screen = usb.core.find(idVendor=0x3151, idProduct=0x4015) #This is the actual screen, not the hub

if screen is None:
    raise ValueError('Device not found')

# Send initial control transfer
screen.set_configuration(1)
initial_payload = bytes.fromhex("a5000100f4da008b0000a2ad00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
screen.ctrl_transfer(0x21, 0x09, 0x0300, 2, initial_payload)

screen.ctrl_transfer(0xA1, 0x01, 0x0300, 2, 64) #GET_REPORT

# Header data = 25 00 01 00 __ 00 38 __
#                          0       161   wwww3w

y = 0x00
x = 0xA1

# Split and send data in chunks
chunk_size = 56  # Use 56 bytes for chunk to leave room for the 8-byte header
for i in range(0, len(encoded_image), chunk_size):
    header = [0x25, 0x00, 0x01, 0x00, y, 0x00, 0x38, x]
    chunk = encoded_image[i:(i + chunk_size)]

    # Combine header and chunk
    combined = bytes(header) + chunk

    # Ensure combined is exactly 64 bytes
    if len(combined) < 64:
        combined += b'\x00' * (64 - len(combined))  # Pad with zeros

    #Print the combined packet
    print(f'Header: {header}')
    print(f'Chunk: {chunk.hex()}')
    print(f'Combined: {combined.hex()}')

    if x - 1 > -1:
        x = int(x) - 1
    else:
        x = 255
    if y + 1 < 256:
        y = int(y) + 1
    else:
        y = 0

    print(combined)
    screen.ctrl_transfer(0x21, 0x09, 0x0300, 2, combined)

print("Image uploaded.")

usb.util.dispose_resources(screen)

排查步骤

  • 逐帧映射像素与数据包的对应关系:制作一张特征明确的测试图(比如仅左上角一个红色像素,其余全黑;或1xN的渐变条),分别用官方软件和你的代码上传,抓取两组USB数据包。逐个对比数据包中的像素数据和屏幕显示位置,重点关联那两个8位计数字段与实际像素坐标的映射规律——比如官方包中某个计数对应屏幕的(5,10)像素,而你的代码对应到了其他位置,就能反推出计数字段的真实作用。
  • 验证计数字段的含义:你当前假设x递减、y递增,但这两个字段可能不是直接的行/列坐标,而是分块编号或像素偏移的分段值。比如每个数据包传输28个像素(56字节=28个RGB565),计数字段可能是当前块的起始像素索引的高低位,或是分块的行列编号(屏幕被划分为若干块,y是块行号,x是块列号)。
  • 检查RGB565的字节序:你用struct.pack('!H', encoded)采用大端字节序,但官方可能使用小端字节序。可以把测试图的单个像素转成RGB565值后,分别用大端和小端打包,对比官方数据包的对应字节,确认是否字节序错误。
  • 核对初始控制指令:你发送的初始a5000100...数据包可能用于设置屏幕模式(比如旋转、镜像、扫描方向),如果官方软件发送的初始指令与你的不一致,也会导致画面混乱。抓取官方软件的初始传输包,对比你使用的initial_payload是否完全匹配。
  • 逆向分块与扫描规则:先确认屏幕的总像素数(比如128x64=8192像素,对应16384字节),然后统计官方数据包的数量和每个包的像素数据量。排查是否分块大小与你设置的56字节不同,或是扫描顺序为蛇形(行内来回扫描)、列优先,甚至分块后按Z字形排列。

内容的提问来源于stack exchange,提问作者AeroGlory

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 12:35:03