Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
8cryptdo — 8BitDoファームウェア暗号化のリバースエンジニアリングされたドキュメントとツール | Kitploit
ツール/GitHubGitHub/aeromodes/8cryptdo
組み込みシステムセキュリティ暗号化/復号化ツールIoTセキュリティリバースエンジニアリング暗号化ハードウェアとIoTセキュリティバイナリ解析論文と研究ファームウェア解析
GitHubaeromodes/8cryptdo

8cryptdo

8BitDoファームウェア暗号化のリバースエンジニアリングされたドキュメントとツール

291日前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有
リポジトリを見る

8CryptDo

8BitDoがGD32ベースのいくつかの製品で使用しているファームウェア暗号化に関する、 リバースエンジニアリングされたドキュメントとツール。

本ドキュメントで説明するアルゴリズムを適用した復号および再暗号化ツールについては 8cryptdo.py を参照。

このツールは、カスタマイズされたファームウェアを作成する可能性を秘めている。 ただし、このリポジトリには公式ファームウェアデータは一切含まれていない。最新の ファームウェアファイルをダウンロードするスクリプトは fwupd/8bitdo-firmware リポジトリにあり、 いくつかの古いファームウェアバイナリもアーカイブされている。

ヘッダー

ファームウェアの .dat ファイルを表面的に見ると、28バイトの平文ヘッダーが存在する。

root@kitploit:~
import struct

raw = open("sn30-v2_07.dat", "rb").read()
version, addr, payload_len = struct.unpack("<III", raw[:12])
# version     = 207        -> firmware == v2.07
# addr        = 0x08003400 -> destination (probably)
# payload_len = 99328      -> section payload length in bytes

最近のファームウェアファイルでは、これらの後に未知の32ビット値が配置されている。 ヘッダーの残りの部分はゼロである。

キーレス連鎖層

特定のファームウェアファイルでは、あるパターンが現れる。各ワードを次のワードに 混ぜ込む層が存在する。

root@kitploit:~
def rotr(x, r):
    return ((x >> r) | (x << (32 - r))) & 0xFFFFFFFF

dechained = [words[0]]
for i in range(1, len(words)):
    dechained.append(words[i] ^ rotr(words[i - 1], 3))

この連鎖は128ワードのブロック境界ごとにリセットされる。ブロックの最初のワード (pos = 0) には前の項がなく、pos = 1..127 は前のワードから連鎖する。

キーレス層はセキュリティを一切追加しない。これを剥がすと、よりクリーンな中間状態 (dechained) が現れ、平文を隠している唯一のものがキーストリームだけになる。

root@kitploit:~
P[i] = dechained[i] ^ keystream[i]

キーストリームの因数分解

fwupd/8bitdo-firmware リポジトリには 古いファームウェアのカタログがある。これらのファームウェアはすべて連鎖層を持っている ようだった。

連鎖層を除去した後でいくつかを互いにXORすると、長いゼロの連続と読み取り可能な 構造が現れる。

root@kitploit:~
plaintext_diff = [a ^ b for a, b in zip(dechained_a, dechained_b)]

両方で同じキーストリームが使用されている場合...

root@kitploit:~
a = dechained_a[i] ^ dechained_b[i]
b = (P_a[i] ^ keystream[i]) ^ (P_b[i] ^ keystream[i])
c = P_a[i] ^ P_b[i]
assert a == b == c

キーストリームは相殺される。

これは古典的な many-time pad である。このようなキーストリームは一度だけ使用 される場合にのみ安全である。何らかの理由で、8BitDoは製品ごとのキーストリーム だけでなく、複数の製品にまたがる単一のキーストリームを使用することを決定した。 これは彼らの暗号のセキュリティを大幅に弱め、ここでの残りの分析を可能にした。

キーストリームの読み取り

2つの平文が互いにXORされていることがわかれば、一方の一部を推測し、その推測を 差し引いてもう一方を読むことができる。これにより、その箇所の keystream が 復元される。

root@kitploit:~
keystream[i] = dechained[i] ^ P[i]
for fw in all_fw:
    P[fw][i] = dechained[fw][i] ^ keystream[i]

これは文字列を使えば最も簡単だろうが、ここで最も生産的なトリックはARM命令の バージョン間再配置だった。ある関数が2つのファームウェアバージョンで位置をずらして 現れることがある。一方のバージョンでキーストリームが判明している箇所では、もう 一方で共有コードを整列させ、新しいキーストリーム値を収穫できる。

これを活用することで、既知のキーストリームワードは約1,500から数千に増え、 ファームウェアのおよそ18%が読み取り可能になった。

キーストリームの規則

より大きな keystream のサンプルを得ると、パターンが見えるようになった。 16ワード離れた位置があり、ほぼ一定量ずつステップし、各バイトは予測可能な サイズだけ移動していた。

試行錯誤の末、ブロック内のすべてのキーストリームワードが次の式に従うことが 発見された。

root@kitploit:~
STEP  = 0x92A753FA
A_MUL = 0x80000301
ROT   = 18
UINT32_MAX  = 0xFFFFFFFF

def rotr(x, r):
    r &= 31
    return ((x >> r) | (x << (32 - r))) & UINT32_MAX if r else x

def block_base(block):
    return (block * A_MUL + block // 2) & UINT32_MAX

def keystream(block, pos, block_key):
    counter = (block_base(block) + pos * STEP) & UINT32_MAX
    mask = rotr(block_key, ROT * pos)
    return counter ^ mask

ブロック内では、単純なカウンター (block_base(block) + pos*STEP) を進め、 block_key から始まりステップごとに18ビット回転するマスクとXORする。 128ワードのブロック全体が、1つの32ビット数 block_key によって決定される。

1つの既知のワードがブロック全体を解錠する。式を並べ替えると、任意の既知の キーストリームワードから block_key が得られる。

root@kitploit:~
def block_key_from_known(block, pos, known_keystream):
    counter = (block_base(block) + pos * STEP) & UINT32_MAX
    return rotr(known_keystream ^ counter, -ROT * pos & 31)

シードテーブル

各ブロックのキーはどこから来たのか? それは256エントリのシードテーブルから 取り出された2つの16ビット半分に因数分解される。

root@kitploit:~
def block_key_of(block, seed_table):
    hi = seed_table[block]
    lo = seed_table[block ^ 0xF9]
    return (hi << 16) | lo

シードテーブル自体には特定の式がないようで、おそらく各デバイスのブートローダーの どこかに保存された512バイトの定数である。しかし、0xF9 とのXORは美しい副作用を もたらす。それは ミラー法則 である。ブロックとそのパートナー block ^ 0xF9 は、 同じ2つのテーブルエントリを入れ替えて構築される。

root@kitploit:~
mirror = block_key_of(block ^ 0xF9, seed_table)
assert mirror == rotr(block_key_of(block, seed_table), 16)

これは以前は未知だった領域を解読した。完全な32ビットの block_key を盲目的に 推測する代わりに、2つの16ビット半分を推測し、どちらの選択が両方のブロックを 信憑性のある文字列やARM命令に変えるかを採点できる。

これにより、この特定の暗号を使用するすべてのファームウェアデータの100%カバレッジが 得られた。

残りの作業

この暗号は2020年頃のほとんどの8BitDo製品、およびGD32 SoCを依然として使用して いる現在の製品 (SN30 Proを含む) をカバーしているようである。

M30やZero 2のようないくつかのデバイスはマルチセクションのようで、一部の ファームウェアデータはこの暗号を使用し、別のファームウェアは異なる暗号化 スキームを使用している。

より新しいデバイス、少なくとも異なるSoCを搭載したものは、より強力なファームウェア 暗号化スキームを持っているようである。いくつかの素朴な分析では、それが共有されて いる可能性があることを示しているが、不明である。さらに調査する必要がある。

ツールをダウンロード