Lithiumee

Lithiumee

Go wild

Yunxi Box 3.0 Pro ハック: 制限からルート化へ

背景#

image
手里に長年放置されていた云犀 Box 3.0 Pro タッチスクリーン導播一体機があり、このデバイスはデュアル 1080P HDMI 入力、内蔵の抖音、淘宝、快手ライブストリーミングおよび RTMP プッシュストリーミング機能をサポートしています。しかし、以前の使用シーンは特異であり、一部のプラットフォームでは RTMP プッシュストリーミングが便利に行えず、主にコンピュータを介して複数の USB キャプチャカードと OBS Studio 仮想カメラを接続する方法が採用されていたため、このデバイスは長年放置されていました。

最近、私はちょうど同様の位置付けのハードウェア製品を開発していたので、このデバイスを再評価し始めました。一方では、そのソフトウェアとハードウェアのソリューションを学び理解したいと思い、もう一方では、ソフトウェアの改造を通じて、その独特なハードウェアが他のシーンで機能することを期待しています。

ハードウェア#

背面の滑り止めパッドの下に隠れているネジを外すと、簡単に後面カバーを開けることができます。マザーボードの設計は比較的シンプルで、部品は片面にのみ配置されており、コアモジュール + 基板の形式を採用しています。

このような PCB スペースが豊富なプロジェクトにとって、コアボードやコアモジュールを使用することには多くの利点があります。一方では、高周波の RF 部分、高速の DDR メモリ、ストレージなど、最も複雑な設計がコアモジュールに統合されており、ハードウェア設計とデバッグの作業量が大幅に削減され、製品をより早く市場に投入するのに役立ちます。もう一方では、基板はより少ない層数とよりシンプルなプロセスを採用でき、高多層基板を最小面積に制限し、コストを効果的に管理できます。

分解図から云犀 Box 3.0 Pro の主要なチップソリューションが確認できます:

  • コアモジュール:移遠 (Quectel) SC60 モジュール、Qualcomm Snapdragon 625 (MSM8953) SoC、2GB RAM および 16GB eMMC。
  • イーサネット:亚信 (ASIX) AX88179 USB 3.0 to Gigabit Ethernet
  • MIPI-DSI to HDMI Tx ブリッジ:龙讯 (Lontium) LT8912B
  • HDMI Rx to MIPI-CSI ブリッジ:东芝 (Toshiba) TC358840XBG *2
  • 電源管理:德州仪器 (TI) BQ25890
  • USB HUB:创惟 (Genesys Logic) GL3523
    IMG_20250330_142827
    IMG_20250330_144149
    最初、私は最も一般的な瑞芯微 RK3399 ソリューションを採用していると推測しましたが、分解後に Qualcomm ソリューションであることがわかりました。よく考えると、ライブストリーミングデバイスはモバイルネットワークを介してストリーミングする必要があるため、ベースバンドの利点を持つ Qualcomm プラットフォームは非常に魅力的であり、Qualcomm ソリューションを選択するのは理にかなっています。
    image 1
    そのコア機能である HDMI キャプチャに関して、云犀 Box は 2 つの東芝 TC358840XBG ブリッジチップを使用して 2 つの信号をキャプチャしています。TC358840XBG はクラシックですが、いくつかの制限もあります。4K 30fps のビデオキャプチャを実現するには、Dual Link MIPI-CSI-2 で合計 8 レーンが必要です(2 つのリンクがそれぞれ 4K 画面の左右をキャプチャします)。云犀 Box のように Single Link CSI-2 を使用すると、1080p キャプチャまでしかサポートできません。それに対して、現在の新しいチップ、例えば龙讯の LT6911UXE は、MIPI の各レーンの最高速度が 2.5Gbps に達し、1 つの 4 レーンの MIPI ポートで 4K 60fps キャプチャをサポートできます。4K キャプチャソリューションはかなりシンプルになります。ただし、TC358840XBG にはオープンソースドライバーがあり、個人開発者に優しいのに対し、LT6911UXE の公開資料は現在非常に少ないです。
    image 2
    ハードウェア設計において、MSM8953 の周辺機器の制限が設計にいくつかの課題をもたらしていることを感じます。MSM8953 には内蔵イーサネット MAC も PCIe インターフェースもないため、有線ネットワーク接続を実現するには基本的に USB ネットワークカードに依存するしかありません。しかし、MSM8953 には 1 つの USB 3.0 インターフェースしかありません。これにより、一連の連鎖反応が引き起こされます。一方では、1 つの USB ネットワークカードと 1 つの外部 USB 3.0 Type-A インターフェースを同時にサポートするために、USB HUB チップ(GL3523)を導入する必要があります。もう一方では、主にデバイス用の Type-C インターフェースとホスト用の USB HUB は、USB スイッチチップを使用して信号の物理的接続を切り替える必要があり、主板上の PI3USB302 および TS3USB221A に対応します。これは、システムがホストモード(ネットワークカードと Type-A ポートを接続)に切り替わると、Type-C インターフェースが完全に切断され、このインターフェースに依存する ADB、Fastboot などの機能が使用できなくなることを意味します。
    image 3

ソフトウェア#

デバイスのインターフェースから、云犀 Box が Android システムを実行していることがわかります。純粋な Linux ソリューションと比較して、Android システムを採用することで、ハードウェアに対する高度な抽象化を利用して開発を簡素化し、Java ベースの成熟したアプリケーション開発エコシステムを活用してアプリケーション開発効率を大幅に向上させることができます。

しかし、この Android システムは深く制限されており、公式の云犀 APP のみを実行でき、任意のアプリケーションを直接起動することはできず、ADB を開く場所もなく、通知バーはロックされ、システム設定などの標準 Android インターフェースも隠されています。簡単なシステムインターフェース操作で制限を解除することは不可能なようです。
IMG_20250406_150942_1
そこで次のステップを計画しました:Qualcomm プラットフォームの 9008 緊急ダウンロードモード(EDL)を利用して、デバイスの eMMC ストレージ内のsystemパーティションイメージを抽出し、イメージをマウントして変更し、内蔵アプリを置き換え、最後に変更されたイメージを再度書き込むことです。

試してみたところ、マザーボード上に予備のボタン S2 があり、ちょうど MSM8953 のforce_boot_from_usbピン(GPIO37)に接続されていることがわかりました。電源を入れたときにこのボタンを押し続けると、デバイスは 9008 モードに入ります。Qualcomm Premium Tool を使用してsystemパーティションイメージをエクスポートし、その後 PC 上で自由にこのイメージをマウントして変更できるようになります。
image 4
云犀 APP は抖音、快手、淘宝ライブストリーミングなどの特定のサードパーティアプリを起動できるため、淘宝ライブストリーミングに対応する APK ファイルをパッケージ名を変更した QuickShortcutMaker APK に置き換えて、すり替えを行いました。変更されたsystem.imgを 9008 モードを介してデバイスに書き戻し、再起動後にデスクトップ上の元の「淘宝ライブ」のアイコンが QuickShortcutMaker のアイコンに変わっているのを確認しました。QuickShortcutMaker を開くと、システム内にインストールされた任意のアプリケーションやシステムコンポーネントを自由に起動できるようになり、隠されていた Android のネイティブ設定も含まれています。
完全なシステム制御権を得るためには、Root が不可欠です。Magisk を使用してデバイスのbootパーティションイメージをパッチし、システムに su コマンドを追加し、書き込んだ後、このデバイスは無事に Root 権限を取得しました。
IMG_20250406_163334
以前心配していた USB の問題が発生しました。設定で USB デバッグオプションを正常に有効にしたにもかかわらず、Type-C ポートをコンピュータに接続しても ADB デバイスが表示されませんでした。これは、USB コントローラがホストモードにあることを示しており、内蔵の USB ネットワークカードと Type-A ポートに使用されています。

しかし、自分で強制的にモードを切り替えるのはかなり厄介です。可能性のあるが未検証の経路は、一方でハードウェア上で USB 切り替えチップの切り替え制御信号線を切断し、USB 信号を物理的に Type-C インターフェースに常に向けること、もう一方でソフトウェア上で/sys/kernel/debug/msm_otg/modeperipheralに設定し、USB コントローラをデバイスモードで動作させることです。

しかし、偶然にもマザーボード上の別の予備ボタン S3 を押すと、USB デバッグが意外に表示されました。これはおそらくメーカーが用意したデバッグ手段です。これで、私たち自身が複雑なソフトウェアとハードウェアの変更を行う手間が省けました。
image 5
その後、USB モード切り替えのプロセスを整理するために、boot.imgからデバイステーブルを抽出し、逆コンパイルしました。デバイステーブルのusb_detectノードで、qcom,gpio-usbdetectExampleと比較して、追加でkey-gpioが定義されており、その GPIO 番号はvol_upキーに対応していることがわかりました。ボード上の S3 ボタンはまさにこのvol_upに対応しています。したがって、メーカーはカーネル内のgpio-usbdetectドライバを変更し、S3 ボタンを押すことで割り込みをトリガーし、USB スイッチの切り替えおよび USB ロール(ホスト / 周辺機器)の切り替えロジックを実行できるようにしたと思われます。

vol_up {
	label = "volume_up";
	gpios = <0xbe 0x55 0x01>;
	linux,input-type = <0x01>;
	linux,code = <0x73>;
	debounce-interval = <0x0f>;
};

usb_detect {
	compatible = "qcom,gpio-usbdetect";
	interrupt-parent = <0x11e>;
	interrupts = <0x00 0xc6 0x00>;
	interrupt-names = "vbus_det_irq";
	key-gpio = <0xbe 0x55 0x01>;
	usb-switch-gpio = <0xbe 0x1c 0x00>;
};

カーネルログ(dmesg)を観察すると、S3 を押す前後の usb_role_switch に関連するログが表示されます:

[ 1089.480345] [yunxi_bq25890]: vbus_detect_delay_work: key_level: 0
[ 1089.480374] [yunxi_bq25890]: usb_role_switch , is_host: 1

[ 1329.977896] --== bq25890_irq_handler
[ 1339.940379] [yunxi_bq25890]: vbus_detect_delay_work: key_level: 0
[ 1339.940410] [yunxi_bq25890]: usb_role_switch , is_host: 0

HDMI キャプチャ#

HDMI キャプチャはこのデバイスの最もコアで独特な機能です。私の目標は、制限の多い公式の云犀 APP を回避し、このハードウェア能力を直接利用できるようにすることです。

TC358843XBG チップは MIPI CSI-2 インターフェースを介してビデオデータを出力し、これは一般的なセンサーが SoC に接続される方法と一致します。異なる点は、TC358843XBG がカメラで一般的な Bayer RAW 形式ではなく、YUV または RGB 形式のデータを直接出力することです。

オープンソースの Open Camera アプリをインストールすると、2 つの HDMI 入力の画面を直接認識しプレビューできます。2 つの HDMI 入力は 2 つのカメラです。これにより、Open Camera を介して HDMI 入力のリアルタイムモニタリングとビデオ録画が直接行えるようになります。再び Android の良さを実感し、優れたハードウェア抽象化が強力な互換性をもたらし、多くの機能が特定のプラットフォームごとに繰り返し開発する必要がなく、膨大なエコシステムの支援を受けて、既存のさらにはオープンソースの膨大なアプリケーションを直接利用できるようになります。

Open Camera のデバッグ情報を確認し、云犀公式 APP の逆コンパイル分析と組み合わせることで、云犀 Box が古いandroid.hardware.Camera API(Camera1 API)を使用しており、より強力で柔軟な Camera2 API をサポートしていないことを確認できます。

image 6

view

その他#

起動プロセス#

パーティションバックアップの書き込み中にいくつかのエラーが発生したため、故障を調査する際に MSM8953 プラットフォームの起動プロセスについて大まかな理解を得ました:

  1. 電源投入と Boot ROM (PBL):
    1. 電源ボタンを押すと、プロセッサは内部 ROM (Boot ROM) の定義されたアドレスから実行を開始し、主ブートローダー (PBL - Primary Boot Loader) を実行します。
    2. PBL は eMMC などのブートメディア上のパーティションテーブル (GPT) を読み取り、sbl1 (Secondary Boot Loader 1) またはバックアップのsbl1bakパーティションを見つけ、それをオンチップメモリ (IMEM) にロードして実行に移ります。
  2. SBL1:
    1. SBL1 はまず DDR メモリを初期化し、その後の起動段階に必要なコンポーネントをロードして検証します:
      • tz.mbn: TrustZone 関連コンポーネント (QSEE, HYP, TEE など)。
      • devcfg.mbn: デバイス設定データ。
      • rpm.mbn: リソース電力管理 (RPM) ファームウェアを RPM コアの内部メモリ (TCM) にロードします。
      • aboot.mbn: アプリケーションブートローダー (APPSBL)、通常は Little Kernel (LK) です。
    2. RPM に起動信号を送信し、そのリセット状態を解除します。
    3. RMR_EL3を書き込んで Warm Reset をリクエストし、CPU は AArch64 EL3 特権レベルで再起動し、TrustZone 実行環境に入ります。
  3. QSEE/ TrustZone:
    1. EL3 環境でセキュリティ関連の初期化を行います。
    2. CPU を AArch32 EL1 に切り替え、aboot.mbn (LK) のエントリポイントにジャンプします。
  4. Aboot (LK):
    1. LK (little kernel) は Fastboot フラッシュモードを提供します。
    2. MIPI-DSI コントローラーやディスプレイパネルなどの必要なハードウェアを初期化し、splashパーティションからロゴファイルを読み込んで起動画面を表示します。
    3. boot.img (通常起動) またはrecovery.img (リカバリーモード) をロードし、それを検証します。
    4. 内核の実行環境を準備し、AArch64 EL1 に切り替え、内核のエントリアドレスにジャンプします。
  5. Kernel:
    1. systemvendoruserdatapersistなどのパーティションをマウントすることを認識します。
    2. モデム (ベースバンド)、WCNSS (Wi-Fi/Bluetooth) などの他のコプロセッサファームウェアをロードして起動します。
    3. userspace に入り、ユーザースペースの最初のプロセスinitを起動し、Android を起動します。

デバイステーブルの抽出#

デバイステーブルはハードウェア情報を記述しており、カーネル起動時にそれを読み取って対応するドライバを構成します。Qualcomm プラットフォームでは、DTB (Device Tree Blob) は通常、カーネルイメージ(zImage)の末尾に付加されます。

unpackbootimgツールを使用してboot.imgからzImageを分離し、次にmoetayuko/split-appended-dtbから取得したsplit-appended-dtbツールを使用して、boot.img からすべての付加された DTB ファイルを抽出し、合計 69 個の DTB ファイルを抽出しました。

$ file boot.img 
boot.img: Android bootimg, kernel, ramdisk, page size: 2048, cmdline (console=ttyHSL0,115200,n8 androidboot.console=ttyHSL0 androidboot.hardware=qcom msm_rtb.filter=0x237 ehci-hcd.park=3 lpm_levels)
$ ./unpackbootimg -i boot.img -o ./
BOARD_KERNEL_CMDLINE console=ttyHSL0,115200,n8 androidboot.console=ttyHSL0 androidboot.hardware=qcom msm_rtb.filter=0x237 ehci-hcd.park=3 lpm_levels.sleep_disabled=1 androidboot.bootdevice=7824900.sdhci earlycon=msm_hsl_uart,0x78af000 buildvariant=user
BOARD_KERNEL_BASE 80000000
BOARD_PAGE_SIZE 2048
$ ls
boot.img  boot.img-base  boot.img-cmdline  boot.img-pagesize  boot.img-ramdisk.gz  boot.img-zImage
$ ./split-appended-dtb boot.img-zImage 
Found 69 appended dtbs, please check the output.

その後、dtc (Device Tree Compiler) ツールを使用して、バイナリの DTB ファイルを人間が読めるテキスト形式の DTS (Device Tree Source) に逆コンパイルしました。

#!/bin/bash
        for i in $( ls *.dtb ); do
            echo decompile $i
            dtc -I dtb -O dts $i -o output/$i.dts
        done

デバイステーブルは多数ありますが、実際には 1 つだけが使用され、LK は SBL から渡されたハードウェア ID(例えばqcom,msm-idqcom,board-id)に基づいて最も適合するものを選択してロードします。LK が起動プロセス中にシリアルポートを介して出力するログ情報を確認すると、ID が<293 8 0 0x0>のデバイステーブルが一致していることがわかります。

[2110] Found an appended flattened device tree (Qualcomm Technologies, Inc. MSM8953 + PMI8950 MTP - 293 8 0 0x0)
[2120] Add DTB entry 293/00000008/0x00000000/0/10016/0/0/0/a11c281f/3e740
[2120] Device tree exact match the board: <293 8 0 0x0> == <293 8 0 0x10001>

これに基づいて、対応するデバイステーブル DTS ファイルは次のようになります:

/ {
	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 MTP";
	compatible = "qcom,msm8953-mtp\0qcom,msm8953\0qcom,mtp";
	qcom,msm-id = <0x125 0x00>;
	interrupt-parent = <0x01>;
	qcom,board-id = <0x08 0x00>;
	qcom,pmic-id = <0x10016 0x00 0x00 0x00>;
	...
};

DTS ファイルからは、特別な GPIO 割り当て、スクリーン初期化シーケンスなど、非常に豊富な情報を取得できます。これはシステム移植に非常に価値があります。例えば、以下の MIPI DSI スクリーン初期化情報があれば、このスクリーンを点灯させるのが容易になります。

qcom,mdss_dsi_yunxi_1200p_video {
				qcom,mdss-dsi-panel-name = "yunxi 1200p video mode dsi panel";
				qcom,mdss-dsi-panel-controller = <0x1a4>;
				qcom,mdss-dsi-panel-type = "dsi_video_mode";
				qcom,mdss-dsi-panel-destination = "display_1";
				qcom,mdss-dsi-panel-framerate = <0x3c>;
				qcom,mdss-dsi-virtual-channel-id = <0x00>;
				qcom,mdss-dsi-stream = <0x00>;
				qcom,mdss-dsi-panel-width = <0x4b0>;
				qcom,mdss-dsi-panel-height = <0x780>;
				qcom,mdss-dsi-h-front-porch = <0x32>;
				qcom,mdss-dsi-h-back-porch = <0x3e>;
				qcom,mdss-dsi-h-pulse-width = <0x08>;
				qcom,mdss-dsi-h-sync-skew = <0x00>;
				qcom,mdss-dsi-v-back-porch = <0x08>;
				qcom,mdss-dsi-v-front-porch = <0x0a>;
				qcom,mdss-dsi-v-pulse-width = <0x06>;
				qcom,mdss-dsi-h-left-border = <0x00>;
				qcom,mdss-dsi-h-right-border = <0x00>;
				qcom,mdss-dsi-v-top-border = <0x00>;
				qcom,mdss-dsi-v-bottom-border = <0x00>;
				qcom,mdss-dsi-bpp = <0x18>;
				qcom,mdss-dsi-color-order = "rgb_swap_rgb";
				qcom,mdss-dsi-underflow-color = <0xff>;
				qcom,mdss-dsi-border-color = <0x00>;
				qcom,mdss-dsi-on-command = [39 01 00 00 01 00 02 b0 00 39 01 00 00 01 00 06 b3 14 08 00 22 00 39 01 00 00 01 00 02 b4 0c 39 01 00 00 01 00 03 b6 3a d3 39 01 00 00 01 00 02 b7 00 39 01 00 00 01 00 07 b8 07 90 1e 00 1e 32 39 01 00 00 01 00 07 b9 07 82 3c 00 3c 87 39 01 00 00 01 00 07 ba 07 9e 20 00 20 8f 39 01 00 00 01 00 19 ce 7d 40 43 49 55 62 71 82 94 a8 b9 cb db e9 f5 fc ff 01 38 02 02 44 24 01 39 01 00 00 01 00 02 c0 ff 39 01 00 00 01 00 25 c1 04 61 00 20 8c a4 d6 ff ff ff ff 7f 73 ef b9 f6 ff ff ff ff bf 54 22 02 00 00 00 00 00 00 62 03 00 22 00 01 39 01 00 00 01 00 0a c2 31 f7 80 00 09 00 04 00 00 39 01 00 00 01 00 04 c3 00 00 00 39 01 00 00 01 00 0d c4 70 00 00 00 00 00 00 00 00 05 04 00 39 01 00 00 01 00 15 c6 6f 73 75 0a 14 0b 16 00 00 00 00 00 00 00 00 00 00 1c 17 09 39 01 00 00 01 00 1f c7 08 0e 19 22 31 3f 49 58 3c 44 4e 5b 64 6c 76 04 0c 13 1e 2d 3d 47 58 3c 44 50 5d 66 6e 76 39 01 00 00 01 00 14 c8 01 00 00 00 00 fc 00 00 00 00 00 fc 00 00 00 00 00 fc 00 39 01 00 00 01 00 0d cb 3f c0 0f f0 03 00 03 00 03 00 00 c0 39 01 00 00 01 00 02 cc 11 39 01 00 00 01 00 06 d0 44 81 bb 15 94 39 01 00 00 01 00 1a d3 0b 33 bf bb b3 33 33 37 00 01 00 a0 98 a0 00 39 39 33 3b 37 72 07 3d bf 99 39 01 00 00 01 00 03 44 07 7f 39 01 00 00 01 00 06 2a 00 00 04 af 00 39 01 00 00 01 00 06 2b 00 00 07 7f 00 39 01 00 00 01 00 02 d6 01 39 01 00 00 01 00 02 3a 77 39 01 00 00 01 00 02 2c 00 39 01 00 00 01 00 02 51 e6 39 01 00 00 01 00 02 53 24 39 01 00 00 01 00 02 55 01 05 01 00 00 32 00 02 29 00 05 01 00 00 78 00 02 11 00 39 01 00 00 32 00 02 35 00];
				qcom,mdss-dsi-off-command = [05 01 00 00 32 00 02 28 00 05 01 00 00 78 00 02 10 00];
				qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
				qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
				qcom,mdss-dsi-h-sync-pulse = <0x01>;
				qcom,mdss-dsi-traffic-mode = "burst_mode";
				qcom,mdss-dsi-lane-map = "lane_map_0123";
				qcom,mdss-dsi-bllp-eof-power-mode;
				qcom,mdss-dsi-bllp-power-mode;
				qcom,mdss-dsi-lane-0-state;
				qcom,mdss-dsi-lane-1-state;
				qcom,mdss-dsi-lane-2-state;
				qcom,mdss-dsi-lane-3-state;
				qcom,mdss-dsi-panel-timings = <0xf23a2800 0x6c6e2c3e 0x2e030400>;
				qcom,mdss-dsi-t-clk-post = <0x02>;
				qcom,mdss-dsi-t-clk-pre = <0x2d>;
				qcom,mdss-dsi-bl-min-level = <0x01>;
				qcom,mdss-dsi-bl-max-level = <0xfff>;
				qcom,mdss-dsi-dma-trigger = "trigger_sw";
				qcom,mdss-dsi-mdp-trigger = "none";
				qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
				qcom,mdss-dsi-reset-sequence = <0x01 0x14 0x00 0x02 0x01 0x14>;
				qcom,mdss-dsi-panel-timings-phy-v2 = <0x241f0809 0x50304a0 0x241f0809 0x50304a0 0x241f0809 0x50304a0 0x241f0809 0x50304a0 0x241c0809 0x50304a0>;
				qcom,mdss-dsi-bl-pmic-pwm-frequency = <0x64>;
				qcom,mdss-dsi-bl-pmic-bank-select = <0x00>;
				qcom,mdss-dsi-pwm-gpio = <0x1a5 0x04 0x00>;
				qcom,panel-supply-entries = <0x1a3>;
				linux,phandle = <0x1a9>;
				phandle = <0x1a9>;
			};
		};

参考#

云犀 BOX - 视界,精彩一触即播,国内领先触屏直播硬件,让直播导播更简单 - 云犀直播

高通プラットフォーム dtb ファイルのロードプロセス_高通 dump dts ファイル - CSDN ブログ

私の 4g ネットワークカードは GNU/Linux を実行しています -- ある 4g 無線ネットワークカードの逆向きエンジニアリングとメインライン Linux 移植 (一)_fatal error: blob has incorrect magic number-CSDN ブログ

9008 を利用した書き込み / バックアップ単一パーティション - 哔哩哔哩

高通チップの起動プロセス - asges 林 - ブログ園

デバイスフラッシュに必要なソフトウェア | 宁宁's Blog

Android デバイスの起動ログ分析_Android 起動ログのマウント - CSDN ブログ

転載 - Qualcomm MSM8953 起動プロセス:PBL-SBL1-(bootloader) LK-Android - liangliangge - ブログ園

Android ブートローダー分析 —— Aboot - 知乎

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。