クイック スタート: Python を使用して Defender for IoT Firmware Analysis にファームウェア イメージをアップロードする

この記事では、Python を使用してファームウェア イメージを Defender for IoT Firmware Analysis にアップロードする方法について説明します。

Defender for IoT Firmware Analysis は、ファームウェア イメージを分析し、ファームウェア イメージのセキュリティの脆弱性を理解できるようにするツールです。

前提条件

このクイック スタートでは、Defender for IoT Firmware Analysis の基本的な理解ができていることを前提としています。 詳細については、「デバイス ビルダーのファームウェア分析」を参照してください。 サポートされているファイル システムの一覧については、「Defender for IoT ファームウェア分析についてよく寄せられる質問」を参照してください。

環境を準備する

  • このパッケージを使用するには、Python バージョン 3.8 以降が必要です。 Python のバージョンを確認するには、コマンド python --version を実行します。
  • Azure サブスクリプション ID、イメージをアップロードするリソース グループの名前、ワークスペース名、アップロードするファームウェア イメージの名前をメモします。
  • Azure アカウントに、Azure サブスクリプションのファームウェア イメージを Defender for IoT Firmware Analysis にアップロードするために必要なアクセス許可があることを確認します。 ファームウェア イメージをアップロードするには、サブスクリプションまたはリソース グループのレベルで所有者、共同作成者、セキュリティ管理者、またはファームウェア分析管理者である必要があります。 詳しくは、「Defender for IoT Firmware Analysis のロール、スコープ、機能」を参照してください。
  • ファームウェア イメージが Python スクリプトと同じディレクトリに保存されていることを確認します。
  • このスクリプトを実行するために必要なパッケージをインストールします。
    pip install azure-mgmt-iotfirmwaredefense
    pip install azure-identity
    
  • az login コマンドを実行して Azure アカウントにログインします。

次の Python スクリプトを実行します

次の Python スクリプトを .py ファイルにコピーし、ファームウェア イメージと同じディレクトリに保存します。 subscription_id 変数を Azure サブスクリプション ID に、resource_group_name をファームウェア イメージをアップロードするリソース グループの名前に、firmware_file を Python スクリプトと同じディレクトリに保存されているファームウェア イメージの名前に置き換えます。

from azure.identity import AzureCliCredential
from azure.mgmt.iotfirmwaredefense import *
from azure.mgmt.iotfirmwaredefense.models import *
from azure.core.exceptions import *
from azure.storage.blob import BlobClient
import uuid
from time import sleep
from halo import Halo
from tabulate import tabulate

subscription_id = "subscription-id"
resource_group_name = "resource-group-name"
workspace_name = "default"
firmware_file = "firmware-image-name"

def main():
    firmware_id = str(uuid.uuid4())
    fw_client = init_connections(firmware_id)
    upload_firmware(fw_client, firmware_id)
    get_results(fw_client, firmware_id)

def init_connections(firmware_id):
    spinner = Halo(text=f"Creating client for firmware {firmware_id}")
    cli_credential = AzureCliCredential()
    client = IoTFirmwareDefenseMgmtClient(cli_credential, subscription_id, 'https://management.azure.com')
    spinner.succeed()
    return client

def upload_firmware(fw_client, firmware_id):
    spinner = Halo(text="Uploading firmware to Azure...", spinner="dots")
    spinner.start()
    token = fw_client.workspaces.generate_upload_url(resource_group_name, workspace_name, {"firmware_id": firmware_id})
    fw_client.firmwares.create(resource_group_name, workspace_name, firmware_id, {"properties": {"file_name": firmware_file, "vendor": "Contoso Ltd.", "model": "Wifi Router", "version": "1.0.1", "status": "Pending"}})
    bl_client = BlobClient.from_blob_url(token.url)
    with open(file=firmware_file, mode="rb") as data:
        bl_client.upload_blob(data=data)
    spinner.succeed()

def get_results(fw_client, firmware_id):
    fw = fw_client.firmwares.get(resource_group_name, workspace_name, firmware_id)

    spinner = Halo("Waiting for analysis to finish...", spinner="dots")
    spinner.start()
    while fw.properties.status != "Ready":
        sleep(5)
        fw = fw_client.firmwares.get(resource_group_name, workspace_name, firmware_id)
    spinner.succeed()

    print("-"*107)

    summary = fw_client.summaries.get(resource_group_name, workspace_name, firmware_id, summary_name=SummaryName.FIRMWARE)
    print_summary(summary.properties)
    print()

    components = fw_client.sbom_components.list_by_firmware(resource_group_name, workspace_name, firmware_id)
    if components is not None:
        print_components(components)
    else:
        print("No components found")

def print_summary(summary):
    table = [[summary.extracted_size, summary.file_size, summary.extracted_file_count, summary.component_count, summary.binary_count, summary.analysis_time_seconds, summary.root_file_systems]]
    header = ["Extracted Size", "File Size", "Extracted Files", "Components", "Binaries", "Analysis Time", "File Systems"]
    print(tabulate(table, header))

def print_components(components):
    table = []
    header = ["Component", "Version", "License", "Paths"]
    for com in components:
        table.append([com.properties.component_name, com.properties.version, com.properties.license, com.properties.file_paths])
    print(tabulate(table, header, maxcolwidths=[None, None, None, 57]))

if __name__ == "__main__":
    exit(main())