|
|
/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
WindowsFileProtection.cpp
Abstract:
This AppVerifier shim hooks the file I/O APIs that could potentially change a file under Windows File Protection.
When one of the files is accessed or modified, an event is written to the log.
Notes:
This is a general purpose shim.
History:
06/25/2001 rparsons Created
11/26/2001 rparsons Remove unused local variables. Make SHFileOperation more efficent.
--*/
#include "precomp.h"
#include "rtlutils.h"
#include "sfc.h"
IMPLEMENT_SHIM_BEGIN(WindowsFileProtection) #include "ShimHookMacro.h"
//
// verifier log entries
//
BEGIN_DEFINE_VERIFIER_LOG(WindowFileProtection) VERIFIER_LOG_ENTRY(VLOG_WFP_COPYFILE) VERIFIER_LOG_ENTRY(VLOG_WFP_MOVEFILE) VERIFIER_LOG_ENTRY(VLOG_WFP_DELETEFILE) VERIFIER_LOG_ENTRY(VLOG_WFP_REPLACEFILE) VERIFIER_LOG_ENTRY(VLOG_WFP_WRITEFILE) VERIFIER_LOG_ENTRY(VLOG_WFP_OPENFILE) VERIFIER_LOG_ENTRY(VLOG_WFP_SHFILEOP) END_DEFINE_VERIFIER_LOG(WindowFileProtection)
INIT_VERIFIER_LOG(WindowFileProtection);
APIHOOK_ENUM_BEGIN
APIHOOK_ENUM_ENTRY(CreateFileA) APIHOOK_ENUM_ENTRY(CreateFileW) APIHOOK_ENUM_ENTRY(OpenFile)
APIHOOK_ENUM_ENTRY(CopyFileA) APIHOOK_ENUM_ENTRY(CopyFileW) APIHOOK_ENUM_ENTRY(CopyFileExA) APIHOOK_ENUM_ENTRY(CopyFileExW) APIHOOK_ENUM_ENTRY(DeleteFileA) APIHOOK_ENUM_ENTRY(DeleteFileW) APIHOOK_ENUM_ENTRY(MoveFileA) APIHOOK_ENUM_ENTRY(MoveFileW) APIHOOK_ENUM_ENTRY(MoveFileExA) APIHOOK_ENUM_ENTRY(MoveFileExW) APIHOOK_ENUM_ENTRY(MoveFileWithProgressA) APIHOOK_ENUM_ENTRY(MoveFileWithProgressW) APIHOOK_ENUM_ENTRY(ReplaceFileA) APIHOOK_ENUM_ENTRY(ReplaceFileW) APIHOOK_ENUM_ENTRY(SHFileOperationA) APIHOOK_ENUM_ENTRY(SHFileOperationW)
APIHOOK_ENUM_ENTRY(_lcreat) APIHOOK_ENUM_ENTRY(_lopen)
APIHOOK_ENUM_ENTRY(NtCreateFile) APIHOOK_ENUM_ENTRY(NtOpenFile)
APIHOOK_ENUM_END
/*++
ANSI wrapper for SfcIsFileProtected.
--*/ BOOL IsFileProtected( LPCSTR pszFileName ) { LPWSTR pwszWideFileName = NULL; int nLen; BOOL bReturn = FALSE;
//
// Convert from ANSI to Unicode.
//
nLen = lstrlenA(pszFileName) + 1;
if (nLen) {
pwszWideFileName = (LPWSTR)RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, nLen * sizeof(WCHAR));
if (!pwszWideFileName) { DPFN(eDbgLevelError, "[IsFileProtected] Failed to allocate memory"); return FALSE; }
if (!MultiByteToWideChar(CP_ACP, 0, pszFileName, -1, pwszWideFileName, nLen)) { DPFN(eDbgLevelError, "[IsFileProtected] ANSI -> Unicode failed"); goto cleanup; }
bReturn = SfcIsFileProtected(NULL, pwszWideFileName); }
cleanup:
if (pwszWideFileName) { RtlFreeHeap(RtlProcessHeap(), 0, pwszWideFileName); }
return bReturn; }
/*++
Wraps SfcIsFileProtected for NT path names.
--*/ BOOL IsNtFileProtected( IN PUNICODE_STRING pstrNtFileName ) { NTSTATUS status; RTL_UNICODE_STRING_BUFFER DosPath; BOOL fReturn = FALSE; UCHAR DosPathBuffer[MAX_PATH * 2];
if (!pstrNtFileName) { DPFN(eDbgLevelError, "[IsNtFileProtected] Invalid parameter"); return FALSE; }
//
// Convert from an NT path to a DOS path.
//
RtlInitUnicodeStringBuffer(&DosPath, DosPathBuffer, sizeof(DosPathBuffer));
status = ShimAssignUnicodeStringBuffer(&DosPath, pstrNtFileName);
if (!NT_SUCCESS(status)) { DPFN(eDbgLevelError, "[IsNtFileProtected] Failed to initialize DOS path buffer"); return fReturn; }
status = ShimNtPathNameToDosPathName(0, &DosPath, NULL, NULL);
if (!NT_SUCCESS(status)) { DPFN(eDbgLevelError, "[IsNtFileProtected] Failed to convert NT \"%ls\" to DOS path", pstrNtFileName->Buffer); goto cleanup; }
//
// Now check for a protected file.
//
if (SfcIsFileProtected(NULL, DosPath.String.Buffer)) { fReturn = TRUE; }
cleanup:
RtlFreeUnicodeStringBuffer(&DosPath);
return fReturn; }
BOOL APIHOOK(CopyFileA)( LPCSTR lpExistingFileName, LPCSTR lpNewFileName, BOOL bFailIfExists ) { //
// Ensure that the destination is not protected.
//
if (!bFailIfExists && IsFileProtected(lpNewFileName)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_COPYFILE, "API: CopyFileA Filename: %s", lpNewFileName); }
return ORIGINAL_API(CopyFileA)(lpExistingFileName, lpNewFileName, bFailIfExists); }
BOOL APIHOOK(CopyFileW)( LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, BOOL bFailIfExists ) { //
// Ensure that the destination is not protected.
//
if (!bFailIfExists && SfcIsFileProtected(NULL, lpNewFileName)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_COPYFILE, "API: CopyFileW Filename: %ls", lpNewFileName); }
return ORIGINAL_API(CopyFileW)(lpExistingFileName, lpNewFileName, bFailIfExists); }
BOOL APIHOOK(CopyFileExA)( LPCSTR lpExistingFileName, LPCSTR lpNewFileName, LPPROGRESS_ROUTINE lpProgressRoutine, LPVOID lpData, LPBOOL pbCancel, DWORD dwCopyFlags ) { //
// Ensure that the destination is not protected.
//
if (!(dwCopyFlags & COPY_FILE_FAIL_IF_EXISTS) && IsFileProtected(lpNewFileName)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_COPYFILE, "API: CopyFileExA Filename: %s", lpNewFileName); }
return ORIGINAL_API(CopyFileExA)(lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, pbCancel, dwCopyFlags);
}
BOOL APIHOOK(CopyFileExW)( LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, LPPROGRESS_ROUTINE lpProgressRoutine, LPVOID lpData, LPBOOL pbCancel, DWORD dwCopyFlags ) { //
// Ensure that the destination is not protected.
//
if (!(dwCopyFlags & COPY_FILE_FAIL_IF_EXISTS) && SfcIsFileProtected(NULL, lpNewFileName)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_COPYFILE, "API: CopyFileExW Filename: %ls", lpNewFileName); }
return ORIGINAL_API(CopyFileExW)(lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, pbCancel, dwCopyFlags);
}
BOOL APIHOOK(DeleteFileA)( LPCSTR lpFileName ) { if (IsFileProtected(lpFileName)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_DELETEFILE, "API: DeleteFileA Filename: %s", lpFileName); }
return ORIGINAL_API(DeleteFileA)(lpFileName); }
BOOL APIHOOK(DeleteFileW)( LPCWSTR lpFileName ) { if (SfcIsFileProtected(NULL, lpFileName)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_DELETEFILE, "API: DeleteFileW Filename: %ls", lpFileName); }
return ORIGINAL_API(DeleteFileW)(lpFileName); }
BOOL APIHOOK(MoveFileA)( LPCSTR lpExistingFileName, LPCSTR lpNewFileName ) { //
// Ensure that the source or destination is not protected.
//
if (IsFileProtected(lpExistingFileName) || IsFileProtected(lpNewFileName)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_MOVEFILE, "API: MoveFileA Filename: %s Filename: %s", lpExistingFileName, lpNewFileName); }
return ORIGINAL_API(MoveFileA)(lpExistingFileName, lpNewFileName); }
BOOL APIHOOK(MoveFileW)( LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName ) { //
// Ensure that the source or destination is not protected.
//
if (SfcIsFileProtected(NULL, lpExistingFileName) || SfcIsFileProtected(NULL, lpNewFileName)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_MOVEFILE, "API: MoveFileW Filename: %ls Filename: %ls", lpExistingFileName, lpNewFileName); }
return ORIGINAL_API(MoveFileW)(lpExistingFileName, lpNewFileName); }
BOOL APIHOOK(MoveFileExA)( LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags ) { //
// Ensure that the source or destination is not protected.
//
if (IsFileProtected(lpExistingFileName) || IsFileProtected(lpNewFileName)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_MOVEFILE, "API: MoveFileExA Filename: %s Filename: %s", lpExistingFileName, lpNewFileName); }
return ORIGINAL_API(MoveFileExA)(lpExistingFileName, lpNewFileName, dwFlags); }
BOOL APIHOOK(MoveFileExW)( LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, DWORD dwFlags ) { //
// Ensure that the source or destination is not protected.
//
if (SfcIsFileProtected(NULL, lpExistingFileName) || SfcIsFileProtected(NULL, lpNewFileName)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_MOVEFILE, "API: MoveFileExW Filename: %ls Filename: %ls", lpExistingFileName, lpNewFileName); }
return ORIGINAL_API(MoveFileExW)(lpExistingFileName, lpNewFileName, dwFlags); }
BOOL APIHOOK(MoveFileWithProgressA)( LPCSTR lpExistingFileName, LPCSTR lpNewFileName, LPPROGRESS_ROUTINE lpProgressRoutine, LPVOID lpData, DWORD dwFlags ) { //
// Ensure that the source or destination is not protected.
//
if (IsFileProtected(lpExistingFileName) || IsFileProtected(lpNewFileName)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_MOVEFILE, "API: MoveFileWithProgressA Filename: %s Filename: %s", lpExistingFileName, lpNewFileName); }
return ORIGINAL_API(MoveFileWithProgressA)(lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, dwFlags); }
BOOL APIHOOK(MoveFileWithProgressW)( LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, LPPROGRESS_ROUTINE lpProgressRoutine, LPVOID lpData, DWORD dwFlags ) { //
// Ensure that the source or destination is not protected.
//
if (SfcIsFileProtected(NULL, lpExistingFileName) || SfcIsFileProtected(NULL, lpNewFileName)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_MOVEFILE, "API: MoveFileWithProgressW Filename: %ls Filename: %ls", lpExistingFileName, lpNewFileName); }
return ORIGINAL_API(MoveFileWithProgressW)(lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, dwFlags); }
BOOL APIHOOK(ReplaceFileA)( LPCSTR lpReplacedFileName, LPCSTR lpReplacementFileName, LPCSTR lpBackupFileName, DWORD dwReplaceFlags, LPVOID lpExclude, LPVOID lpReserved ) { //
// Ensure that the destination is not protected.
//
if (IsFileProtected(lpReplacedFileName)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_REPLACEFILE, "API: ReplaceFileA Filename: %s", lpReplacedFileName); }
return ORIGINAL_API(ReplaceFileA)(lpReplacedFileName, lpReplacementFileName, lpBackupFileName, dwReplaceFlags, lpExclude, lpReserved);
}
BOOL APIHOOK(ReplaceFileW)( LPCWSTR lpReplacedFileName, LPCWSTR lpReplacementFileName, LPCWSTR lpBackupFileName, DWORD dwReplaceFlags, LPVOID lpExclude, LPVOID lpReserved ) { //
// Ensure that the destination is not protected.
//
if (SfcIsFileProtected(NULL, lpReplacedFileName)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_REPLACEFILE, "API: ReplaceFileW Filename: %ls", lpReplacedFileName); }
return ORIGINAL_API(ReplaceFileW)(lpReplacedFileName, lpReplacementFileName, lpBackupFileName, dwReplaceFlags, lpExclude, lpReserved);
}
void ReportProtectedFileA( LPCSTR pszFilePath ) { UINT uSize = 0;
if (pszFilePath) { while (TRUE) { if (IsFileProtected(pszFilePath)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_SHFILEOP, "API: SHFileOperationA Filename: %s", pszFilePath); }
uSize = lstrlenA(pszFilePath) + 1; pszFilePath += uSize;
if (*pszFilePath == '\0') { break; } } } }
void ReportProtectedFileW( LPCWSTR pwszFilePath ) { UINT uSize = 0;
if (pwszFilePath) { while (TRUE) { if (SfcIsFileProtected(NULL, pwszFilePath)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_SHFILEOP, "API: SHFileOperationW Filename: %ls", pwszFilePath); }
uSize = lstrlenW(pwszFilePath) + 1; pwszFilePath += uSize;
if (*pwszFilePath == '\0') { break; } } } }
int APIHOOK(SHFileOperationA)( LPSHFILEOPSTRUCTA lpFileOp ) { //
// If they're going to rename files on collision, don't bother.
//
if (!(lpFileOp->fFlags & FOF_RENAMEONCOLLISION)) { ReportProtectedFileA(lpFileOp->pFrom); ReportProtectedFileA(lpFileOp->pTo); }
return ORIGINAL_API(SHFileOperationA)(lpFileOp); }
int APIHOOK(SHFileOperationW)( LPSHFILEOPSTRUCTW lpFileOp ) { //
// If they're going to rename files on collision, don't bother.
//
if (!(lpFileOp->fFlags & FOF_RENAMEONCOLLISION)) { ReportProtectedFileW(lpFileOp->pFrom); ReportProtectedFileW(lpFileOp->pTo); }
return ORIGINAL_API(SHFileOperationW)(lpFileOp); }
HANDLE APIHOOK(CreateFileA)( LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ) { if (IsFileProtected(lpFileName) && (dwDesiredAccess & GENERIC_WRITE || dwDesiredAccess & GENERIC_READ)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_OPENFILE, "API: CreateFileA Filename: %s", lpFileName); }
return ORIGINAL_API(CreateFileA)(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); }
HANDLE APIHOOK(CreateFileW)( LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ) { if (SfcIsFileProtected(NULL, lpFileName) && (dwDesiredAccess & GENERIC_WRITE || dwDesiredAccess & GENERIC_READ)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_OPENFILE, "API: CreateFileW Filename: %ls", lpFileName); }
return ORIGINAL_API(CreateFileW)(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); }
HFILE APIHOOK(OpenFile)( LPCSTR lpFileName, LPOFSTRUCT lpReOpenBuff, UINT uStyle ) { if (IsFileProtected(lpFileName) && (uStyle & OF_READWRITE || uStyle & OF_CREATE || uStyle & OF_DELETE)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_OPENFILE, "API: OpenFile Filename: %s", lpFileName); }
return ORIGINAL_API(OpenFile)(lpFileName, lpReOpenBuff, uStyle); }
HFILE APIHOOK(_lopen)( LPCSTR lpPathName, int iReadWrite ) { if (IsFileProtected(lpPathName) && (iReadWrite & OF_READWRITE || iReadWrite & OF_WRITE)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_OPENFILE, "API: _lopen Filename: %s", lpPathName); }
return ORIGINAL_API(_lopen)(lpPathName, iReadWrite); }
HFILE APIHOOK(_lcreat)( LPCSTR lpPathName, int iAttribute ) { if (IsFileProtected(lpPathName) && iAttribute != 1) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_OPENFILE, "API: _lcreat Filename: %s", lpPathName); }
return ORIGINAL_API(_lcreat)(lpPathName, iAttribute);
}
NTSTATUS APIHOOK(NtCreateFile)( PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength ) { if (IsNtFileProtected(ObjectAttributes->ObjectName)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_OPENFILE, "API: NtCreateFile Filename: %ls", ObjectAttributes->ObjectName->Buffer); }
return ORIGINAL_API(NtCreateFile)(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);
}
NTSTATUS APIHOOK(NtOpenFile)( PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, ULONG ShareAccess, ULONG OpenOptions ) { if (IsNtFileProtected(ObjectAttributes->ObjectName)) { VLOG(VLOG_LEVEL_ERROR, VLOG_WFP_OPENFILE, "API: NtOpenFile Filename: %ls", ObjectAttributes->ObjectName->Buffer); }
return ORIGINAL_API(NtOpenFile)(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, ShareAccess, OpenOptions);
}
SHIM_INFO_BEGIN()
SHIM_INFO_DESCRIPTION(AVS_WINFILEPROTECT_DESC) SHIM_INFO_FRIENDLY_NAME(AVS_WINFILEPROTECT_FRIENDLY) SHIM_INFO_VERSION(1, 5) SHIM_INFO_FLAGS(AVRF_FLAG_EXTERNAL_ONLY) SHIM_INFO_INCLUDE_EXCLUDE("E:rpcrt4.dll kernel32.dll")
SHIM_INFO_END()
/*++
Register hooked functions.
--*/ HOOK_BEGIN
DUMP_VERIFIER_LOG_ENTRY(VLOG_WFP_COPYFILE, AVS_WFP_COPYFILE, AVS_WFP_GENERAL_R, AVS_WFP_GENERAL_URL)
DUMP_VERIFIER_LOG_ENTRY(VLOG_WFP_MOVEFILE, AVS_WFP_MOVEFILE, AVS_WFP_GENERAL_R, AVS_WFP_GENERAL_URL)
DUMP_VERIFIER_LOG_ENTRY(VLOG_WFP_DELETEFILE, AVS_WFP_DELETEFILE, AVS_WFP_GENERAL_R, AVS_WFP_GENERAL_URL)
DUMP_VERIFIER_LOG_ENTRY(VLOG_WFP_REPLACEFILE, AVS_WFP_REPLACEFILE, AVS_WFP_GENERAL_R, AVS_WFP_GENERAL_URL)
DUMP_VERIFIER_LOG_ENTRY(VLOG_WFP_WRITEFILE, AVS_WFP_WRITEFILE, AVS_WFP_GENERAL_R, AVS_WFP_GENERAL_URL)
DUMP_VERIFIER_LOG_ENTRY(VLOG_WFP_OPENFILE, AVS_WFP_OPENFILE, AVS_WFP_GENERAL_R, AVS_WFP_GENERAL_URL)
DUMP_VERIFIER_LOG_ENTRY(VLOG_WFP_SHFILEOP, AVS_WFP_SHFILEOP, AVS_WFP_GENERAL_R, AVS_WFP_GENERAL_URL)
APIHOOK_ENTRY(KERNEL32.DLL, CreateFileA) APIHOOK_ENTRY(KERNEL32.DLL, CreateFileW) APIHOOK_ENTRY(KERNEL32.DLL, OpenFile)
APIHOOK_ENTRY(KERNEL32.DLL, CopyFileA) APIHOOK_ENTRY(KERNEL32.DLL, CopyFileW) APIHOOK_ENTRY(KERNEL32.DLL, CopyFileExA) APIHOOK_ENTRY(KERNEL32.DLL, CopyFileExW)
APIHOOK_ENTRY(KERNEL32.DLL, DeleteFileA) APIHOOK_ENTRY(KERNEL32.DLL, DeleteFileW)
APIHOOK_ENTRY(KERNEL32.DLL, MoveFileA) APIHOOK_ENTRY(KERNEL32.DLL, MoveFileW) APIHOOK_ENTRY(KERNEL32.DLL, MoveFileExA) APIHOOK_ENTRY(KERNEL32.DLL, MoveFileExW) APIHOOK_ENTRY(KERNEL32.DLL, MoveFileWithProgressA) APIHOOK_ENTRY(KERNEL32.DLL, MoveFileWithProgressW)
APIHOOK_ENTRY(KERNEL32.DLL, ReplaceFileA) APIHOOK_ENTRY(KERNEL32.DLL, ReplaceFileW)
APIHOOK_ENTRY(SHELL32.DLL, SHFileOperationA) APIHOOK_ENTRY(SHELL32.DLL, SHFileOperationW)
// 16-bit compatibility file routines.
APIHOOK_ENTRY(KERNEL32.DLL, _lopen) APIHOOK_ENTRY(KERNEL32.DLL, _lcreat)
APIHOOK_ENTRY(NTDLL.DLL, NtCreateFile) APIHOOK_ENTRY(NTDLL.DLL, NtOpenFile)
HOOK_END
IMPLEMENT_SHIM_END
|