[VBA]AvastがVBAから呼び出したCopyMemory(WinAPI)を監視しまくる件
2022年3月20日
事の発端
先日、とあるExcel VBAの速度が、期待したより遅い事象が発生しました。どこで遅くなっているか調査した結果、原因はWindows APIのCopyMemory(RtlMoveMemory)でした。VBAの標準機能ではポインタによるデータ操作ができないVBAにとって、RtlMoveMemoryが遅いのは死活問題です(ポインタが必要なら別言語でやれというツッコミは無しで)。しかし、昔この関数を使っていて、そこまで遅かった記憶が無かったことや、そもそも単なるメモリコピーが何で遅くなるのか疑問に思ったため、根本原因を調査してみました。なお、根本原因はタイトルにある通り、Avast Antivirus(Var.20.4)でした。
調査結果
とあるExcel VBAで負荷テストを実施していた所、ある自作関数を1回実行するのに約10ミリ秒掛かっていました。この関数は連続で100~1000回くらい呼び出されることを想定しており、このままでは実行時間に1~10秒程度掛かってしまいます。対話的な操作を行うソフトウェアにおいて、応答時間が1秒というのは長く、ストレスを感じます。10秒は論外です。可能であれば、私なら別のソフトウェアを探します。
閑話休題。問題の自作関数の中では、いくつかのWindows APIが呼び出されていましたが、極端に遅かったのがCopyMemoryでした。速度を計測したサンプルコードを下記に示します。対照として、同じメモリ操作関数であるZeroMemoryの速度も計測しました。速度の計測にはtimeGetTimeを使用したい所ですが、今回はDeclareする関数を検証する関数だけにしたかったので、敢えてTimer関数を使用しています。
Option Explicit
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
Sub MeasureExecTimeOfCopyMemory()
Dim a As Long
Dim b As Long
Dim i As Long
Dim s As Double
s = Timer
For i = 1 To 12345
Call CopyMemory(a, b, 4)
Next
Debug.Print Timer - s
End Sub
Option Explicit
Private Declare PtrSafe Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" (dest As Any, ByVal numBytes As LongPtr)
Sub MeasureExecTimeOfZeroMemory()
Dim a As Long
Dim i As Long
Dim s As Double
s = Timer
For i = 1 To 12345
Call ZeroMemory(a, 4)
Next
Debug.Print Timer - s
End Sub
なお、実行環境は下記の通りです。
項目 | 仕様 |
OS | Windows 10 64bit |
Excel | 2016 32bit |
PC | ThinkPad E595 |
CPU | AMD Ryzen 5 3500U プロセッサー (2.10GHz, 4MB) |
メモリ | DDR4 2400MHz 16GB |
3回計測した結果、CopyMemoryは12345回の実行に24秒315、1回の実行に1970ナノ秒掛かっていました。ZeroMemoryは12345回の実行に0.138秒、1回の実行に11ナノ秒掛かっていました。実に176倍の差です。
なお、この事象は新規ワークブックでは発生せず、ワークブックを保存すると発生しました。しかし、アドイン化すると、この問題は発生しませんでした。また、別のパソコンでは、この事象は発生しませんでした。これらの事実から浮かび上がってきた容疑者が、Avastでした。そこで、CopyMemoryを実行している最中にタスクマネージャーでCPUの使用率を確認したところ、「Avast Service」というプロセスが反応していました。
そのため、早速Avastをアンインストールして、再びCopyMemoryの速度計測を行ったところ、12345回の実行に5.523秒、1回の実行が447ナノ秒となりました。少し改善されましたが、体感的にはまだ遅い感じがしたため、タスクマネージャーでCPUの使用率を確認したところ、今度はWindows Defenderが反応していました。
Windows Defenderを安全に無効にするには、別のウィルス対策ソフトをインストールする必要があるため、今度はAviraをインストールしました。その結果、CopyMemoryの速度は12345回の実行で0.065秒、1回の実行が5ナノ秒となりました。Timer関数の精度を考慮し、実行回数を増やした所、123456回で0.385秒、1回の実行が3ナノ秒となりました。Avastと比較して実に657倍の差が発生しました。
また、Avastインストール環境のZeroMemoryがAviraインストール環境のCopyMemoryよりも遅いので、AvastはCopyMemory以外のWindowsAPIも監視しているのだと思います。
ウィルス対策ソフト | CopyMemoryの実行時間(1回) |
Avast Antivirus | 1970ナノ秒 |
Windows Defender | 447ナノ秒 |
Avira Antivirus | 3ナノ秒 |
調査の過程で、いくつかのWindowsAPIの速度を計測しましたが、顕著の遅くなったのはCopyMemoryのみでした。そのため、CopyMemoryによる速度低下が問題になる場合は、別の関数を使用できないか検討すると良いでしょう。
ウィルス対策ソフトがCopyMemoryを監視する理由
どうやらCopyMemoryはVBAでマルウェアを作る際にも使用される関数のようです(https://www.virusbulletin.com/virusbulletin/2014/04/back-vba)。そのため、ウィルス対策ソフトはCopyMemoryでコピーした内容にマシンコードが含まれているかチェックしているのだと思います。そう考えると、Aviraの速さが逆に気になりますね。ウィルスの検出率は高いようなので、アルゴリズムが優れていると信じたいです。ちなみに、速度計測はしていませんが、McAfeeもAviraと同等か、それ以上の速度でした。
この記事のトラックバックURL
スポンサーリンク
カテゴリー
スポンサーリンク
-
ホーム -
上へ
ディスカッション
コメント一覧
まだ、コメントがありません