はじめに
AMSI(Antimalware Scan Interface)と呼ばれるWindowsの機能のうち、マルウェアスキャン用の関数について少し調べてみました。
使用したOSについて
今回使用したOSは下記のとおりです。
- Windows 10 バージョン1909 OSビルド 18363.752
AMSIについて
AMSIは、各種アプリケーションやサービスとマルウェア対策プログラムを統合するインターフェースで、ユーザーのデータやアプリケーションなどをマルウェアから保護するための機能を提供します。
Windows 10の場合、AMSIは下記のコンポーネントと統合されています。
- ユーザーアカウント制御(UAC)
- PowerShell
- Windows Script Host
- JavaScriptおよびVBScript
- Office VBAマクロ
たとえばPowerShellコンソール上で文字列を表示させたとき、その文字列が悪質なものであると判断された場合にエラーメッセージが表示されるのはAMSIが機能しているためです。
また、下図のように上記コンポーネント以外のアプリケーションからもAMSIを利用できます。 この図によると、PowerShellはAMSI関連のライブラリーとしてamsi.dllを利用していることが分かります。
PowerShellでのamsi.dll内関数の呼び出しについて
PowerShellの利用時にどのようなamsi.dll内関数が呼ばれているのかを調べてみます。
API Monitorを使ってpowershell.exeを起動すると、先ほどの図にあったとおりamsi.dllをロードしていることが分かりました。
powershell.exeから呼び出されるamsi.dll内の関数をAPI Monitorで調べてみたところ、powershellコンソール上で文字列を表示させたときに「AmsiOpenSession()」、「AmsiScanBuffer()」、「AmsiCloseSession()」といった3つの関数が呼ばれていることが分かりました。
各関数の引数を見てみると、powershellコンソールで表示した文字列(ここでは「echo "hoge"」)は関数AmsiScanBuffer()に渡されていることが分かりました。
AmsiScanBuffer()について
仕様
関数AmsiScanBuffer()は、引数として渡されたバッファの内容に対してそれがマルウェアであるかどうかをスキャンするもので、下記のような仕様になっています。 詳細な説明は引用元を参照してください。
戻り値
当該関数の戻り値はHRESULT型の値です。S_OK(0x0)をはじめ、HRESULT型の詳細な説明はCommon HRESULT Valuesに記載されています。
引数をWinDbgで見てみる
Microsoft社が提供しているデバッガーWinDbgを使って、関数AmsiScanBuffer()での引数の受け渡しを見てみます。
上図において、関数AmsiScanBuffer()の引数は下記のとおりです。スタックを使用しているものについては関数の開始時のアドレス(オフセット)にしています。(参照先:x64 calling convention)
- HAMSICONTEXT amsiContext:rcx
- PVOID buffer:rdx
- ULONG length:r8
- LPCWSTR contentName:r9
- HAMSISESSION amsiSession:rsp + 0x28(rsp + 0xb0 -0x88)
- AMSI_RESULT *result:rsp + 0x30(rsp + 0xb8 -0x88)
引数resultについて
関数AmsiScanBuffer()の引数のうち、スキャン結果はresultに格納されます。 引数resultはAMSI_RESULT型のポインターで、AMSI_RESULT型は下記のように定義されています。 詳細な説明は引用元を参照してください。
上記の各要素の値はSecuritySupport.csに定義されており、具体的な値は下記のとおりです。 (AMSI_RESULT_BLOCKED_BY_ADMIN_STARTとAMSI_RESULT_BLOCKED_BY_ADMIN_BEGINについて、名称は異なるものの、定義されている値は同じのようです)
- AMSI_RESULT_CLEAN:0x0
- AMSI_RESULT_NOT_DETECTED:0x1
- AMSI_RESULT_BLOCKED_BY_ADMIN_BEGIN:0x4000(16384)
- AMSI_RESULT_BLOCKED_BY_ADMIN_END:0x4fff(20479)
- AMSI_RESULT_DETECTED:0x8000(32768)
なお詳細は省きますが、少し試してみたところ、resultの参照先の値がAMSI_RESULT_BLOCKED_BY_ADMIN_BEGIN(0x4000)以上でAMSI_RESULT_BLOCKED_BY_ADMIN_END(0x4fff)以下の場合に「ポリシー設定による不審なコンテンツ」と判断され、AMSI_RESULT_DETECTED(0x8000)の場合に「悪質なコンテンツ」と判断されました。
resultの参照先の値について
関数AmsiScanBuffer()について、無害な文字列と、有害と疑われる文字列それぞれをPowerShellコンソール上に表示させようとしたときの、AMSI_RESULT型のポインターである引数resultの参照先の値を取得してみます。 値はWinDbg上で取得し、取得のタイミングは関数の開始時と終了時(ret直前)としました。
取得結果
resultの参照先の値の取得結果は下記のとおりです。
無害な文字列を表示
- 関数の開始時:0x0
- 関数の終了時:0x1
有害と疑われる文字列を表示
- 関数の開始時:0x0
- 関数の終了時:0x8000(32768)
関数終了時のresultの参照先の値はPowerShellコンソール上に表示させようとする文字列によって変わりますが、開始時の値は文字列に関係なく初期値の0x0(AMSI_RESULT_CLEAN)です。 このことから、関数の先頭部分を「mov eax, 0x0; ret;」に書き換えた場合、戻り値(eax)がS_OK(0x0)でresultの参照先の値がAMSI_RESULT_CLEAN(0x0)の状態で関数の呼び出し元に返ることになります。 この書き換え手法はAMSI Bypassテクニックの1つとして知られています。(参照先:AMSI Bypass)
おわりに
今回は、AMSIでのマルウェアスキャン用の関数であるAmsiScanBuffer()の一部の引数について、WinDbgを使用して確認しました。 同様のことはFridaなど他のツールでも実施可能ですが、手段を複数持っておいてもいいんじゃないかと思った次第です。
参照先


記事の著者
セキュリティ診断業務に10年間従事後、2016年よりシニアセキュリティアナリストとしてペネトレーションテスト業務に従事。
診断業務開始当初は社内イントラサイトの脆弱性に対する過激な指摘により物議を醸したが、
現在はだいぶおとなしくなっている。好きな周波数は13.56MHz。
GXPN、GAWN、GREM、GPEN、GDAT、GCPN、GCFA、GCFE、CRTE、CARTP、CEH
関連記事
RELATED ARTICLE