Skip to content

Instantly share code, notes, and snippets.

@meitinger
Last active March 28, 2020 14:14
Show Gist options
  • Save meitinger/7863a6d2830847c6e1845bc977e21007 to your computer and use it in GitHub Desktop.
Save meitinger/7863a6d2830847c6e1845bc977e21007 to your computer and use it in GitHub Desktop.
Stub for Kernel32.dll loaded by Qt5Core.dll (and others) to treat symlinks like ordinary files.
/* Copyright (C) 2017-2020, Manuel Meitinger
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define STRICT
#include <stddef.h>
#include <Windows.h>
// export replacement functions
#ifdef _WIN64
#pragma comment(linker, "/EXPORT:CreateFileW=InternalCreateFileW")
#pragma comment(linker, "/EXPORT:FindClose=InternalFindClose")
#pragma comment(linker, "/EXPORT:FindFirstFileExW=InternalFindFirstFileExW")
#pragma comment(linker, "/EXPORT:FindFirstFileW=InternalFindFirstFileW")
#pragma comment(linker, "/EXPORT:FindNextFileW=InternalFindNextFileW")
#pragma comment(linker, "/EXPORT:GetFileAttributesExW=InternalGetFileAttributesExW")
#pragma comment(linker, "/EXPORT:GetFileAttributesW=InternalGetFileAttributesW")
#else
#pragma comment(linker, "/EXPORT:CreateFileW=_InternalCreateFileW@28")
#pragma comment(linker, "/EXPORT:FindClose=_InternalFindClose@4")
#pragma comment(linker, "/EXPORT:FindFirstFileExW=_InternalFindFirstFileExW@24")
#pragma comment(linker, "/EXPORT:FindFirstFileW=_InternalFindFirstFileW@8")
#pragma comment(linker, "/EXPORT:FindNextFileW=_InternalFindNextFileW@8")
#pragma comment(linker, "/EXPORT:GetFileAttributesExW=_InternalGetFileAttributesExW@12")
#pragma comment(linker, "/EXPORT:GetFileAttributesW=_InternalGetFileAttributesW@4")
#endif
// forward other functions to the kernel
#pragma comment(linker, "/EXPORT:CancelIo=Kernel32.CancelIo")
#pragma comment(linker, "/EXPORT:CancelIoEx=Kernel32.CancelIoEx")
#pragma comment(linker, "/EXPORT:CloseHandle=Kernel32.CloseHandle")
#pragma comment(linker, "/EXPORT:CompareStringW=Kernel32.CompareStringW")
#pragma comment(linker, "/EXPORT:ConnectNamedPipe=Kernel32.ConnectNamedPipe")
#pragma comment(linker, "/EXPORT:CopyFileW=Kernel32.CopyFileW")
#pragma comment(linker, "/EXPORT:CreateDirectoryW=Kernel32.CreateDirectoryW")
#pragma comment(linker, "/EXPORT:CreateEventW=Kernel32.CreateEventW")
#pragma comment(linker, "/EXPORT:CreateFileMappingW=Kernel32.CreateFileMappingW")
#pragma comment(linker, "/EXPORT:CreateIoCompletionPort=Kernel32.CreateIoCompletionPort")
#pragma comment(linker, "/EXPORT:CreateMutexW=Kernel32.CreateMutexW")
#pragma comment(linker, "/EXPORT:CreateNamedPipeW=Kernel32.CreateNamedPipeW")
#pragma comment(linker, "/EXPORT:CreateProcessW=Kernel32.CreateProcessW")
#pragma comment(linker, "/EXPORT:CreateSemaphoreW=Kernel32.CreateSemaphoreW")
#pragma comment(linker, "/EXPORT:CreateThread=Kernel32.CreateThread")
#pragma comment(linker, "/EXPORT:DecodePointer=Kernel32.DecodePointer")
#pragma comment(linker, "/EXPORT:DeleteCriticalSection=Kernel32.DeleteCriticalSection")
#pragma comment(linker, "/EXPORT:DeleteFileW=Kernel32.DeleteFileW")
#pragma comment(linker, "/EXPORT:DeviceIoControl=Kernel32.DeviceIoControl")
#pragma comment(linker, "/EXPORT:DisableThreadLibraryCalls=Kernel32.DisableThreadLibraryCalls")
#pragma comment(linker, "/EXPORT:DuplicateHandle=Kernel32.DuplicateHandle")
#pragma comment(linker, "/EXPORT:EncodePointer=Kernel32.EncodePointer")
#pragma comment(linker, "/EXPORT:EnterCriticalSection=Kernel32.EnterCriticalSection")
#pragma comment(linker, "/EXPORT:FileTimeToSystemTime=Kernel32.FileTimeToSystemTime")
#pragma comment(linker, "/EXPORT:FindCloseChangeNotification=Kernel32.FindCloseChangeNotification")
#pragma comment(linker, "/EXPORT:FindFirstChangeNotificationW=Kernel32.FindFirstChangeNotificationW")
#pragma comment(linker, "/EXPORT:FindNextChangeNotification=Kernel32.FindNextChangeNotification")
#pragma comment(linker, "/EXPORT:FlushFileBuffers=Kernel32.FlushFileBuffers")
#pragma comment(linker, "/EXPORT:FormatMessageW=Kernel32.FormatMessageW")
#pragma comment(linker, "/EXPORT:FreeEnvironmentStringsW=Kernel32.FreeEnvironmentStringsW")
#pragma comment(linker, "/EXPORT:FreeLibrary=Kernel32.FreeLibrary")
#pragma comment(linker, "/EXPORT:GetCommandLineW=Kernel32.GetCommandLineW")
#pragma comment(linker, "/EXPORT:GetConsoleWindow=Kernel32.GetConsoleWindow")
#pragma comment(linker, "/EXPORT:GetCurrencyFormatW=Kernel32.GetCurrencyFormatW")
#pragma comment(linker, "/EXPORT:GetCurrentDirectoryW=Kernel32.GetCurrentDirectoryW")
#pragma comment(linker, "/EXPORT:GetCurrentProcess=Kernel32.GetCurrentProcess")
#pragma comment(linker, "/EXPORT:GetCurrentProcessId=Kernel32.GetCurrentProcessId")
#pragma comment(linker, "/EXPORT:GetCurrentThread=Kernel32.GetCurrentThread")
#pragma comment(linker, "/EXPORT:GetCurrentThreadId=Kernel32.GetCurrentThreadId")
#pragma comment(linker, "/EXPORT:GetDateFormatW=Kernel32.GetDateFormatW")
#pragma comment(linker, "/EXPORT:GetDiskFreeSpaceExW=Kernel32.GetDiskFreeSpaceExW")
#pragma comment(linker, "/EXPORT:GetDriveTypeW=Kernel32.GetDriveTypeW")
#pragma comment(linker, "/EXPORT:GetEnvironmentStringsW=Kernel32.GetEnvironmentStringsW")
#pragma comment(linker, "/EXPORT:GetExitCodeProcess=Kernel32.GetExitCodeProcess")
#pragma comment(linker, "/EXPORT:GetFileInformationByHandle=Kernel32.GetFileInformationByHandle")
#pragma comment(linker, "/EXPORT:GetFileInformationByHandleEx=Kernel32.GetFileInformationByHandleEx")
#pragma comment(linker, "/EXPORT:GetFileType=Kernel32.GetFileType")
#pragma comment(linker, "/EXPORT:GetFullPathNameW=Kernel32.GetFullPathNameW")
#pragma comment(linker, "/EXPORT:GetGeoInfoW=Kernel32.GetGeoInfoW")
#pragma comment(linker, "/EXPORT:GetLastError=Kernel32.GetLastError")
#pragma comment(linker, "/EXPORT:GetLocaleInfoW=Kernel32.GetLocaleInfoW")
#pragma comment(linker, "/EXPORT:GetLocalTime=Kernel32.GetLocalTime")
#pragma comment(linker, "/EXPORT:GetLogicalDrives=Kernel32.GetLogicalDrives")
#pragma comment(linker, "/EXPORT:GetLongPathNameW=Kernel32.GetLongPathNameW")
#pragma comment(linker, "/EXPORT:GetModuleFileNameW=Kernel32.GetModuleFileNameW")
#pragma comment(linker, "/EXPORT:GetModuleHandleA=Kernel32.GetModuleHandleA")
#pragma comment(linker, "/EXPORT:GetModuleHandleExW=Kernel32.GetModuleHandleExW")
#pragma comment(linker, "/EXPORT:GetModuleHandleW=Kernel32.GetModuleHandleW")
#pragma comment(linker, "/EXPORT:GetNativeSystemInfo=Kernel32.GetNativeSystemInfo")
#pragma comment(linker, "/EXPORT:GetProcAddress=Kernel32.GetProcAddress")
#pragma comment(linker, "/EXPORT:GetProcessId=Kernel32.GetProcessId")
#pragma comment(linker, "/EXPORT:GetQueuedCompletionStatus=Kernel32.GetQueuedCompletionStatus")
#pragma comment(linker, "/EXPORT:GetStartupInfoW=Kernel32.GetStartupInfoW")
#pragma comment(linker, "/EXPORT:GetStdHandle=Kernel32.GetStdHandle")
#pragma comment(linker, "/EXPORT:GetSystemDirectoryW=Kernel32.GetSystemDirectoryW")
#pragma comment(linker, "/EXPORT:GetSystemInfo=Kernel32.GetSystemInfo")
#pragma comment(linker, "/EXPORT:GetSystemTime=Kernel32.GetSystemTime")
#pragma comment(linker, "/EXPORT:GetSystemTimeAsFileTime=Kernel32.GetSystemTimeAsFileTime")
#pragma comment(linker, "/EXPORT:GetTempPathW=Kernel32.GetTempPathW")
#pragma comment(linker, "/EXPORT:GetThreadPriority=Kernel32.GetThreadPriority")
#pragma comment(linker, "/EXPORT:GetTickCount=Kernel32.GetTickCount")
#pragma comment(linker, "/EXPORT:GetTickCount64=Kernel32.GetTickCount64")
#pragma comment(linker, "/EXPORT:GetTimeFormatW=Kernel32.GetTimeFormatW")
#pragma comment(linker, "/EXPORT:GetTimeZoneInformation=Kernel32.GetTimeZoneInformation")
#pragma comment(linker, "/EXPORT:GetUserDefaultLCID=Kernel32.GetUserDefaultLCID")
#pragma comment(linker, "/EXPORT:GetUserDefaultUILanguage=Kernel32.GetUserDefaultUILanguage")
#pragma comment(linker, "/EXPORT:GetUserGeoID=Kernel32.GetUserGeoID")
#pragma comment(linker, "/EXPORT:GetUserPreferredUILanguages=Kernel32.GetUserPreferredUILanguages")
#pragma comment(linker, "/EXPORT:GetVolumeInformationW=Kernel32.GetVolumeInformationW")
#pragma comment(linker, "/EXPORT:GetVolumeNameForVolumeMountPointW=Kernel32.GetVolumeNameForVolumeMountPointW")
#pragma comment(linker, "/EXPORT:GetVolumePathNamesForVolumeNameW=Kernel32.GetVolumePathNamesForVolumeNameW")
#pragma comment(linker, "/EXPORT:GetVolumePathNameW=Kernel32.GetVolumePathNameW")
#pragma comment(linker, "/EXPORT:InitializeCriticalSection=Kernel32.InitializeCriticalSection")
#pragma comment(linker, "/EXPORT:InitializeSListHead=Kernel32.InitializeSListHead")
#pragma comment(linker, "/EXPORT:IsDBCSLeadByteEx=Kernel32.IsDBCSLeadByteEx")
#pragma comment(linker, "/EXPORT:IsDebuggerPresent=Kernel32.IsDebuggerPresent")
#pragma comment(linker, "/EXPORT:IsProcessorFeaturePresent=Kernel32.IsProcessorFeaturePresent")
#pragma comment(linker, "/EXPORT:LCMapStringW=Kernel32.LCMapStringW")
#pragma comment(linker, "/EXPORT:LeaveCriticalSection=Kernel32.LeaveCriticalSection")
#pragma comment(linker, "/EXPORT:LoadLibraryA=Kernel32.LoadLibraryA")
#pragma comment(linker, "/EXPORT:LoadLibraryW=Kernel32.LoadLibraryW")
#pragma comment(linker, "/EXPORT:LocalFree=Kernel32.LocalFree")
#pragma comment(linker, "/EXPORT:MapViewOfFile=Kernel32.MapViewOfFile")
#pragma comment(linker, "/EXPORT:MoveFileExW=Kernel32.MoveFileExW")
#pragma comment(linker, "/EXPORT:MoveFileW=Kernel32.MoveFileW")
#pragma comment(linker, "/EXPORT:MultiByteToWideChar=Kernel32.MultiByteToWideChar")
#pragma comment(linker, "/EXPORT:OpenFileMappingW=Kernel32.OpenFileMappingW")
#pragma comment(linker, "/EXPORT:OpenProcess=Kernel32.OpenProcess")
#pragma comment(linker, "/EXPORT:OutputDebugStringW=Kernel32.OutputDebugStringW")
#pragma comment(linker, "/EXPORT:PeekNamedPipe=Kernel32.PeekNamedPipe")
#pragma comment(linker, "/EXPORT:PostQueuedCompletionStatus=Kernel32.PostQueuedCompletionStatus")
#pragma comment(linker, "/EXPORT:QueryPerformanceCounter=Kernel32.QueryPerformanceCounter")
#pragma comment(linker, "/EXPORT:QueryPerformanceFrequency=Kernel32.QueryPerformanceFrequency")
#pragma comment(linker, "/EXPORT:RaiseFailFastException=Kernel32.RaiseFailFastException")
#pragma comment(linker, "/EXPORT:ReadFile=Kernel32.ReadFile")
#pragma comment(linker, "/EXPORT:ReadFileEx=Kernel32.ReadFileEx")
#pragma comment(linker, "/EXPORT:RegisterWaitForSingleObject=Kernel32.RegisterWaitForSingleObject")
#pragma comment(linker, "/EXPORT:ReleaseMutex=Kernel32.ReleaseMutex")
#pragma comment(linker, "/EXPORT:ReleaseSemaphore=Kernel32.ReleaseSemaphore")
#pragma comment(linker, "/EXPORT:RemoveDirectoryW=Kernel32.RemoveDirectoryW")
#pragma comment(linker, "/EXPORT:ResetEvent=Kernel32.ResetEvent")
#pragma comment(linker, "/EXPORT:ResumeThread=Kernel32.ResumeThread")
#pragma comment(linker, "/EXPORT:RtlAddFunctionTable=Kernel32.RtlAddFunctionTable")
#pragma comment(linker, "/EXPORT:RtlCaptureContext=Kernel32.RtlCaptureContext")
#pragma comment(linker, "/EXPORT:RtlLookupFunctionEntry=Kernel32.RtlLookupFunctionEntry")
#pragma comment(linker, "/EXPORT:RtlVirtualUnwind=Kernel32.RtlVirtualUnwind")
#pragma comment(linker, "/EXPORT:SetCurrentDirectoryW=Kernel32.SetCurrentDirectoryW")
#pragma comment(linker, "/EXPORT:SetEndOfFile=Kernel32.SetEndOfFile")
#pragma comment(linker, "/EXPORT:SetErrorMode=Kernel32.SetErrorMode")
#pragma comment(linker, "/EXPORT:SetEvent=Kernel32.SetEvent")
#pragma comment(linker, "/EXPORT:SetFilePointer=Kernel32.SetFilePointer")
#pragma comment(linker, "/EXPORT:SetFilePointerEx=Kernel32.SetFilePointerEx")
#pragma comment(linker, "/EXPORT:SetFileTime=Kernel32.SetFileTime")
#pragma comment(linker, "/EXPORT:SetThreadPriority=Kernel32.SetThreadPriority")
#pragma comment(linker, "/EXPORT:SetUnhandledExceptionFilter=Kernel32.SetUnhandledExceptionFilter")
#pragma comment(linker, "/EXPORT:Sleep=Kernel32.Sleep")
#pragma comment(linker, "/EXPORT:SleepEx=Kernel32.SleepEx")
#pragma comment(linker, "/EXPORT:SwitchToThread=Kernel32.SwitchToThread")
#pragma comment(linker, "/EXPORT:SystemTimeToFileTime=Kernel32.SystemTimeToFileTime")
#pragma comment(linker, "/EXPORT:SystemTimeToTzSpecificLocalTime=Kernel32.SystemTimeToTzSpecificLocalTime")
#pragma comment(linker, "/EXPORT:TerminateProcess=Kernel32.TerminateProcess")
#pragma comment(linker, "/EXPORT:TerminateThread=Kernel32.TerminateThread")
#pragma comment(linker, "/EXPORT:TlsAlloc=Kernel32.TlsAlloc")
#pragma comment(linker, "/EXPORT:TlsFree=Kernel32.TlsFree")
#pragma comment(linker, "/EXPORT:TlsGetValue=Kernel32.TlsGetValue")
#pragma comment(linker, "/EXPORT:TlsSetValue=Kernel32.TlsSetValue")
#pragma comment(linker, "/EXPORT:TzSpecificLocalTimeToSystemTime=Kernel32.TzSpecificLocalTimeToSystemTime")
#pragma comment(linker, "/EXPORT:UnhandledExceptionFilter=Kernel32.UnhandledExceptionFilter")
#pragma comment(linker, "/EXPORT:UnmapViewOfFile=Kernel32.UnmapViewOfFile")
#pragma comment(linker, "/EXPORT:UnregisterWaitEx=Kernel32.UnregisterWaitEx")
#pragma comment(linker, "/EXPORT:VirtualAlloc=Kernel32.VirtualAlloc")
#pragma comment(linker, "/EXPORT:VirtualFree=Kernel32.VirtualFree")
#pragma comment(linker, "/EXPORT:VirtualProtect=Kernel32.VirtualProtect")
#pragma comment(linker, "/EXPORT:VirtualQuery=Kernel32.VirtualQuery")
#pragma comment(linker, "/EXPORT:WaitForMultipleObjects=Kernel32.WaitForMultipleObjects")
#pragma comment(linker, "/EXPORT:WaitForSingleObject=Kernel32.WaitForSingleObject")
#pragma comment(linker, "/EXPORT:WaitForSingleObjectEx=Kernel32.WaitForSingleObjectEx")
#pragma comment(linker, "/EXPORT:WideCharToMultiByte=Kernel32.WideCharToMultiByte")
#pragma comment(linker, "/EXPORT:WriteFile=Kernel32.WriteFile")
#pragma comment(linker, "/EXPORT:WriteFileEx=Kernel32.WriteFileEx")
// use own entry point to avoid CRT
#pragma comment(linker, "/ENTRY:DllMain")
// import kernel functions
#pragma comment(lib, "Kernel32.lib")
BOOL WINAPI DllMain(
__in HINSTANCE hinstDLL,
__in DWORD fdwReason,
__in LPVOID lpvReserved)
{
// disable thread calls to DllMain
if (fdwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hinstDLL);
}
// always succeed
return TRUE;
}
typedef struct tagFINDINFO
{
HANDLE hKernelHandle;
BOOL bIsClosed;
INT iSearchRootLength;
WCHAR cSearchRootAndBuffer[ANYSIZE_ARRAY];
} FINDINFO, *LPFINDINFO;
#define INVALID_FIND_INFO ((LPFINDINFO)INVALID_HANDLE_VALUE)
static LPFINDINFO CreateFindInfo(
__in HANDLE hKernelHandle,
__in LPCWSTR lpFileName)
{
INT iSearchRootLength;
INT iOffset;
LPFINDINFO lpFindInfo;
DWORD dwError;
// check if the FindFirstFile* operation succeeded
if (hKernelHandle == INVALID_HANDLE_VALUE)
{
return INVALID_FIND_INFO;
}
// get the last backslash in the search file name
for (iOffset = 0, iSearchRootLength = 0; lpFileName[iOffset]; iOffset++)
{
if (lpFileName[iOffset] == L'\\')
{
iSearchRootLength = iOffset + 1;
}
}
// allocate the internal structure
lpFindInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, offsetof(FINDINFO, cSearchRootAndBuffer) + sizeof(WCHAR) * (iSearchRootLength + MAX_PATH));
if (!lpFindInfo)
{
// on error close the kernel's find operation and fail with HeapAlloc's error code
dwError = GetLastError();
FindClose(hKernelHandle);
SetLastError(dwError);
return INVALID_FIND_INFO;
}
// initialize and return the internal structure
lpFindInfo->hKernelHandle = hKernelHandle;
lpFindInfo->iSearchRootLength = iSearchRootLength;
while (iSearchRootLength-- > 0)
{
lpFindInfo->cSearchRootAndBuffer[iSearchRootLength] = lpFileName[iSearchRootLength];
}
return lpFindInfo;
}
static BOOL VerifyFindInfo(
__in LPFINDINFO lpFindInfo)
{
SIZE_T size;
// ensure the pointer itself is valid
if (lpFindInfo && lpFindInfo != INVALID_FIND_INFO && HeapValidate(GetProcessHeap(), 0, lpFindInfo))
{
// check if the size matches the internal structure and FindClose has not been called
size = HeapSize(GetProcessHeap(), 0, lpFindInfo);
if (size >= offsetof(FINDINFO, cSearchRootAndBuffer) && size == (offsetof(FINDINFO, cSearchRootAndBuffer) + sizeof(WCHAR) * (lpFindInfo->iSearchRootLength + MAX_PATH)) && !lpFindInfo->bIsClosed)
{
// succeed
return TRUE;
}
}
// fail with the proper error code
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
static BOOL DestroyFindInfo(
__in LPFINDINFO lpFindInfo)
{
// simply free the internal structure
return HeapFree(GetProcessHeap(), 0, lpFindInfo);
}
static BOOL OpenTargetAndTryGetFileSize(
__in LPCWSTR lpFileName,
__inout LPDWORD lpFileSizeHigh,
__inout LPDWORD lpFileSizeLow)
{
HANDLE hFile;
LARGE_INTEGER liSize;
// open the target file with minimum rights
hFile = CreateFileW(lpFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
// fail because the file is inaccessible
return FALSE;
}
// try to get and store the proper file size
if (GetFileSizeEx(hFile, &liSize))
{
*lpFileSizeHigh = liSize.HighPart;
*lpFileSizeLow = liSize.LowPart;
}
// close the file and succeed
CloseHandle(hFile);
return TRUE;
}
static BOOL ResolveFindData(
__in LPFINDINFO lpFindInfo,
__inout LPWIN32_FIND_DATAW lpFindFileData)
{
INT iOffset;
// check if the found file has a reparse point
if ((lpFindFileData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
{
// clear the reparse flag and append the file name to the search root
lpFindFileData->dwFileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;
for (iOffset = 0; iOffset < MAX_PATH; iOffset++)
{
lpFindInfo->cSearchRootAndBuffer[lpFindInfo->iSearchRootLength + iOffset] = lpFindFileData->cFileName[iOffset];
}
// get the target's file size
if (!OpenTargetAndTryGetFileSize(lpFindInfo->cSearchRootAndBuffer, &lpFindFileData->nFileSizeHigh, &lpFindFileData->nFileSizeLow))
{
// fail because the file is inaccessible
return FALSE;
}
}
// succeed
return TRUE;
}
__out HANDLE WINAPI InternalCreateFileW(
__in LPCWSTR lpFileName,
__in DWORD dwDesiredAccess,
__in DWORD dwShareMode,
__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
__in DWORD dwCreationDisposition,
__in DWORD dwFlagsAndAttributes,
__in_opt HANDLE hTemplateFile)
{
// forward the call but clear the reparse point flag
return CreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes & ~FILE_ATTRIBUTE_REPARSE_POINT, hTemplateFile);
}
DWORD WINAPI InternalGetFileAttributesW(
__in LPCWSTR lpFileName)
{
DWORD dwAttributes;
DWORD dwHighDummy;
DWORD dwLowDummy;
// forward the call to the kernel
dwAttributes = GetFileAttributesW(lpFileName);
// check if the attributes are valid and the file has a reparse point
if (dwAttributes != INVALID_FILE_ATTRIBUTES && (dwAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
{
// clear the reparse flag and open the target file
dwAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;
if (!OpenTargetAndTryGetFileSize(lpFileName, &dwHighDummy, &dwLowDummy))
{
// fail because the file is inaccessible
return INVALID_FILE_ATTRIBUTES;
}
}
// return the attributes
return dwAttributes;
}
BOOL WINAPI InternalGetFileAttributesExW(
__in LPCWSTR lpFileName,
__in GET_FILEEX_INFO_LEVELS fInfoLevelId,
__out LPVOID lpFileInformation)
{
// forward the call to the kernel
if (!GetFileAttributesExW(lpFileName, fInfoLevelId, lpFileInformation))
{
return FALSE;
}
// check if the file has a reparse point
if ((((LPWIN32_FILE_ATTRIBUTE_DATA)lpFileInformation)->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
{
// clear the reparse flag and get the size of the target file
((LPWIN32_FILE_ATTRIBUTE_DATA)lpFileInformation)->dwFileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;
if (!OpenTargetAndTryGetFileSize(lpFileName, &((LPWIN32_FILE_ATTRIBUTE_DATA)lpFileInformation)->nFileSizeHigh, &((LPWIN32_FILE_ATTRIBUTE_DATA)lpFileInformation)->nFileSizeLow))
{
// fail because the file is inaccessible
return FALSE;
}
}
// succeed
return TRUE;
}
BOOL WINAPI InternalFindClose(
__inout LPFINDINFO lpFindInfo)
{
// check if the given pointer to the internal structure is valid and close the kernel's find operation
if (VerifyFindInfo(lpFindInfo) && FindClose(lpFindInfo->hKernelHandle))
{
// set the closed flag and try to destroy the internal structure
lpFindInfo->bIsClosed = TRUE;
DestroyFindInfo(lpFindInfo);
// succeed
return TRUE;
}
// fail (error code set by VerifyFindInfo or FindClose)
return FALSE;
}
BOOL WINAPI InternalFindNextFileW(
__in LPFINDINFO lpFindInfo,
__out LPWIN32_FIND_DATAW lpFindFileData)
{
// check if the given pointer to the internal structure is valid
if (VerifyFindInfo(lpFindInfo))
{
// continue looking for files
while (FindNextFileW(lpFindInfo->hKernelHandle, lpFindFileData))
{
// check if the file is accessible
if (ResolveFindData(lpFindInfo, lpFindFileData))
{
// succeed
return TRUE;
}
}
}
// fail (error code set by VerifyFindInfo or FindNextFileW)
return FALSE;
}
static __out LPFINDINFO FindFirstFileBase(
__in HANDLE hFindFile,
__in LPCWSTR lpFileName,
__inout LPWIN32_FIND_DATAW lpFindFileData)
{
LPFINDINFO lpFindInfo;
DWORD dwError;
// build the internal structure
lpFindInfo = CreateFindInfo(hFindFile, lpFileName);
// check if the found file and all other files are inaccessible
if (lpFindInfo != INVALID_FIND_INFO && !ResolveFindData(lpFindInfo, lpFindFileData) && !InternalFindNextFileW(lpFindInfo, lpFindFileData))
{
// call off the search and fail with InternalFindNextFileW's error code
dwError = GetLastError();
InternalFindClose(lpFindInfo);
SetLastError(dwError);
return INVALID_FIND_INFO;
}
// return the pointer to the internal structure
return lpFindInfo;
}
__out LPFINDINFO WINAPI InternalFindFirstFileW(
__in LPCWSTR lpFileName,
__out LPWIN32_FIND_DATAW lpFindFileData)
{
// forward the call to the kernel and perform the base operation
return FindFirstFileBase(FindFirstFileW(lpFileName, lpFindFileData), lpFileName, lpFindFileData);
}
__out LPFINDINFO WINAPI InternalFindFirstFileExW(
__in LPCWSTR lpFileName,
__in FINDEX_INFO_LEVELS fInfoLevelId,
__out LPVOID lpFindFileData,
__in FINDEX_SEARCH_OPS fSearchOp,
__reserved LPVOID lpSearchFilter,
__in DWORD dwAdditionalFlags)
{
// forward the call to the kernel and perform the base operation
return FindFirstFileBase(FindFirstFileExW(lpFileName, fInfoLevelId, lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags), lpFileName, (LPWIN32_FIND_DATAW)lpFindFileData);
}
Param (
[Parameter(Mandatory = $true)]
[string] $Path
)
Set-StrictMode -Version Latest
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
$data = [System.IO.File]::ReadAllBytes($Path)
If ([System.BitConverter]::ToInt16($data, 0) -ne 0x5A4D) { Throw 'DOS header missing.' }
$IMAGE_NT_HEADERS = [System.BitConverter]::ToInt32($data, 0x3c)
If ([System.BitConverter]::ToInt32($data, $IMAGE_NT_HEADERS) -ne 0x00004550 ) { Throw 'PE header missing.' }
$IMAGE_FILE_HEADER = $IMAGE_NT_HEADERS + 4
$SizeOfOptionalHeader = [System.BitConverter]::ToInt16($data, $IMAGE_FILE_HEADER + 16)
If ($SizeOfOptionalHeader -eq 0) { Throw 'Empty optional header.' }
$IMAGE_OPTIONAL_HEADER = $IMAGE_FILE_HEADER + 20
Switch ([System.BitConverter]::ToInt16($data, $IMAGE_OPTIONAL_HEADER)) {
0x010B { $importTableOffset = 104 }
0x020B { $importTableOffset = 120 }
Default { Throw 'Unknown optional header.' }
}
$NumberOfRvaAndSizes = [System.BitConverter]::ToInt32($data, $IMAGE_OPTIONAL_HEADER + $importTableOffset - 12)
If ($NumberOfRvaAndSizes -lt 2) { Throw 'No import table exists.' }
$IMAGE_DATA_DIRECTORY = $IMAGE_OPTIONAL_HEADER + $importTableOffset
$ImportTableVirtualAddress = [System.BitConverter]::ToInt32($data, $IMAGE_DATA_DIRECTORY)
$ImportTableSize = [System.BitConverter]::ToInt32($data, $IMAGE_DATA_DIRECTORY + 4)
If ($ImportTableVirtualAddress -eq 0 -or $ImportTableSize -eq 0) { Throw 'Empty import table.' }
$NumberOfSections = [System.BitConverter]::ToInt16($data, $IMAGE_FILE_HEADER + 2)
$IMAGE_SECTION_HEADER = $IMAGE_OPTIONAL_HEADER + $SizeOfOptionalHeader
For ($i = 0; $i -lt $NumberOfSections; $i++) {
$SectionVirtualSize = [System.BitConverter]::ToInt32($data, $IMAGE_SECTION_HEADER + 8)
If ($SectionVirtualSize -eq 0) {
$SectionVirtualSize = [System.BitConverter]::ToInt32($data, $IMAGE_SECTION_HEADER + 16)
}
$SectionVirtualAddress = [System.BitConverter]::ToInt32($data, $IMAGE_SECTION_HEADER + 12)
If ($ImportTableVirtualAddress -ge $SectionVirtualAddress -and $ImportTableVirtualAddress -le ($SectionVirtualAddress + $SectionVirtualSize)) {
Break
}
$IMAGE_SECTION_HEADER += 40
}
If ($i -ge $NumberOfSections) { Throw 'Image table not found in any section.' }
$PointerToRawData = [System.BitConverter]::ToInt32($data, $IMAGE_SECTION_HEADER + 20)
$rvaOffset = $PointerToRawData - $SectionVirtualAddress
$IMAGE_IMPORT_DESCRIPTOR = $rvaOffset + $ImportTableVirtualAddress
While ($true) {
If ($ImportTableSize -eq 0) { Throw 'Improperly terminated import table.' }
If ([System.BitConverter]::ToInt32($data, $IMAGE_IMPORT_DESCRIPTOR) -eq 0) { Break }
$Name = $rvaOffset + [System.BitConverter]::ToInt32($data, $IMAGE_IMPORT_DESCRIPTOR + 12)
Switch ([System.Text.Encoding]::Default.GetString($data, $Name, [array]::IndexOf($data, [byte]0, $Name) - $Name)) {
'KERNEL32.DLL' {
[array]::Copy([System.Text.Encoding]::Default.GetBytes('QTKERNEL'), 0, $data, $Name, 8)
[System.IO.File]::WriteAllBytes($Path, $data)
Write-Host -Object 'Patched successfully.'
}
'QTKERNEL.DLL' {
Write-Host -Object 'Already patched.'
}
}
$IMAGE_IMPORT_DESCRIPTOR += 20
$ImportTableSize -= 20
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment