以下の内容はhttps://daisuke20240310.hatenablog.com/entry/libusbより取得しました。


CodeQL(静的解析ツール)の挙動を確認するための対象ソースコードとしてlibusbを調査する

前回 は、CERT C というセキュアコーディングについて調べてみました。

CodeQL の挙動を理解するに、Tinyhttpd を使ってきましたが、この OSS は、規模が小さいので良い面もありましたが、クエリの条件に当てはまるコードが無い場合が出てきました。そこで、新しく、対象の OSS として、libusb も使っていきたいと思います。

libusb は、とても有名な OSS で、USB を扱う OSS が採用してる場合が多いです。apt でパッケージを入れたときに、一緒に入ったりしてると思います。libusb は、USBデバイスへの汎用的なアクセスを提供してくれる Cソースライブラリです。USB の規格は膨大な量であり、USBデバイスにアクセスすることも結構な労力が必要になります。そこの役割をしてくれるのが libusb です。

それでは、やっていきます。

はじめに

「セキュリティ」の記事一覧です。良かったら参考にしてください。

セキュリティの記事一覧
・第1回:Ghidraで始めるリバースエンジニアリング(環境構築編)
・第2回:Ghidraで始めるリバースエンジニアリング(使い方編)
・第3回:VirtualBoxにParrotOS(OVA)をインストールする
・第4回:tcpdumpを理解して出力を正しく見れるようにする
・第5回:nginx(エンジンエックス)を理解する
・第6回:Python+Flask(WSGI+Werkzeug+Jinja2)を動かしてみる
・第7回:Python+FlaskのファイルをCython化してみる
・第8回:shadowファイルを理解してパスワードを解読してみる
・第9回:安全なWebアプリケーションの作り方(徳丸本)の環境構築
・第10回:Vue.jsの2.xと3.xをVue CLIを使って動かしてみる(ビルドも行う)
・第11回:Vue.jsのソースコードを確認する(ビルド後のソースも見てみる)
・第12回:徳丸本:OWASP ZAPの自動脆弱性スキャンをやってみる
・第13回:徳丸本:セッション管理を理解してセッションID漏洩で成りすましを試す
・第14回:OWASP ZAPの自動スキャン結果の分析と対策:パストラバーサル
・第15回:OWASP ZAPの自動スキャン結果の分析と対策:クロスサイトスクリプティング(XSS)
・第16回:OWASP ZAPの自動スキャン結果の分析と対策:SQLインジェクション
・第17回:OWASP ZAPの自動スキャン結果の分析と対策:オープンリダイレクト
・第18回:OWASP ZAPの自動スキャン結果の分析と対策:リスク中すべて
・第19回:CTF初心者向けのCpawCTFをやってみた
・第20回:hashcatの使い方(GPU実行時間の見積りとパスワード付きZIPファイル)
・第21回:Scapyの環境構築とネットワークプログラミング
・第22回:CpawCTF2にチャレンジします(クリア状況は随時更新します)
・第23回:K&Rのmalloc関数とfree関数を理解する
・第24回:C言語、アセンブラでシェルを起動するプログラムを作る(ARM64)
・第25回:機械語でシェルを起動するプログラムを作る(ARM64)
・第26回:入門セキュリhttps://github.com/SECCON/SECCON2017_online_CTF.gitティコンテスト(CTFを解きながら学ぶ実践技術)を読んだ
・第27回:x86-64 ELF(Linux)のアセンブラをGDBでデバッグしながら理解する(GDBコマンド、関連ツールもまとめておく)
・第28回:入門セキュリティコンテスト(CTFを解きながら学ぶ実践技術)のPwnable問題をやってみる
・第29回:実行ファイルのセキュリティ機構を調べるツール「checksec」のまとめ
・第30回:setodaNote CTF Exhibitionにチャレンジします(クリア状況は随時更新します)
・第31回:常設CTFのksnctfにチャレンジします(クリア状況は随時更新します)
・第32回:セキュリティコンテストチャレンジブックの「Part2 pwn」を読んだ
・第33回:セキュリティコンテストチャレンジブックの「付録」を読んでx86とx64のシェルコードを作った
・第34回:TryHackMeを始めてみたけどハードルが高かった話
・第35回:picoCTFを始めてみた(Beginner picoMini 2022:全13問完了)
・第36回:picoCTF 2024:Binary Exploitationの全10問をやってみた(Hardの1問は後日やります)
・第37回:picoCTF 2024:Reverse Engineeringの全7問をやってみた(Windowsプログラムの3問は後日やります)
・第38回:picoCTF 2024:General Skillsの全10問をやってみた
・第39回:picoCTF 2024:Web Exploitationの全6問をやってみた(最後の2問は解けず)
・第40回:picoCTF 2024:Forensicsの全8問をやってみた(最後の2問は解けず)
・第41回:picoCTF 2024:Cryptographyの全5問をやってみた(最後の2問は手つかず)
・第42回:picoCTF 2023:General Skillsの全6問をやってみた
・第43回:picoCTF 2023:Reverse Engineeringの全9問をやってみた
・第44回:picoCTF 2023:Binary Exploitationの全7問をやってみた(最後の1問は後日やります)
・第45回:書籍「セキュリティコンテストのためのCTF問題集」を読んだ
・第46回:書籍「詳解セキュリティコンテスト」のReversingを読んだ
・第47回:書籍「詳解セキュリティコンテスト」のPwnableのシェルコードを読んだ
・第48回:書籍「バイナリファイル解析 実践ガイド」を読んだ
・第49回:書籍「詳解セキュリティコンテスト」Pwnableのスタックベースエクスプロイトを読んだ
・第50回:書籍「詳解セキュリティコンテスト」Pwnableの共有ライブラリと関数呼び出しを読んだ
・第51回:picoCTF 2025:General Skillsの全5問をやってみた
・第52回:picoCTF 2025:Reverse Engineeringの全7問をやってみた
・第53回:picoCTF 2025:Binary Exploitationの全6問をやってみた
・第54回:書籍「詳解セキュリティコンテスト」Pwnableの仕様に起因する脆弱性を読んだ
・第55回:システムにインストールされたものと異なるバージョンのglibcを使う方法
・第56回:書籍「詳解セキュリティコンテスト」Pwnableのヒープベースエクスプロイトを読んだ
・第57回:書籍「解題pwnable」の第1章「準備」を読んだ
・第58回:書籍「解題pwnable」の第2章「login1(スタックバッファオーバーフロー1)」を読んだ
・第59回:書籍「解題pwnable」の第3章「login2(スタックバッファオーバーフロー2)」を読んだ
・第60回:書籍「解題pwnable」の第4章「login3(スタックバッファオーバーフロー3)」を読んだ
・第61回:書籍「解題pwnable」の第5章「rot13(書式文字列攻撃)」を読んだ
・第62回:GitHubが開発した静的解析ツール(脆弱性検出ツール)のCodeQLを使ってみる
・第63回:CodeQL(静的解析ツール)で使われるクエリの選ばれ方を調べた
・第64回:CodeQL(静的解析ツール)のクエリの書き方を調べた
・第65回:CodeQL(静的解析ツール)で使われているアラートクエリの中身を調べる
・第66回:CodeQL(静的解析ツール)で使われているパスクエリの中身を調べる
・第67回:CodeQL(静的解析ツール)をVSCodeで使う方法を理解する
・第68回:CodeQL(静的解析ツール)の挙動を確認するための対象ソースコードとしてTinyhttpdを調査する
・第69回:Tinyhttpdを使用してCodeQL(静的解析ツール)のクエリの挙動を確認する
・第70回:セキュアコーディング:CERT C INT02-C 「整数変換のルールを理解する」を調べる
・第71回:CodeQL(静的解析ツール)の挙動を確認するための対象ソースコードとしてlibusbを調査する ← 今回

現在の実行環境は、Windows11 に、WSL2 で、Ubuntu 24.04 を入れた環境です。

libusb の GitHub は以下です。

github.com

README.md に書かれてますが、libusb のホームページは以下です。

libusb.info

また、開発者向けの APIリファレンスは以下です。

libusb.sourceforge.io

以前、USB について簡単に調べた記事を書きました。

daisuke20240310.hatenablog.com

libusbの環境構築

以下のように調べてみると、私の環境では、既に libusb をインストールしていたようです。

$ dpkg -l | grep libusb
ii  libusb-1.0-0:amd64              2:1.0.27-1                              amd64        userspace USB programming library
ii  libusb-1.0-0-dev:amd64          2:1.0.27-1                              amd64        userspace USB programming library development files
ii  libusb-1.0-doc                  2:1.0.27-1                              all          documentation for userspace USB programming

2:1.0.27-1 と表示されているのは、ChatGPT によると、以下の意味とのことです。

対象 意味
2: Debian オーバーレイの Epoch
1.0.27 upstream(libusb のバージョン)
-1 Debian/Ubuntu でのパッケージリビジョン

libusb のバージョンが特定できたので、GitHub からソースコードをダウンロードします。同じバージョンの方が安定してそうですし、動かしたときに不都合が起きにくいと思います。

よって、1.0.27 を取得します。一応、既にインストールされている libusb のヘッダと差分が無いかを確認したところ、一致しました。

$ git clone https://github.com/libusb/libusb.git

$ cd libusb/

$ git checkout -b v1.0.27 refs/tags/v1.0.27

$ diff -upr libusb/libusb.h /usr/include/libusb-1.0/libusb.h

ビルド方法は、以下の wiki に書かれていました。

github.com

これに従って、ビルドします。

ビルドされたソースファイルを確認したくなるかもしれないので、make のログだけ、一部貼っておきます。libusb の本体と、examples と tests がビルドされたようです。

$ ./autogen.sh

$ ./bootstrap.sh

$ make
make  all-recursive
make[1]: Entering directory '/home/ubuntu/svn/oss/libusb/libusb'
Making all in libusb
make[2]: Entering directory '/home/ubuntu/svn/oss/libusb/libusb/libusb'
  CC       core.lo
  CC       descriptor.lo
  CC       hotplug.lo
  CC       io.lo
  CC       strerror.lo
  CC       sync.lo
  CC       os/events_posix.lo
  CC       os/threads_posix.lo
  CC       os/linux_usbfs.lo
  CC       os/linux_udev.lo
  CCLD     libusb-1.0.la
make[2]: Leaving directory '/home/ubuntu/svn/oss/libusb/libusb/libusb'
Making all in examples
make[2]: Entering directory '/home/ubuntu/svn/oss/libusb/libusb/examples'
  CC       dpfp.o
  CCLD     dpfp
  CC       dpfp_threaded-dpfp.o
  CCLD     dpfp_threaded
  CC       ezusb.o
  CC       fxload.o
  CCLD     fxload
  CC       hotplugtest.o
  CCLD     hotplugtest
  CC       listdevs.o
  CCLD     listdevs
  CC       sam3u_benchmark.o
  CCLD     sam3u_benchmark
  CC       testlibusb.o
  CCLD     testlibusb
  CC       xusb.o
  CCLD     xusb
make[2]: Leaving directory '/home/ubuntu/svn/oss/libusb/libusb/examples'
Making all in tests
make[2]: Entering directory '/home/ubuntu/svn/oss/libusb/libusb/tests'
  CC       stress.o
  CC       testlib.o
  CCLD     stress
  CC       stress_mt-stress_mt.o
  CCLD     stress_mt
  CC       set_option.o
  CCLD     set_option
  CC       init_context.o
  CCLD     init_context
make[2]: Leaving directory '/home/ubuntu/svn/oss/libusb/libusb/tests'
make[2]: Entering directory '/home/ubuntu/svn/oss/libusb/libusb'
make[2]: Leaving directory '/home/ubuntu/svn/oss/libusb/libusb'
make[1]: Leaving directory '/home/ubuntu/svn/oss/libusb/libusb'

これで libusb を動作させる準備はできました。私の環境は WSL なので、Windows側で認識している USBデバイスを WSL で認識できるようにする必要があります。以下の記事に、WSL で USBデバイスを認識させる方法を追記しました。この方法で、USBメモリを WSL側で認識できるようにしました。

daisuke20240310.hatenablog.com

それでは、USBデバイスの一覧を表示するサンプルアプリを動かしてみます。まず、パッケージでインストールされている libusb ではなく、今回ビルドした libusb が使われることを確認します。

以下の実行方法であれば、ビルドした libusb が使われることが確認できました。

$ LD_LIBRARY_PATH=libusb/.libs/ ldd examples/.libs/listdevs
        linux-vdso.so.1 (0x00007fff49f33000)
        libusb-1.0.so.0 => libusb/.libs/libusb-1.0.so.0 (0x00007ceb763ae000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ceb76000000)
        libudev.so.1 => /lib/x86_64-linux-gnu/libudev.so.1 (0x00007ceb76374000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ceb763d5000)
        libcap.so.2 => /lib/x86_64-linux-gnu/libcap.so.2 (0x00007ceb76367000)

では、listdevs を実行してみます。

事前に lsusb で期待する結果を確認しました。listdevs でも正しく認識できているようです。ちなみに、以下では 3つの USBデバイスが認識されていますが、USBメモリを WSL側に認識させる前の状態では、何も出力されませんでした。

$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 003: ID 346d:5678 Biwin USB Device
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub

$ LD_LIBRARY_PATH=libusb/.libs/ examples/.libs/listdevs
1d6b:0003 (bus 2, device 1)
346d:5678 (bus 1, device 3) path: 1
1d6b:0002 (bus 1, device 1)

libusb の環境構築は以上です。

libusbに対してCodeQLを実行する

早速、CodeQL を実行していきます。

Tinyhttpd のときと同様に、デフォルトのクエリスイート「cpp-code-scanning.qls」は、あまり指摘が出ないため、「cpp-security-and-quality.qls」を使います。

CodeQL のデータベースを作る際、CodeQL はビルドを実行するので、実行する前に、libusb を make clean しておく必要があります。

(libusb のディレクトリで以下を実行)
$ make clean

(以降は、CodeQL v2.22.0-bundle を置いたディレクトリで実行)
$ codeql/codeql database create db/libusb -l c-cpp -s ../../oss/libusb/libusb

$ codeql/codeql database analyze db/libusb cpp-security-and-quality.qls --format csv --output csv/libusb_cppsq.csv -M 6000

warning が 5件、recommendation が 5件でした。warning だけ確認するので、以下は、warning のみです。

検出したクエリ名 クエリの説明 重要度 警告メッセージ パス 開始行 開始列 終了行 終了列
Comparison result is always the same When a comparison operation, such as x < y, always returns the same result, it means that the comparison is redundant and may mask a bug because a different check was intended. warning Comparison is always false because iso_packets >= 0. /libusb/io.c 1293 6 1293 20
Local variable address stored in non-local memory Storing the address of a local variable in non-local memory can cause a dangling pointer bug if the address is used after the function returns. warning A stack address which arrived via a [["parameter"|"relative:///libusb/libusb.h:1852:24:1852:32"]] may be assigned to a non-local variable. /libusb/libusb.h 1852 2 1852 32
Local variable address stored in non-local memory Storing the address of a local variable in non-local memory can cause a dangling pointer bug if the address is used after the function returns. warning A stack address which arrived via a [["parameter"|"relative:///libusb/libusb.h:1824:24:1824:32"]] may be assigned to a non-local variable. /libusb/libusb.h 1824 2 1824 32
Local variable address stored in non-local memory Storing the address of a local variable in non-local memory can cause a dangling pointer bug if the address is used after the function returns. warning A stack address which arrived via a [["parameter"|"relative:///libusb/libusbi.h:235:21:235:24"]] may be assigned to a non-local variable. /libusb/libusbi.h 235 2 235 24
Local variable address stored in non-local memory Storing the address of a local variable in non-local memory can cause a dangling pointer bug if the address is used after the function returns. warning A stack address which arrived via a [["parameter"|"relative:///libusb/libusbi.h:237:21:237:24"]] may be assigned to a non-local variable. /libusb/libusbi.h 237 2 237 24

1つ目の warning は以下のソースコードです。Ghidra で見たところ、assert文は有効な行でした。この警告は、その次の if文に対してであり、assert文で iso_packets は 0以上なので、この if文は冗長なチェックとなります。それは何か不具合があるのかも、という警告でした。このケースでは、assert文がコンパイルで無効化されたときのための if文だと思うので、問題ないと思います。

DEFAULT_VISIBILITY
struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(int iso_packets)
{
    assert(iso_packets >= 0);
    if (iso_packets < 0)
        return NULL;

2番目以降は、同じ指摘内容が 4つ続きます。

以下は、該当箇所のソースコードです。

user_data を代入してる行が指摘されています。スタックを指すポインタが代入された場合、スコープが外れると、正しく値を参照できない、というよくある警告です。他のポインタが指摘されていない理由はよく分かっていません。ChatGPT に聞くと、user_data には libusb を使うアプリ?のデータが入るので、ユーザ責任になるので、libusb としては問題ないということでした。

static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer,
    libusb_device_handle *dev_handle, unsigned char endpoint,
    unsigned char *buffer, int length, libusb_transfer_cb_fn callback,
    void *user_data, unsigned int timeout)
{
    transfer->dev_handle = dev_handle;
    transfer->endpoint = endpoint;
    transfer->type = LIBUSB_TRANSFER_TYPE_BULK;
    transfer->timeout = timeout;
    transfer->buffer = buffer;
    transfer->length = length;
    transfer->user_data = user_data;
    transfer->callback = callback;
}

残り、3つも同じような感じなので割愛します。

サンプルアプリ(examples)を調査する

サンプルアプリは下表のものがあるようです(from ChatGPT)。

アプリ 機能
dpfp USBデバイスからのデータを非同期で受信する最小例
dpfp_threaded dpfp のイベント処理を別スレッドに分離した版
fxload USBデバイスへのファームウェア転送
hotplugtest USB 抜き差し検知
listdevs USBデバイスの一覧表示(lsusb)
sam3u_benchmark USB 転送性能測定
testlibusb 基本の API テスト
xusb USBデバイス構造の詳細表示(lsusb -t)

必要に応じて、各アプリの内容を確認したいと思います。

サンプルアプリ:examples/listdevs

examples/listdevs.c を見ていきます。

まず、main関数です。

libusb_XXX となっているのは、libusb の API です。

引数は無いようです。

libusb の初期化(libusb_init_context)を行い、接続されている USBデバイスを探索(libusb_get_device_list)しています。あとは、次に確認する print_devs関数を実行して、終了処理をしています。

int main(void)
{
    libusb_device **devs;
    int r;
    ssize_t cnt;

    r = libusb_init_context(/*ctx=*/NULL, /*options=*/NULL, /*num_options=*/0);
    if (r < 0)
        return r;

    cnt = libusb_get_device_list(NULL, &devs);
    if (cnt < 0){
        libusb_exit(NULL);
        return (int) cnt;
    }

    print_devs(devs);
    libusb_free_device_list(devs, 1);

    libusb_exit(NULL);
    return 0;
}

取得したデバイスリストに対して、whileループですべて確認していきます。libusb_get_device_descriptor でディスクリプタを取得し、VenderID、ProductID を取得します。また、libusb_get_bus_number で、バス番号を取得し、libusb_get_device_address で、デバイスアドレスを取得し、これらを表示しています。

また、libusb_get_port_numbers で、ポート番号を取得し、path として表示しています。

static void print_devs(libusb_device **devs)
{
    libusb_device *dev;
    int i = 0, j = 0;
    uint8_t path[8]; 

    while ((dev = devs[i++]) != NULL) {
        struct libusb_device_descriptor desc;
        int r = libusb_get_device_descriptor(dev, &desc);
        if (r < 0) {
            fprintf(stderr, "failed to get device descriptor");
            return;
        }

        printf("%04x:%04x (bus %d, device %d)",
            desc.idVendor, desc.idProduct,
            libusb_get_bus_number(dev), libusb_get_device_address(dev));

        r = libusb_get_port_numbers(dev, path, sizeof(path));
        if (r > 0) {
            printf(" path: %d", path[0]);
            for (j = 1; j < r; j++)
                printf(".%d", path[j]);
        }
        printf("\n");
    }
}

listdevs は、libusb の API をひたすら実行しているだけでした。

おわりに

今回は、GitHub に公開されている OSS の libusb について調査しました。今後は、Tinyhttpd とともに、libusb を使って、CodeQL の挙動の確認を進めていきたいと思います。その際、libusb の中身を見ていくことも必要になると思います。そのときは、この記事を更新していこうと思います。

最後になりましたが、エンジニアグループのランキングに参加中です。

気楽にポチッとよろしくお願いいたします🙇

今回は以上です!

最後までお読みいただき、ありがとうございました。




以上の内容はhttps://daisuke20240310.hatenablog.com/entry/libusbより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14