EternalWindows
セキュリティコンテキスト / ファイルの実行確認

ローカル セキュリティ ポリシーによって追加された規則は、 主にファイルが実行されようとしている場合に確認されます。 たとえば、.exeファイルを実行するCreateProcessや、 様々なファイル形式を実行するShellExecuteは、 ファイルの情報(パスやハッシュ)が規則の内容と一致するかを調べ、 一致して起動できない場合はGetLastErrorがERROR_ACCESS_DISABLED_BY_POLICYを返すことになります。 この場合、アプリケーションによっては、 ポリシーによって制限されたことを示すダイアログを表示することもあります。

以前に述べたように、ファイルが安全に実行されるかどうかは 内部的に利用されるDLLに依存するため、DLLに対して規則を設定する場合もあります。 LoadLibraryは、指定されたDLLの情報が規則に一致しているかを調べ、 一致している場合はGetLastErrorがERROR_ACCESS_DISABLED_BY_POLICYを返します。 ただし、レジストリエントリのTransparentEnabledの値が2に設定されていなければ、 DLLに対しての規則は有効であると判断されません。

あるファイルを実行できるかどうかを知るためには、主に2つの方法があります。 1つはそのファイルを実際に実行することであり、 GetLastErrorがERROR_ACCESS_DISABLED_BY_POLICYを返したかどうかを確認します。 そしてもう1つは、SaferIdentifyLevelを呼び出して、 明示的に規則の確認を行う方法です。 つまり、CreateProcessなどの関数が内部的に行っている処理と同じことをします。 SaferIdentifyLevelは、次のように定義されています。

BOOL WINAPI SaferIdentifyLevel(
  DWORD dwNumProperties,
  PSAFER_CODE_PROPERTIES pCodeProperties,
  SAFER_LEVEL_HANDLE *pLevelHandle,
  LPVOID lpReserved
);

dwNumPropertiesは、pCodePropertiesの要素数を指定します。 pCodePropertiesは、SAFER_CODE_PROPERTIES構造体のアドレスを指定します。 pLevelHandleは、SAFER_LEVEL_HANDLE型の変数のアドレスを指定します。 lpReservedは、予約されているためNULLを指定します。 SAFER_CODE_PROPERTIES構造体は、次のように定義されています。

typedef struct SAFER_CODE_PROPERTIES {
  DWORD cbSize;
  DWORD dwCheckFlags;
  LPCWSTR ImagePath;
  HANDLE hImageFileHandle;
  DWORD UrlZoneId;
  BYTE ImageHash[SAFER_MAX_HASH_SIZE];
  DWORD dwImageHashSize;
  LARGE_INTEGER ImageSize;
  ALG_ID HashAlgorithm;
  LPBYTE pByteBlock;
  HWND hWndParent;
  DWORD dwWVTUIChoice;
} SAFER_CODE_PROPERTIES,  *PSAFER_CODE_PROPERTIES;

cbSizeは、構造体のサイズを指定します。 dwCheckFlagsは、規則の種類を表す定数を指定します。 ImagePathは、規則の比較対象とするファイルのパスを指定します。 hImageFileHandleは、規則の比較対象とするファイルのハンドルを指定します。 UrlZoneIdは、セキュリティゾーンを表す定数を指定します。 ImageHashは、規則の比較対象とするファイルのハッシュを指定します。 dwImageHashSizeは、ImageHashのサイズを指定します。 ImageSizeは、pByteBlockのサイズを指定します。 HashAlgorithmは、ImageHashの作成に使用されたハッシュアルゴリズムを指定します。 pByteBlockは、コードのイメージを含むデータを指定します。 hWndParentは、署名の検証時に表示されるダイアログの親とするウインドウハンドルを指定します。 dwWVTUIChoiceは、UI表示に関する定数を指定します。 dwCheckFlagsに指定できる定数を次に示します。

定数 意味
SAFER_CRITERIA_IMAGEPATH パスの規則について確認する。
SAFER_CRITERIA_IMAGEHASH ハッシュの規則について確認する。
SAFER_CRITERIA_AUTHENTICODE 証明書の規則について確認する。
SAFER_CRITERIA_URLZONE ネットワークゾーンの規則について確認する。

SAFER_CODE_PROPERTIES構造体には、規則が設定されているかを確認するためのメンバが数多くありますが、 基本的に使用するのはImagePathのみです。 ここに目的のファイルのパスを指定するだけで、 dwCheckFlagsに指定した規則との比較が行われます。

SaferIdentifyLevelでセキュリティレベルを表すハンドルを取得すれば、 SaferGetLevelInformationを呼び出すことで、 実際にセキュリティレベルを確認することができます。

BOOL WINAPI SaferGetLevelInformation(
  SAFER_LEVEL_HANDLE LevelHandle,
  SAFER_OBJECT_INFO_CLASS dwInfoType,
  LPVOID lpQueryBuffer,
  DWORD dwInBufferSize,
  LPDWORD lpdwOutBufferSize
)

LevelHandleは、SAFER_LEVEL_HANDLE型のハンドルを指定します。 dwInfoTypeは、SAFER_OBJECT_INFO_CLASS型として定義されている列挙定数を指定します。 lpQueryBufferは、データを受け取るバッファを指定します。 dwInBufferSizeは、lpQueryBufferのサイズを指定します。 lpdwOutBufferSizeは、lpQueryBufferにコピーされたデータのサイズを受け取る変数のアドレスを指定します。 dwInfoTypeに指定できる定数を次に示します。

定数 意味
SaferObjectLevelId セキュリティレベルを表す定数を取得する。これらには、SAFER_LEVELID_DISALLOWEDなどがある。
SaferObjectScopeId セキュリティレベルの適用範囲を取得する。これらには、SAFER_SCOPEID_MACHINEなどがある。
SaferObjectFriendlyName セキュリティレベルの名前を取得する。これらには、「許可しない」などがある。
SaferObjectDescription セキュリティレベルについての説明文を取得する。これらには、「ユーザーにアクセス権があっても、ソフトウェアは実行されません。 」などがある。

SaferObjectLevelIdとSaferObjectScopeIdでは値が返るため、 lpQueryBufferにDWORD型変数のアドレスを指定します。 SaferObjectFriendlyNameとSaferObjectDescriptionはUNICCODE文字列を返すため、 lpQueryBufferにはWCHAR型のバッファを指定します。

今回のプログラムは、SaferIdentifyLevelを呼び出して、 指定されたファイルにパス規則が設定されているかどうかを調べます。

#include <windows.h>
#include <winsafer.h>

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	SAFER_LEVEL_HANDLE    hLevel;
	SAFER_CODE_PROPERTIES codeProperties;
	DWORD                 dwError;
	
	ZeroMemory(&codeProperties, sizeof(SAFER_CODE_PROPERTIES));
	codeProperties.cbSize       = sizeof(SAFER_CODE_PROPERTIES);
	codeProperties.dwCheckFlags = SAFER_CRITERIA_IMAGEPATH;
	codeProperties.ImagePath    = L"";
	
	if (!SaferIdentifyLevel(1, &codeProperties, &hLevel, NULL))
		return 0;
	
	dwError = GetLastError();
	if (dwError == ERROR_MORE_DATA) {
		DWORD dwSize;
		WCHAR szBuf[256];
		SaferGetLevelInformation(hLevel, SaferObjectFriendlyName, szBuf, sizeof(szBuf), &dwSize);
		MessageBoxW(NULL, L"指定されたファイルが、指定された規則と一致しました。", szBuf, MB_OK);
	}
	else if (dwError == ERROR_SUCCESS)
		MessageBox(NULL, TEXT("指定されたファイルは、指定された規則と一致しません。"), TEXT("OK"), MB_OK);
	else if (dwError == ERROR_FILE_NOT_FOUND)
		MessageBox(NULL, TEXT("指定されたファイルが存在しません。"), NULL, MB_ICONWARNING);
	else
		;
	
	SaferCloseLevel(hLevel);

	return 0;
}

パス規則の確認を行うため、SAFER_CODE_PROPERTIES構造体のdwCheckFlagsには、 SAFER_CRITERIA_IMAGEPATHを指定しています。 ハッシュ規則の確認の場合は、SAFER_CRITERIA_IMAGEHASHを指定することになるでしょう。 ImagePathには、規則が設定されているかを確認したいファイルのフルパスを指定します。

SaferIdentifyLevelが成功すれば、GetLastErrorが具体的な内容を特定します。 ERROR_MORE_DATAが返った場合は、何らかの制限がファイルに設定されていることを意味しているため、 それをSaferGetLevelInformationで取得しています。 制限に応じて処理を変化させたい場合は、SaferObjectLevelIdを指定してセキュリティレベルを取得し、 その値とSAFER_LEVELID_DISALLOWEDなどを比較することになるでしょう。

イベントログへの書き込み

実行禁止の規則が設定されたファイルをエクスプローラ上などで実行した場合、 実行できなかった旨をアプリケーションイベントログに書き込む処理が行われています。 アプリケーションがSaferIdentifyLevelで明示的に規則を確認する場合は、 このような書き込みについても対応しておく必要があるといえるでしょう。 次に、例を示します。

SaferRecordEventLogEntry(hLevel, szFilePath, NULL);

第1引数は、SaferIdentifyLevelで取得したハンドルを指定します。 第2引数は、イベントログの詳細(AttemptedPath)にて表示される文字列であり、 基本的にはSAFER_CODE_PROPERTIES構造体のImagePathに指定した文字列を指定すると思われます。 第3引数は予約されているためNULLを指定します。



戻る