概要
現在のローカルの日付と時刻を取得するWin32 APIの関数です。取得した日時はSYSTEMTIME構造体に格納されます。
C++
void GetLocalTime(
[out] LPSYSTEMTIME lpSystemTime
);
マルウェアは以下の理由でこの関数を利用することがあります。
(1) 特定の日時に(または特定の日時以外に)悪性コードを実行する(時限式実行)
(2) コードの実行に掛かる時間を計測し、デバッガー上で実行されていることを検知する
コードの実行に掛かる時間を計測する目的では、GetTickCount関数が使われることもあります。
実装の例
(1) 特定の日時に悪性コードを実行するC++/x86 Assemblyのコード例
実行環境における現在の年が2022年ではない場合、プロセスは終了されます。
C++
SYSTEMTIME st;
GetLocalTime(&st);
if(st.wYear != 2022){
exit(1);
}
Assembly(x86)
lea eax, [ebp+SystemTime]
push eax ; lpSystemTime
call GetLocalTime
cmp [ebp+SystemTime.wYear], 2022
jnz short loc_not_specified_date
loc_not_specified_date:
push 1
call exit
(2) コードの実行に掛かる時間を計測し、デバッガー上で実行されていることを検知するC++のコード例
ある処理の実行時間が通常時に比べて大きかった場合、isDebugged関数はtrueを返します。
C++
BOOL isDebugged(ULONGLONG estimatedElapsedTime) {
SYSTEMTIME st1, st2;
FILETIME ft1, ft2;
ULARGE_INTEGER ui1, ui2;
GetLocalTime(&st1);
//処理に時間の掛かる動作
GetLocalTime(&st2);
SystemTimeToFileTime(&st1, &ft1);
SystemTimeToFileTime(&st2, &ft2);
ui1.HighPart = ft1.dwHighDateTime;
ui1.LowPart = ft1.dwLowDateTime;
ui2.HighPart = ft2.dwHighDateTime;
ui2.LowPart = ft2.dwLowDateTime;
return (ui2.QuadPart - ui1.QuadPart) > estimatedElapsedTime;
}
回避手法
GetLocalTimeによるマルウェア解析環境検知を回避し、プログラムを解析する手法を以下に示します。一時的に検出を回避するときには1の手法を、恒久的に検出を回避するときには2の手法を使います。
- デバッグ中に分岐命令を書き換える
GetLocalTimeを呼び出した後の分岐命令をJMP命令またはNOP命令に書き換えます。 - プログラムを書き換える
GetLocalTimeを呼び出した後の分岐命令をJMP命令またはNOP命令に書き換え、変更を保存します。
検出手法
GetLocalTimeを検出するYARAルールを以下に示します。
YARAルール
rule Win32API_GetLocalTime
{
strings:
$str ="GetLocalTime"
condition:
$str
}