概要
トップレベルウィンドウのハンドルを列挙するWin32 APIの関数です。第一引数であるコールバック関数のポインタに、取得されたハンドルが渡されます。
C++
BOOL EnumWindows(
[in] WNDENUMPROC lpEnumFunc,
[in] LPARAM lParam
);
EnumWindowsを使用し、マルウェア解析環境であるかどうかを判定する代表的な方法は、トップレベルウィンドウの数をカウントし、攻撃者が指定した閾値より少ない場合をマルウェア解析環境と判断するものです。マルウェア解析環境では、マルウェア本体とマルウェア解析ツールのみを実行することがあります。そのため、一般的な端末と比較した場合、トップレベルウィンドウの数が少なくなる傾向にあります。そこで、EnumWindowsを使用することでトップレベルウィンドウの数を取得し、通常よりも数が少ない環境をマルウェア解析環境と判断し、動作を終了するマルウェアが存在します。
そのほかにもEnumWindowsに加え、GetWindowTextやGetClassNameを使用することで、ウィンドウ名やウィンドウクラス名を取得し、攻撃者が作成したブロックリストの内容と一致する場合をマルウェア解析環境と判断する方法もあります。攻撃者が作成するブロックリストには、著名なマルウェア解析ツールやデバッガが指定されることが多いです。
実装の例
C++/x86 Assemblyのコード例を以下に示します。本コードでは、トップレベルウィンドウの数が10以下の場合、動作を終了します。
C++
BOOL CALLBACK EnumWndProc(HWND hWnd, LPARAM lParam){
int* lpCount = (int*)lParam;
*lpCount += 1;
return TRUE;
}
BOOL isAnalysedEnv(){
BOOL bRet = FALSE;
int nCount = 0;
EnumWindows(EnumWndProc, (LPARAM)&nCount);
if (nCount <= 10){
bRet = TRUE;
}
return bRet;
}
Assembly(x86)
mov [ebp+lParam], 0
lea eax, [ebp+lParam]
push eax ; lParam
push 8 ; lpEnumFunc
call EnumWindows
cmp [ebp+lParam], 10
jle short loc_analysisenvironment_found
回避手法
EnumWindowsによるマルウェア解析環境検知を回避し、プログラムを解析する手法を以下に示します。一時的に検出を回避するときには1、2の手法を、恒久的に検出を回避するときには2の手法を使います。
- デバッグ中にレジスタの値を変更する
EnumWindowsを呼び出した後に、lParamの値を変更します。変更する値は、攻撃者が指定したトップレベルウィンドウ数の閾値より大きくする必要があります。 - 意図的に複数のプロセスを実行する
マルウェア解析環境であると判断されないように、意図的に複数のプロセスを実行し、トップレベルウィンドウ数を増加させます。 - プログラムを書き換える
EnumWindowsを呼び出した後の分岐命令をJMP命令またはNOP命令に書き換え、変更を保存します。
検出手法
EnumWindowsが使用されたファイルを検出するためのYARAルールは下記のとおりです。
YARAルール
rule Win32API_EnumWindows
{
strings:
$str = "EnumWindows"
condition:
$str
}