Last active
March 28, 2020 14:14
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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