/* * kexbasen\shell32\SHGetFolderPath.c * * Copyright (C) 2009-2010, Xeno86, Tihiy * Copyright (C) 2019, jumper * * This file is part of KernelEx source code. * * KernelEx 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; version 2 of the License. * * KernelEx 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 GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include "delayload.h" #include "kexcoresdk.h" #include "folderfix.h" typedef int (WINAPI *PROC5)(PVOID, int, PVOID, int, PVOID); #define RELOAD (PROC)(-1) static PROC pSHGetFolderPathA = RELOAD; static PROC pSHGetFolderPathW = RELOAD; static HMODULE hShfolder; /* * Hook Shfolder.dll and process DLL_PROCESS_DETACH to protect against * buggy apps that call FreeLibrary too many times. */ #include typedef struct { BYTE jmp; DWORD func; } LONGJMP, *PLONGJMP; #include static BYTE prev_entry[sizeof(LONGJMP)]; static BOOL APIENTRY shfolder_entry(HINSTANCE inst, DWORD reason, BOOL load_static) { if (reason == DLL_PROCESS_DETACH) { DBGPRINTF(("kexbasen: shfolder detached\n")); pSHGetFolderPathA = RELOAD; pSHGetFolderPathW = RELOAD; hShfolder = NULL; } return TRUE; } static void protect_shfolder() { DWORD fold; DWORD fnew; DWORD entry_addr; PLONGJMP ljmp; DWORD dwShfolder = (DWORD) hShfolder; IMAGE_DOS_HEADER* dosh = (IMAGE_DOS_HEADER*) hShfolder; IMAGE_NT_HEADERS* nth = (IMAGE_NT_HEADERS*) (dwShfolder + dosh->e_lfanew); entry_addr = dwShfolder + nth->OptionalHeader.AddressOfEntryPoint; memcpy(prev_entry, (PVOID) entry_addr, sizeof(LONGJMP)); VirtualProtect((PVOID) entry_addr, sizeof(LONGJMP), PAGE_READWRITE, &fold); ljmp = (PLONGJMP) entry_addr; ljmp->jmp = 0xe9; //jmp near rel ljmp->func = (DWORD) shfolder_entry - (entry_addr + sizeof(LONGJMP)); VirtualProtect((PVOID) entry_addr, sizeof(LONGJMP), fold, &fnew); } static void unprotect_shfolder() { DWORD fold; DWORD fnew; DWORD entry_addr; DWORD dwShfolder = (DWORD) hShfolder; IMAGE_DOS_HEADER* dosh = (IMAGE_DOS_HEADER*) hShfolder; IMAGE_NT_HEADERS* nth = (IMAGE_NT_HEADERS*) (dwShfolder + dosh->e_lfanew); entry_addr = dwShfolder + nth->OptionalHeader.AddressOfEntryPoint; VirtualProtect((PVOID) entry_addr, sizeof(LONGJMP), PAGE_READWRITE, &fold); memcpy((PVOID) entry_addr, prev_entry, sizeof(LONGJMP)); VirtualProtect((PVOID) entry_addr, sizeof(LONGJMP), fold, &fnew); } void uninit_SHGetFolderPath() { if (!hShfolder) return; unprotect_shfolder(); hShfolder = NULL; } static PROC LoadShProc(LPSTR proc) { static const char ShfolderFn[] = "SHFOLDER.DLL"; PROC ret; DWORD lasterr = GetLastError(); //first try with shell32 ret = GetDelayProc(Shell32, proc); //fallback to shfolder if (!ret) { if (!hShfolder) { hShfolder = LoadLibrary(ShfolderFn); if (hShfolder) protect_shfolder(); } if (hShfolder) ret = kexGetProcAddress(hShfolder, proc); } SetLastError(lasterr); return ret; } /* MAKE_EXPORT SHGetFolderPathA_fix=SHGetFolderPathA */ HRESULT WINAPI SHGetFolderPathA_fix( HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPSTR pszPath) { if (pSHGetFolderPathA == RELOAD) pSHGetFolderPathA = LoadShProc("SHGetFolderPathA"); if (pSHGetFolderPathA == NULL) return E_NOTIMPL; nFolder = folder_fix(nFolder); return ((PROC5)pSHGetFolderPathA)(hwndOwner, nFolder, NULL, dwFlags, pszPath); } /* MAKE_EXPORT SHGetFolderPathW_fix=SHGetFolderPathW */ HRESULT WINAPI SHGetFolderPathW_fix( HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath) { if (pSHGetFolderPathW == RELOAD) pSHGetFolderPathW = LoadShProc("SHGetFolderPathW"); if (pSHGetFolderPathW == NULL) return E_NOTIMPL; nFolder = folder_fix(nFolder); return ((PROC5)pSHGetFolderPathW)(hwndOwner, nFolder, NULL, dwFlags, pszPath); } /* MAKE_EXPORT SHGetFolderPathAndSubDirA_xp=SHGetFolderPathAndSubDirA */ HRESULT SHGetFolderPathAndSubDirA_xp ( HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPCSTR pszSubDir, LPSTR pszPath ) { HRESULT hr = SHGetFolderPathA_fix (hwnd, csidl, hToken, dwFlags, pszPath); if (hr == S_OK) PathAppendA (pszPath, pszSubDir); return hr; } /* MAKE_EXPORT SHGetFolderPathAndSubDirW_xp=SHGetFolderPathAndSubDirW */ HRESULT SHGetFolderPathAndSubDirW_xp ( HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPCWSTR pwszSubDir, LPWSTR pwszPath ) { HRESULT hr = SHGetFolderPathW_fix (hwnd, csidl, hToken, dwFlags, pwszPath); if (hr == S_OK) PathAppendW (pwszPath, pwszSubDir); return hr; }