From ed521ae4c7432c5e9c85cb0d33479f990f64dfff Mon Sep 17 00:00:00 2001 From: llccd Date: Sun, 19 Sep 2021 17:56:46 +0800 Subject: [PATCH] Add systemwide patch --- .gitmodules | 3 + FakeDLL/FakeDLL.cpp | 103 +++++ FakeDLL/FakeDLL.vcxproj | 65 +++ FakeDLL/FakeDLL.vcxproj.filters | 22 + FakeDLL/FakeDLL.vcxproj.user | 4 + README.md | 14 +- Shell32Patcher.sln | 7 + Shell32Patcher/Shell32Patcher.cpp | 431 +++++++++++++++--- Shell32Patcher/Shell32Patcher.vcxproj | 15 + Shell32Patcher/Shell32Patcher.vcxproj.filters | 5 + Shell32Patcher/ntdll.h | 47 ++ zydis | 1 + 12 files changed, 651 insertions(+), 66 deletions(-) create mode 100644 .gitmodules create mode 100644 FakeDLL/FakeDLL.cpp create mode 100644 FakeDLL/FakeDLL.vcxproj create mode 100644 FakeDLL/FakeDLL.vcxproj.filters create mode 100644 FakeDLL/FakeDLL.vcxproj.user create mode 100644 Shell32Patcher/ntdll.h create mode 160000 zydis diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..775ed33 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "zydis"] + path = zydis + url = https://github.com/zyantific/zydis/ diff --git a/FakeDLL/FakeDLL.cpp b/FakeDLL/FakeDLL.cpp new file mode 100644 index 0000000..8625340 --- /dev/null +++ b/FakeDLL/FakeDLL.cpp @@ -0,0 +1,103 @@ +#include "ntdll.h" +#include +#include + +extern "C" __declspec(dllexport) void APIENTRY BriCreateBrokeredEvent(); +extern "C" __declspec(dllexport) void APIENTRY BriDeleteBrokeredEvent(); +extern "C" __declspec(dllexport) void APIENTRY EaCreateAggregatedEvent(); +extern "C" __declspec(dllexport) void APIENTRY EACreateAggregateEvent(); +extern "C" __declspec(dllexport) void APIENTRY EaQueryAggregatedEventParameters(); +extern "C" __declspec(dllexport) void APIENTRY EAQueryAggregateEventData(); +extern "C" __declspec(dllexport) void APIENTRY EaFreeAggregatedEventParameters(); +extern "C" __declspec(dllexport) void APIENTRY EaDeleteAggregatedEvent(); +extern "C" __declspec(dllexport) void APIENTRY EADeleteAggregateEvent(); + +BOOL DeleteSection(LPCWSTR path) +{ + HANDLE hLink; + UNICODE_STRING name; + + RtlInitUnicodeString(&name, path); + OBJECT_ATTRIBUTES oa = { sizeof(oa), NULL, &name, OBJ_CASE_INSENSITIVE, NULL, NULL }; + + if (NtOpenSection(&hLink, DELETE, &oa)) + return FALSE; + + BOOL returnValue = NtMakeTemporaryObject(hLink) == 0; + + NtClose(hLink); + + return returnValue; +} + +BOOL DeleteObjectLink(LPCWSTR path) +{ + HANDLE hLink; + UNICODE_STRING name; + SECURITY_DESCRIPTOR sd; + + RtlInitUnicodeString(&name, path); + OBJECT_ATTRIBUTES oa = { sizeof(oa), NULL, &name, OBJ_CASE_INSENSITIVE, NULL, NULL }; + + if (NtOpenSymbolicLinkObject(&hLink, WRITE_DAC, &oa)) + return FALSE; + + InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); +#pragma warning( suppress : 6248 ) // Disable warning as setting a NULL DACL is intentional here + SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); + + if (!SetKernelObjectSecurity(hLink, DACL_SECURITY_INFORMATION, &sd) | NtClose(hLink)) + return FALSE; + + if (NtOpenSymbolicLinkObject(&hLink, DELETE, &oa)) + return FALSE; + + BOOL returnValue = NtMakeTemporaryObject(hLink) == 0; + + NtClose(hLink); + + return returnValue; +} + +HANDLE ObjectManagerCreateSymlink(LPCWSTR linkname, LPCWSTR targetname) +{ + UNICODE_STRING name, target; + HANDLE hLink; + PSECURITY_DESCRIPTOR sd; + + if (!ConvertStringSecurityDescriptorToSecurityDescriptorW( + L"D:(A;;GA;;;BA)(A;;GR;;;RC)(A;;GR;;;WD)(A;;GR;;;AC)(A;;GR;;;S-1-15-2-2)S:(ML;;NW;;;LW)", + SDDL_REVISION_1, &sd, 0)) return NULL; + + RtlInitUnicodeString(&name, linkname); + RtlInitUnicodeString(&target, targetname); + OBJECT_ATTRIBUTES oa = { sizeof(oa), NULL, &name, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, NULL, NULL }; + + if (NtCreateSymbolicLinkObject(&hLink, SYMBOLIC_LINK_ALL_ACCESS, &oa, &target)) + return NULL; + + SetKernelObjectSecurity(hLink, DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, sd); + + return hLink; +} + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +{ + if (ul_reason_for_call == DLL_PROCESS_ATTACH) + { + DeleteObjectLink(L"\\KnownDlls\\EventAggregation.dll"); + if (DeleteSection(L"\\KnownDlls\\shell32.dll")) + ObjectManagerCreateSymlink(L"\\KnownDlls\\shell32.dll", L"\\BaseNamedObjects\\shell32.dll"); + } + return TRUE; +} + +void APIENTRY BriCreateBrokeredEvent() { } +void APIENTRY BriDeleteBrokeredEvent() { } +void APIENTRY EaCreateAggregatedEvent() { } +void APIENTRY EACreateAggregateEvent() { } +void APIENTRY EaQueryAggregatedEventParameters() { } +void APIENTRY EAQueryAggregateEventData() { } +void APIENTRY EaFreeAggregatedEventParameters() { } +void APIENTRY EaDeleteAggregatedEvent() { } +void APIENTRY EADeleteAggregateEvent() { } \ No newline at end of file diff --git a/FakeDLL/FakeDLL.vcxproj b/FakeDLL/FakeDLL.vcxproj new file mode 100644 index 0000000..390b4c5 --- /dev/null +++ b/FakeDLL/FakeDLL.vcxproj @@ -0,0 +1,65 @@ + + + + + Release + x64 + + + + 16.0 + Win32Proj + {16eef1dc-bbff-4b4a-93ea-9f9162ac4902} + FakeDLL + 10.0 + + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + false + false + + + + Level3 + true + true + false + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + None + false + false + MultiThreaded + $(SolutionDir)Shell32Patcher + + + Console + true + true + false + ntdll.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + \ No newline at end of file diff --git a/FakeDLL/FakeDLL.vcxproj.filters b/FakeDLL/FakeDLL.vcxproj.filters new file mode 100644 index 0000000..05de2a8 --- /dev/null +++ b/FakeDLL/FakeDLL.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/FakeDLL/FakeDLL.vcxproj.user b/FakeDLL/FakeDLL.vcxproj.user new file mode 100644 index 0000000..0f14913 --- /dev/null +++ b/FakeDLL/FakeDLL.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/README.md b/README.md index 6d3f193..c1ccd4d 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,18 @@ Shell32Patcher allows you to use classic context menu in Windows 11 file explore ## Usage +### Non-persist patch + Uncheck 'Launch folder windows in a separate process' -Run program. The patch will take effect immediately +Run program. The patch will take effect immediately and apply to your current session only + +Newly started explorer.exe remains unpatched. To revert, simply restart explorer.exe + +### Systemwide patch + +To perform systemwide patch, you need to run program using an administrative account (but **not** `NT AUTHORITY\SYSTEM`) with commandline option `-p` + +The patch will apply to all users and all newly started processes -To revert, simply restart explorer.exe +To revert systemwide patch, you must restart your computer diff --git a/Shell32Patcher.sln b/Shell32Patcher.sln index 5874d66..7afd2ec 100644 --- a/Shell32Patcher.sln +++ b/Shell32Patcher.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.31424.327 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Shell32Patcher", "Shell32Patcher\Shell32Patcher.vcxproj", "{72A07A64-5D50-4561-A3A1-0BF42710A1BB}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FakeDLL", "FakeDLL\FakeDLL.vcxproj", "{16EEF1DC-BBFF-4B4A-93EA-9F9162AC4902}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -18,6 +20,11 @@ Global {72A07A64-5D50-4561-A3A1-0BF42710A1BB}.Release|x64.Build.0 = Release|x64 {72A07A64-5D50-4561-A3A1-0BF42710A1BB}.ReleaseW|x64.ActiveCfg = ReleaseW|x64 {72A07A64-5D50-4561-A3A1-0BF42710A1BB}.ReleaseW|x64.Build.0 = ReleaseW|x64 + {16EEF1DC-BBFF-4B4A-93EA-9F9162AC4902}.Debug|x64.ActiveCfg = Release|x64 + {16EEF1DC-BBFF-4B4A-93EA-9F9162AC4902}.Release|x64.ActiveCfg = Release|x64 + {16EEF1DC-BBFF-4B4A-93EA-9F9162AC4902}.Release|x64.Build.0 = Release|x64 + {16EEF1DC-BBFF-4B4A-93EA-9F9162AC4902}.ReleaseW|x64.ActiveCfg = Release|x64 + {16EEF1DC-BBFF-4B4A-93EA-9F9162AC4902}.ReleaseW|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Shell32Patcher/Shell32Patcher.cpp b/Shell32Patcher/Shell32Patcher.cpp index d215b66..0f4048a 100644 --- a/Shell32Patcher/Shell32Patcher.cpp +++ b/Shell32Patcher/Shell32Patcher.cpp @@ -1,78 +1,381 @@ #include -#include -#include +#include "ntdll.h" +#include +#include #include #include -#pragma comment(lib, "Dbghelp.lib") -#pragma comment(lib, "Wtsapi32.lib") #ifndef _CONSOLE #define printf(...) {} +#define EXIT(msg, x) {ExitProcess(x);} +#else +#define EXIT(msg, x) {puts(msg); return x;} #endif +static HANDLE heap = 0; + +static WCHAR szShell32[MAX_PATH]; + +// xor rdi, rdi +// nop +static const char patchData[] = "\x48\x31\xFF\x0F\x18\x24\x00"; + +constexpr const char guid[] = "\xB1\xC5\x06\xB3\xF2\xB4\x3C\x47\xB6\xFF\x70\x1B\x24\x6C\xE2\xD2"; + +PIMAGE_SECTION_HEADER findSection(PIMAGE_NT_HEADERS64 pNT, const char* str) +{ + auto pSection = IMAGE_FIRST_SECTION(pNT); + + for (DWORD64 i = 0; i < pNT->FileHeader.NumberOfSections; i++) + if (CSTR_EQUAL == CompareStringA(LOCALE_INVARIANT, 0, (char*)pSection[i].Name, -1, str, -1)) + return pSection + i; + + return NULL; +} + +DWORD64 pattenMatch(DWORD64 base, PIMAGE_SECTION_HEADER pSection, const void* str, DWORD64 size) +{ + auto rdata = base + pSection->VirtualAddress; + + for (DWORD64 i = 0; i < pSection->SizeOfRawData; i += 4) + if (!memcmp((void*)(rdata + i), str, size)) return pSection->VirtualAddress + i; + + return -1; +} + +DWORD64 searchXref(ZydisDecoder* decoder, DWORD64 base, PRUNTIME_FUNCTION func, DWORD64 target) +{ + auto IP = base + func->BeginAddress; + auto length = (ZyanUSize)func->EndAddress - func->BeginAddress; + ZydisDecodedInstruction instruction; + + while (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(decoder, (void*)IP, length, &instruction))) + { + IP += instruction.length; + length -= instruction.length; + if (instruction.operand_count == 2 && + instruction.mnemonic == ZYDIS_MNEMONIC_LEA && + instruction.operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY && + instruction.operands[1].mem.base == ZYDIS_REGISTER_RIP && + instruction.operands[1].mem.disp.value + IP == target + base && + instruction.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER) + return IP - base; + } + + return 0; +} + HMODULE GetModule(HANDLE hProcess, LPCWSTR target) { - DWORD cb, cbNeeded; - WCHAR szModName[MAX_PATH]; - EnumProcessModulesEx(hProcess, NULL, 0, &cb, LIST_MODULES_64BIT); - auto hMods = (HMODULE*)LocalAlloc(NONZEROLPTR, cb); - if (!hMods) return NULL; - EnumProcessModulesEx(hProcess, hMods, cb, &cbNeeded, LIST_MODULES_64BIT); - if (cbNeeded < cb) cb = cbNeeded; - - HMODULE hMod = NULL; - for (DWORD i = 0; i < cb / sizeof(HMODULE); i++) { - if (!GetModuleFileNameExW(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(WCHAR))) continue; - if (!lstrcmpiW(szModName, target)) { - hMod = hMods[i]; - break; - } - } - LocalFree(hMods); - return hMod; + DWORD cb, cbNeeded; + WCHAR szModName[MAX_PATH]; + EnumProcessModulesEx(hProcess, NULL, 0, &cb, LIST_MODULES_64BIT); + auto hMods = (HMODULE*)HeapAlloc(heap, 0, cb); + if (!hMods) return NULL; + EnumProcessModulesEx(hProcess, hMods, cb, &cbNeeded, LIST_MODULES_64BIT); + if (cbNeeded < cb) cb = cbNeeded; + + HMODULE hMod = NULL; + for (DWORD i = 0; i < cb / sizeof(HMODULE); i++) { + if (!GetModuleFileNameExW(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(WCHAR))) continue; + if (!lstrcmpiW(szModName, target)) { + hMod = hMods[i]; + break; + } + } + HeapFree(heap, 0, hMods); + return hMod; } -int main() +LPVOID get_token_info(HANDLE token, const TOKEN_INFORMATION_CLASS& type) +{ + DWORD length; + void* buf = NULL; + GetTokenInformation(token, type, NULL, 0, &length); + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + buf = (void*)HeapAlloc(heap, 0, length); + GetTokenInformation(token, type, buf, length, &length); + } + return buf; +} + +void enable_all_privileges(HANDLE token) +{ + auto privileges = (PTOKEN_PRIVILEGES)get_token_info(token, TokenPrivileges); + if (privileges) + { + for (DWORD i = 0; i < privileges->PrivilegeCount; ++i) + privileges->Privileges[i].Attributes = SE_PRIVILEGE_ENABLED; + + AdjustTokenPrivileges(token, false, privileges, 0, NULL, NULL); + HeapFree(heap, 0, privileges); + } +} + +BOOL get_token_pid(const DWORD& ProcessId, PHANDLE TokenHandle) +{ + auto process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ProcessId); + if (GetLastError() == ERROR_ACCESS_DENIED) process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, ProcessId); + if (!process) return false; + + const auto ret = OpenProcessToken(process, MAXIMUM_ALLOWED, TokenHandle); + + CloseHandle(process); + return ret; +} + +DWORD get_lsass_pid() +{ + const auto scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); + if (!scm) return -1; + + const auto service = OpenServiceW(scm, L"SamSs", SERVICE_QUERY_STATUS); + if (!service) + { + CloseServiceHandle(scm); + return -1; + } + + SERVICE_STATUS_PROCESS status; + DWORD bytes_needed = sizeof(status); + DWORD pid = -1; + if (QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&status, sizeof(status), &bytes_needed)) + if (SERVICE_STOPPED != status.dwCurrentState) pid = status.dwProcessId; + + CloseServiceHandle(service); + CloseServiceHandle(scm); + return pid; +} + +HANDLE ObjectManagerCreateSymlink(LPCWSTR linkname, LPCWSTR targetname) +{ + UNICODE_STRING name, target; + HANDLE hLink; + + RtlInitUnicodeString(&name, linkname); + RtlInitUnicodeString(&target, targetname); + OBJECT_ATTRIBUTES oa = { sizeof(oa), NULL, &name, OBJ_CASE_INSENSITIVE, NULL, NULL}; + + if (NtCreateSymbolicLinkObject(&hLink, SYMBOLIC_LINK_ALL_ACCESS, &oa, &target)) + return NULL; + + return hLink; +} + +HANDLE ObjectManagerCreateDirectory(LPCWSTR dirname) +{ + UNICODE_STRING name; + HANDLE hDirectory; + + RtlInitUnicodeString(&name, dirname); + OBJECT_ATTRIBUTES oa = { sizeof(oa), NULL, &name, OBJ_CASE_INSENSITIVE, NULL, NULL }; + + if (NtCreateDirectoryObjectEx(&hDirectory, DIRECTORY_ALL_ACCESS, &oa, NULL, FALSE)) + return NULL; + + return hDirectory; +} + +_Success_(return) BOOL MapDll(_In_ LPCWSTR sectionName, _In_ HANDLE file, _Out_ PHANDLE section, _In_ DWORD flags) +{ + UNICODE_STRING name; + + RtlInitUnicodeString(&name, sectionName); + OBJECT_ATTRIBUTES oa = { sizeof(oa), NULL, &name, OBJ_CASE_INSENSITIVE | flags, NULL, NULL }; + + if (flags & OBJ_PERMANENT && !NtOpenSection(section, DELETE, &oa)) + { + NtMakeTemporaryObject(*section); + NtClose(*section); + } + + if (NtCreateSection(section, SECTION_ALL_ACCESS, &oa, NULL, PAGE_READONLY, SEC_IMAGE, file)) + return FALSE; + + return TRUE; +} + +_Success_(return) BOOL CreateProtectedProcessAsUser(_In_ HANDLE token, _In_ LPWSTR cmdline, _Out_ PHANDLE phProcess) +{ + STARTUPINFOW si; + PROCESS_INFORMATION pi; + + si.cb = sizeof(STARTUPINFOW); + si.cbReserved2 = 0; + si.lpDesktop = NULL; + si.lpTitle = NULL; + si.lpReserved = NULL; + si.lpReserved2 = NULL; + si.dwFlags = 0; + + if (!CreateProcessAsUserW(token, NULL, cmdline, NULL, NULL, FALSE, CREATE_PROTECTED_PROCESS | CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &si, &pi)) + return FALSE; + + *phProcess = pi.hProcess; + CloseHandle(pi.hThread); + + return TRUE; +} + +DWORD64 getPatchOffset(DWORD64 base, DWORD64 target, PRUNTIME_FUNCTION funcTable, DWORD64 funcTableSize) { - auto hProcess = GetCurrentProcess(); - SymSetOptions(SYMOPT_EXACT_SYMBOLS | SYMOPT_ALLOW_ABSOLUTE_SYMBOLS | SYMOPT_DEBUG | SYMOPT_UNDNAME); - LPCWSTR symPath = NULL; - GetEnvironmentVariableW(L"_NT_SYMBOL_PATH", NULL, 0); - if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) symPath = L"cache*;srv*https://msdl.microsoft.com/download/symbols"; - if (!SymInitializeW(hProcess, symPath, FALSE)) return -1; - - WCHAR szShell32[MAX_PATH]; - lstrcpyW(szShell32 + GetSystemDirectoryW(szShell32, sizeof(szShell32) / sizeof(WCHAR)), L"\\shell32.dll"); - if (!SymLoadModuleExW(hProcess, NULL, szShell32, NULL, 0, 0, NULL, 0)) return -2; - - SYMBOL_INFOW symbol; - symbol.SizeOfStruct = sizeof(SYMBOL_INFOW); - symbol.MaxNameLen = 0; - if (!SymFromNameW(hProcess, L"CDefView::TryGetContextMenuPresenter", &symbol)) return -3; - printf("Found offset %llX\n", symbol.Address - symbol.ModBase); - - PWTS_PROCESS_INFOW processList; - DWORD processCount = 0, pLevel = 0; - if(!WTSEnumerateProcessesExW(WTS_CURRENT_SERVER_HANDLE, &pLevel, WTS_CURRENT_SESSION, (LPWSTR *)&processList, &processCount)) - return -4; - - for (DWORD i = 0; i < processCount; i++) { - if (!lstrcmpiW(processList[i].pProcessName, L"explorer.exe")) { - printf("Found explorer PID:%u\n", processList[i].ProcessId); - auto hExplorer = OpenProcess(PROCESS_VM_READ | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, TRUE, processList[i].ProcessId); - if (!hExplorer) continue; - - auto hModShell32 = GetModule(hExplorer, szShell32); - if (!hModShell32) continue; - - // and qword ptr [rdx], 0 - // and rax, 0 - // retn - const char patched[] = "\x48\x83\x22\x00\x48\x83\xE0\x00\xC3\x90"; - size_t written = 0; - WriteProcessMemory(hExplorer, (void*)((size_t)hModShell32 + symbol.Address - symbol.ModBase), patched, sizeof(patched) - 1, &written); - printf("WriteProcessMemory() wrote %llu\n", written); - } - } - return 0; + ZydisDecoder decoder; + ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64); + + for (DWORD i = 0; i < funcTableSize; i++) { + DWORD64 RVA = searchXref(&decoder, base, funcTable + i, target); + if (!RVA) continue; + + auto IP = base + RVA; + auto length = (funcTable + i)->EndAddress - RVA; + ZydisDecodedInstruction instruction; + + while (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, (void*)IP, length, &instruction))) + { + if (instruction.length == 7 && + instruction.mnemonic == ZYDIS_MNEMONIC_CALL) + return IP - base; + IP += instruction.length; + length -= instruction.length; + } + } + + return 0; +} + +HANDLE patch(DWORD64 offset) +{ + WCHAR szTemp[MAX_PATH]; + lstrcpyW(szTemp + GetTempPathW(sizeof(szTemp) / sizeof(WCHAR), szTemp), L"shell32.dll"); + CopyFileW(szShell32, szTemp, FALSE); + + auto hFile = CreateFileW(szTemp, FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE, 7, NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL); + if (hFile == INVALID_HANDLE_VALUE) return hFile; + + DWORD written = 0; + if(SetFilePointerEx(hFile, *(LARGE_INTEGER*)&offset, NULL, FILE_BEGIN)) + WriteFile(hFile, patchData, sizeof(patchData) - 1, &written, NULL); + printf("Write() wrote %d\n", written); + + return hFile; +} + +int patchProcess(DWORD64 RVA, DWORD session) +{ + PWTS_PROCESS_INFOW processList; + DWORD processCount = 0, pLevel = 0; + if (!WTSEnumerateProcessesExW(WTS_CURRENT_SERVER_HANDLE, &pLevel, session, (LPWSTR*)&processList, &processCount)) + EXIT("Cannot enumerate process", -25); + + for (DWORD i = 0; i < processCount; i++) { + if (lstrcmpiW(processList[i].pProcessName, L"explorer.exe")) continue; + auto hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, TRUE, processList[i].ProcessId); + if (!hProcess) continue; + + auto hModShell32 = GetModule(hProcess, szShell32); + if (hModShell32) + { + printf("Patching PID:%u\n", processList[i].ProcessId); + size_t written = 0; + WriteProcessMemory(hProcess, (void*)((size_t)hModShell32 + RVA), patchData, sizeof(patchData) - 1, &written); + printf("WriteProcessMemory() wrote %llu\n", written); + } + CloseHandle(hProcess); + } + EXIT("Patch finised", 0); } + +int main() +{ + heap = GetProcessHeap(); + if (!heap) EXIT("GetProcessHeap() Failed", -1); + + int argc; + const auto current_cmdline = GetCommandLineW(); + const auto argv = CommandLineToArgvW(current_cmdline, &argc); + if (!argv) EXIT("CommandLineToArgv() Failed", 0x101); + + bool persist = false; + for (int i = 1; i < argc; ++i) + if (!lstrcmpiW(argv[i], L"-p")) + persist = true; + LocalFree(argv); + + lstrcpyW(szShell32 + GetSystemDirectoryW(szShell32, sizeof(szShell32) / sizeof(WCHAR)), L"\\shell32.dll"); + + auto base = (size_t)LoadLibraryExW(szShell32, NULL, DONT_RESOLVE_DLL_REFERENCES); + if (!base) EXIT("Load shell32.dll Failed", -2); + auto pNT = (PIMAGE_NT_HEADERS64)(base + ((PIMAGE_DOS_HEADER)base)->e_lfanew); + + auto pSection = findSection(pNT, ".rdata"); + if (!pSection) EXIT("Cannot find .rdata", -3); + + auto ContextMenuPresenter = pattenMatch(base, pSection, guid, sizeof(guid) - 1); + if (ContextMenuPresenter == -1) EXIT("GUID patten not found", -4); + + auto pExceptionDirectory = pNT->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXCEPTION; + auto funcTable = (PRUNTIME_FUNCTION)(base + pExceptionDirectory->VirtualAddress); + auto funcTableSize = pExceptionDirectory->Size / (DWORD)sizeof(RUNTIME_FUNCTION); + if (!funcTableSize) EXIT("Exception directory not found", -5); + + DWORD64 RVA = getPatchOffset(base, ContextMenuPresenter, funcTable, funcTableSize); + if (!RVA) EXIT("Patch patten not found, already patched?", -6); + printf("Found offset %llX\n", RVA); + + pSection = IMAGE_FIRST_SECTION(pNT); + auto rawOffset = RVA + pSection->PointerToRawData - pSection->VirtualAddress; + + HANDLE token; + if (!OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, &token)) EXIT("Open token of CurrentProcess failed", - 7); + enable_all_privileges(token); + CloseHandle(token); + + if (!persist) return patchProcess(RVA, WTS_CURRENT_SESSION); + + if (!ObjectManagerCreateSymlink(L"\\??\\GLOBALROOT", L"\\GLOBAL??")) EXIT("Create GLOBALROOT symlink failed", -8); + + if (!get_token_pid(get_lsass_pid(), &token)) EXIT("Open token of lsass.exe failed", -9); + HANDLE dup_token; + if (!DuplicateTokenEx(token, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenImpersonation, &dup_token)) EXIT("Duplicate impersonation token of lsass.exe failed", -10); + CloseHandle(token); + enable_all_privileges(dup_token); + + if (!DuplicateTokenEx(dup_token, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenPrimary, &token)) EXIT("Duplicate primary token of lsass.exe failed", -11); + if (!(SetThreadToken(NULL, dup_token) || ImpersonateLoggedOnUser(dup_token))) EXIT("Impersonate SYSTEM failed", -12); + + if (!ObjectManagerCreateDirectory(L"\\GLOBAL??\\KnownDlls")) EXIT("Create KnownDlls directory failed", -13); + if (!ObjectManagerCreateSymlink(L"\\GLOBAL??\\KnownDlls\\EventAggregation.dll", L"CreazyUniverse")) EXIT("Create EventAggregation symlink failed", -14); + + if (!RevertToSelf()) EXIT("RevertToSelf() failed", -15); + + if (!DefineDosDevice(DDD_NO_BROADCAST_SYSTEM | DDD_RAW_TARGET_PATH, L"GLOBALROOT\\KnownDlls\\EventAggregation.dll", L"\\BaseNamedObjects\\EventAggregation.dll")) + if(GetLastError() != ERROR_ALREADY_EXISTS) EXIT("DefineDosDevice() failed", -16); + + if (!(SetThreadToken(NULL, dup_token) || ImpersonateLoggedOnUser(dup_token))) EXIT("Impersonate SYSTEM failed", -17); + + auto hFile = CreateFileW(L"FakeDLL.dll", GENERIC_READ, 7, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) EXIT("Cannot open FakeDLL.dll", -18); + HANDLE hDllSection; + if (!MapDll(L"\\BaseNamedObjects\\EventAggregation.dll", hFile, &hDllSection, 0)) EXIT("Cannot create EventAggregation.dll section", -19); + CloseHandle(hFile); + + hFile = patch(rawOffset); + if (hFile == INVALID_HANDLE_VALUE) EXIT("Cannot copy and patch shell32.dll", -20); + if (!MapDll(L"\\BaseNamedObjects\\shell32.dll", hFile, &hDllSection, OBJ_PERMANENT)) EXIT("Cannot create shell32.dll section", -21); + CloseHandle(hFile); + + PSECURITY_DESCRIPTOR sd; + if(!ConvertStringSecurityDescriptorToSecurityDescriptorW( + L"D:(A;;GA;;;BA)(A;;0x2000f;;;RC)(A;;0x2000f;;;WD)(A;;0x2000f;;;AC)(A;;0x2000f;;;S-1-15-2-2)S:(ML;;NW;;;LW)", + SDDL_REVISION_1, &sd, 0)) EXIT("ConvertStringSecurityDescriptorToSecurityDescriptorW() failed", -22); + if (!SetKernelObjectSecurity(hDllSection, DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, sd)) + EXIT("Cannot set security information of shell32 section", -23); + + HANDLE hNewProcess; + WCHAR szServices[MAX_PATH]; + lstrcpyW(szServices + GetSystemDirectoryW(szServices, sizeof(szServices) / sizeof(WCHAR)), L"\\services.exe"); + if (!CreateProtectedProcessAsUser(token, szServices, &hNewProcess)) EXIT("Start services.exe failed", -24); + + return patchProcess(RVA, WTS_ANY_SESSION); +} \ No newline at end of file diff --git a/Shell32Patcher/Shell32Patcher.vcxproj b/Shell32Patcher/Shell32Patcher.vcxproj index ae92c39..3476f97 100644 --- a/Shell32Patcher/Shell32Patcher.vcxproj +++ b/Shell32Patcher/Shell32Patcher.vcxproj @@ -75,10 +75,12 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true + $(SolutionDir)zydis\include;$(SolutionDir)zydis\dependencies\zycore\include;$(SolutionDir)zydis\msvc Console true + $(SolutionDir)zydis\msvc\bin\DebugX64\Zydis.lib;ntdll.lib;Wtsapi32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) @@ -90,6 +92,9 @@ NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true None + false + false + $(SolutionDir)zydis\include;$(SolutionDir)zydis\dependencies\zycore\include;$(SolutionDir)zydis\msvc Console @@ -98,6 +103,8 @@ false + false + $(SolutionDir)zydis\msvc\bin\ReleaseX64\Zydis.lib;ntdll.lib;Wtsapi32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) @@ -110,6 +117,8 @@ true None false + false + $(SolutionDir)zydis\include;$(SolutionDir)zydis\dependencies\zycore\include;$(SolutionDir)zydis\msvc Windows @@ -117,11 +126,17 @@ true false main + false + $(SolutionDir)zydis\msvc\bin\ReleaseX64\Zydis.lib;ntdll.lib;Wtsapi32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + true + + + diff --git a/Shell32Patcher/Shell32Patcher.vcxproj.filters b/Shell32Patcher/Shell32Patcher.vcxproj.filters index 841f572..2e835f2 100644 --- a/Shell32Patcher/Shell32Patcher.vcxproj.filters +++ b/Shell32Patcher/Shell32Patcher.vcxproj.filters @@ -19,4 +19,9 @@ Source Files + + + Header Files + + \ No newline at end of file diff --git a/Shell32Patcher/ntdll.h b/Shell32Patcher/ntdll.h new file mode 100644 index 0000000..4f2da2c --- /dev/null +++ b/Shell32Patcher/ntdll.h @@ -0,0 +1,47 @@ +#pragma once +#include +#include + +#define DIRECTORY_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0xF) +#define SYMBOLIC_LINK_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x1) + +extern "C" NTSTATUS NTAPI NtCreateDirectoryObjectEx( + OUT PHANDLE SymbolicLinkHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + HANDLE ShadowDir, + ULONG Something +); + +extern "C" NTSTATUS NTAPI NtCreateSymbolicLinkObject( + OUT PHANDLE SymbolicLinkHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN PUNICODE_STRING DestinationName +); + +extern "C" NTSTATUS NTAPI NtCreateSection( + OUT PHANDLE SectionHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, + IN PLARGE_INTEGER MaximumSize OPTIONAL, + IN ULONG SectionPageProtection, + IN ULONG AllocationAttributes, + IN HANDLE FileHandle OPTIONAL +); + +extern "C" NTSTATUS NTAPI NtOpenSymbolicLinkObject( + OUT PHANDLE SymbolicLinkHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes +); + +extern "C" NTSTATUS NTAPI NtOpenSection( + OUT PHANDLE SectionHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes +); + +extern "C" NTSTATUS NTAPI NtMakeTemporaryObject( + IN HANDLE ObjectHandle +); \ No newline at end of file diff --git a/zydis b/zydis new file mode 160000 index 0000000..80d8d52 --- /dev/null +++ b/zydis @@ -0,0 +1 @@ +Subproject commit 80d8d5231f7204c7e39f70d2fe5887654037825c