USBフラッシュメモリの挿入を検知する

いまだに驚異になっているリムーバブルメディアで感染を広げる「MAL_OTORUN」。トレンドマイクロの3月インターネット脅威リポートにおいても8か月連続で被害報告数ベスト(ワースト?)1。大流行のConfickerもリムーバルメディア経由での感染機能を持っている。

マルウェアの実行はautorun.infを用いて自動実行するのは周知のこと。では、感染活動のほうに目を向けるとどうなのか。つまり、USBフラッシュに自分自身をコピーするのはどうやってるんだろう。プログラム側でUSBフラッシュが接続されたことを認識する必要がある。

実はこれは至って簡単で、Windowsは、USBフラッシュメモリの挿入を含めHWデバイスに変更が発生すると、すべてのアプリケーションに対して、「WM_DEVICECHANGE」メッセージを送信する。プログラム側でイベントハンドラを定義してこのメッセージを受け取った時の処理を記述すればいい(ファイルをコピーするとか)。

やってみた。
ダイアログベースのアプリケーション(DetectUSBDrive)を作成。直接処理とは関係ないがワームっぽくするためにダイアログ自体を非表示にする。

これは OnWindowPosChanging() をオーバーライドして、以下のようにとするだけでいい。これでプログラムを実行してもウィンドウは画面上に表示されない。

void CDetectUSBDriveDlg::OnWindowPosChanging(WINDOWPOS * lpwndpos)
{
    lpwndpos->flags &= ~SWP_SHOWWINDOW;
    CDialog::OnWindowPosChanging(lpwndpos);
}


WM_DEVICECHANGEメッセージ処理関数(今回はOnMyDeviceChange)をメッセージマップを追加。ヘッダファイルにもオーバーライド定義。


BEGIN_MESSAGE_MAP(CDetectUSBDriveDlg, CDialog)
	ON_MESSAGE(WM_DEVICECHANGE, OnMyDeviceChange)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_WINDOWPOSCHANGING()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


Dbt.hのincludeを忘れずに。


#include <Dbt.h>

メッセージ処理関数 OnMyDeviceChange() の本体について、単にHWデバイスが追加されたことだけ拾えればいいのであれば以下のようになる。

LRESULT CDetectUSBDriveDlg::OnMyDeviceChange(WPARAM wParam, LPARAM lParam)
{
         CString mes = _T("");
	switch(wParam)
	{
	case	DBT_DEVICEARRIVAL:
		mes = _T("デバイスが使用可能になりました");
		break;
         }

         AfxMessageBox(mes);

         return 0;
}

ここまでで、とりあえずプログラムを実行してUSBフラッシュを差してみた。ちゃんと検出した。



ちなみにイベントの種類はほかにもあって、この場合、wParamには以下のような値が入ってくる。

DBT_DEVICEARRIVAL デバイスが挿入されていて、使用できる状態です. 
DBT_DEVICEQUERYREMOVE デバイスを削除するための許可が要求されています.
DBT_DEVICEQUERYREMOVEFAILED デバイスを削除する要求がキャンセルされました.
DBT_DEVICEREMOVEPENDING デバイスが削除されようとしています.拒否できません.
DBT_DEVICEREMOVECOMPLETE デバイスが削除されました.
DBT_DEVICETYPESPECIFIC デバイス固有のイベントです.
DBT_CONFIGCHANGED 現在の設定が変更されました.
DBT_DEVNODES_CHANGED デバイス ノードが変更されました.


Device Management Events
http://msdn.microsoft.com/en-us/library/aa363232(VS.85).aspx


検知できることは分かったがそのほかの詳細情報は取得できないものか。ファイルをコピーするんだったら、少なくともマッピングされたドライブ文字くらいは知りたい。
実はこれらの情報 DEV_BROADCAST_USERDEFINED 構造体を辿るとことで分かる。そしてイベント処理関数に渡ってくるlParamにはこの構造体のポインタが格納されている。


DEV_BROADCAST_HDR Structure
http://msdn.microsoft.com/en-us/library/aa363246(VS.85).aspx


そしてこの構造体の dbch_devicetype にはイベント特有の情報が含まれていて、今回の場合ドライブ文字を取得したいので、dbch_devicetype が DBT_DEVTYP_VOLUME であるイベントを取得する。こんな感じ。
で、DEV_BROADCAST_VOLUME構造体 の dbcv_unitmask にドライブ文字を指定する情報が格納されている。これはDWORD型で0bit目が立っていれば「A」1ビット目が立っていれば「B」.....ということ。

DEV_BROADCAST_HDR* pHdr = (DEV_BROADCAST_HDR*)lParam;
switch(pHdr->dbch_devicetype)
{
 case DBT_DEVTYP_VOLUME:
 {
   DEV_BROADCAST_VOLUME *pVol = (DEV_BROADCAST_VOLUME*)pHdr;
   if(pVol)
   {
         CString str;
         str.Format(_T(":%d"), pExtend->dbcv_unitmask);
         mes += str;
      }
   }
   break;
}


まとめると、ものすごく簡単だが、デバイスの挿入を検知してマップされた論理ドライブ文字を取得するためだけの機能を持った OnMyDeviceChange() は以下のようになる。

LRESULT CDetectUSBDriveDlg::OnMyDeviceChange(WPARAM wParam, LPARAM lParam)
{
   if(wParam == DBT_DEVICEARRIVAL)
   {
      DEV_BROADCAST_HDR* pHdr = (DEV_BROADCAST_HDR*)lParam;
      if(pHdr)
      {
        if(pHdr->dbch_devicetype == DBT_DEVTYP_VOLUME)
        {
           DEV_BROADCAST_VOLUME* pVol = (DEV_BROADCAST_VOLUME*)pHdr;
           
           if(pVol)
           {
              CString mes;
              mes.Format(_T("Detect->[%ld]"), pVol->dbcv_unitmask);
              AfxMessageBox(mes);
           }
        }
      }
    }
    return 0;
}


実行してみた。



4096 は 2の12乗なので「M」を表す。実際にMドライブにマップされている。



ちゃんちゃん。特にまとめはない。だからなんだ?って感じになってしまった。

スパムでばらまいたりWebにホスティングしたりすることに比べて、一気に爆発的に増殖数を増やすことはできなさそうだけど、増殖方法としてはお手軽でいいかもね。