#include "stdinc.h" #include "detours.h" #include "user32detours.h" #define NUMBER_OF(x) (sizeof(x)/sizeof(*x)) namespace User32Trampolines { bool fInitialized = false; CSimpleMap m_ItemMap; HKEY g_hkLocalMachine = NULL; HKEY g_hkClasses = NULL; HKEY g_hkCurrentUser = NULL; HKEY g_hkRedirectionRoot = NULL; HKEY g_hkUsers = NULL; GUID g_uuidRedirection; HKEY RemapRegKey(HKEY); extern "C" { DETOUR_TRAMPOLINE( ATOM WINAPI Real_RegisterClassW(CONST WNDCLASSW *lpWndClass), RegisterClassW); DETOUR_TRAMPOLINE( ATOM WINAPI Real_RegisterClassA(CONST WNDCLASSA *lpWndClass), RegisterClassA); DETOUR_TRAMPOLINE( ATOM WINAPI Real_RegisterClassExW(CONST WNDCLASSEXW *), RegisterClassExW); DETOUR_TRAMPOLINE( ATOM WINAPI Real_RegisterClassExA(CONST WNDCLASSEXA *), RegisterClassExA); DETOUR_TRAMPOLINE( LONG APIENTRY Real_RegOpenKeyW ( IN HKEY hKey, IN LPCWSTR lpSubKey, OUT PHKEY phkResult ), RegOpenKeyW); DETOUR_TRAMPOLINE( LONG APIENTRY Real_RegOpenKeyA ( IN HKEY hKey, IN LPCSTR lpSubKey, OUT PHKEY phkResult ), RegOpenKeyA); DETOUR_TRAMPOLINE( LONG APIENTRY Real_RegOpenKeyExW ( IN HKEY hKey, IN LPCWSTR lpSubKey, DWORD, REGSAM, PHKEY), RegOpenKeyExW); DETOUR_TRAMPOLINE( LONG APIENTRY Real_RegOpenKeyExA ( IN HKEY hKey, IN LPCSTR lpSubKey, DWORD, REGSAM, PHKEY), RegOpenKeyExA); DETOUR_TRAMPOLINE( LONG APIENTRY Real_RegSetValueExA (HKEY,LPCSTR,DWORD,DWORD,CONST BYTE*,DWORD), RegSetValueExA); DETOUR_TRAMPOLINE( LONG APIENTRY Real_RegSetValueExW (HKEY,LPCWSTR,DWORD,DWORD,CONST BYTE*,DWORD), RegSetValueExW); DETOUR_TRAMPOLINE( LONG APIENTRY Real_RegSetValueW(HKEY,LPCWSTR,DWORD,LPCWSTR,DWORD), RegSetValueW); DETOUR_TRAMPOLINE( LONG APIENTRY Real_RegSetValueA(HKEY,LPCSTR,DWORD,LPCSTR,DWORD), RegSetValueA); DETOUR_TRAMPOLINE( LONG APIENTRY Real_RegQueryValueExA(HKEY,LPCSTR,LPDWORD,LPDWORD,LPBYTE,LPDWORD), RegQueryValueExA); DETOUR_TRAMPOLINE( LONG APIENTRY Real_RegQueryValueExW(HKEY,LPCWSTR,LPDWORD,LPDWORD,LPBYTE,LPDWORD), RegQueryValueExW); DETOUR_TRAMPOLINE( LONG APIENTRY Real_RegQueryValueA(HKEY,LPCSTR,LPSTR,PLONG), RegQueryValueA); DETOUR_TRAMPOLINE( LONG APIENTRY Real_RegQueryValueW(HKEY,LPCWSTR,LPWSTR,PLONG), RegQueryValueW); DETOUR_TRAMPOLINE( LONG APIENTRY Real_RegCreateKeyW(HKEY, PWSTR, PHKEY), RegCreateKeyW); DETOUR_TRAMPOLINE( LONG APIENTRY Real_RegCreateKeyA(HKEY, PSTR, PHKEY), RegCreateKeyA); DETOUR_TRAMPOLINE( LONG APIENTRY Real_RegCreateKeyExW(HKEY, PCWSTR, DWORD, PCWSTR, DWORD, REGSAM, LPSECURITY_ATTRIBUTES, PHKEY, PDWORD), RegCreateKeyExW); DETOUR_TRAMPOLINE( LONG APIENTRY Real_RegCreateKeyExA(HKEY, PCSTR, DWORD, PCSTR, DWORD, REGSAM, LPSECURITY_ATTRIBUTES, PHKEY, PDWORD), RegCreateKeyExA); } }; // // Remap from a well-known registry value to our internal ones. // HKEY User32Trampolines::RemapRegKey(HKEY hKey) { if (hKey == HKEY_CLASSES_ROOT) return User32Trampolines::g_hkClasses; else if (hKey == HKEY_CURRENT_USER) return User32Trampolines::g_hkCurrentUser; else if (hKey == HKEY_LOCAL_MACHINE) return User32Trampolines::g_hkLocalMachine; else if (hKey == HKEY_USERS) return User32Trampolines::g_hkUsers; else return hKey; } LONG APIENTRY User32Trampolines::InternalRegSetValueA ( IN HKEY hKey, IN PCSTR lpSubKey, IN DWORD dwType, IN PCSTR lpData, IN DWORD cbData ) { hKey = User32Trampolines::RemapRegKey(hKey); return Real_RegSetValueA(hKey, lpSubKey, dwType, lpData, cbData); } LONG APIENTRY User32Trampolines::InternalRegSetValueW ( IN HKEY hKey, IN PCWSTR lpSubKey, IN DWORD dwType, IN PCWSTR lpData, IN DWORD cbData ) { hKey = User32Trampolines::RemapRegKey(hKey); return Real_RegSetValueW(hKey, lpSubKey, dwType, lpData, cbData); } LONG APIENTRY User32Trampolines::InternalRegSetValueExA ( IN HKEY hKey, IN LPCSTR lpValueName, IN DWORD Reserved, IN DWORD dwType, IN CONST BYTE* lpData, IN DWORD cbData ) { // // Always translate on a set. // hKey = User32Trampolines::RemapRegKey(hKey); return Real_RegSetValueExA(hKey, lpValueName, Reserved, dwType, lpData, cbData); } LONG APIENTRY User32Trampolines::InternalRegSetValueExW ( IN HKEY hKey, IN LPCWSTR lpValueName, IN DWORD Reserved, IN DWORD dwType, IN CONST BYTE* lpData, IN DWORD cbData ) { hKey = User32Trampolines::RemapRegKey(hKey); return Real_RegSetValueExW(hKey, lpValueName, Reserved, dwType, lpData, cbData); } LONG APIENTRY User32Trampolines::InternalRegOpenKeyA ( IN HKEY hKey, IN LPCSTR lpSubKey, OUT PHKEY phkResult ) { // // Opening a key remaps the hKey to our set of values if possible first. If that // fails, then try the unremapped version to read from the 'real' registry. // ULONG ulResult; ulResult = Real_RegOpenKeyA(RemapRegKey(hKey), lpSubKey, phkResult); if (ulResult != 0) { ulResult = Real_RegOpenKeyA(hKey, lpSubKey, phkResult); } return ulResult; } LONG APIENTRY User32Trampolines::InternalRegOpenKeyW ( IN HKEY hKey, IN LPCWSTR lpSubKey, OUT PHKEY phkResult ) { // // Opening a key remaps the hKey to our set of values if possible first. If that // fails, then try the unremapped version to read from the 'real' registry. // ULONG ulResult; ulResult = Real_RegOpenKeyW(RemapRegKey(hKey), lpSubKey, phkResult); if (ulResult != 0) { ulResult = Real_RegOpenKeyW(hKey, lpSubKey, phkResult); } return ulResult; } LONG APIENTRY User32Trampolines::InternalRegCreateKeyA(HKEY hk, PSTR ps, PHKEY phk) { return Real_RegCreateKeyA(RemapRegKey(hk), ps, phk); } LONG APIENTRY User32Trampolines::InternalRegCreateKeyExA(HKEY a, PCSTR b, DWORD c, PCSTR d, DWORD e, REGSAM f, LPSECURITY_ATTRIBUTES g, PHKEY h, PDWORD i) { return Real_RegCreateKeyExA(RemapRegKey(a), b, c, d, e, f, g, h, i); } LONG APIENTRY User32Trampolines::InternalRegCreateKeyW(HKEY hk, PWSTR ps, PHKEY phk) { return Real_RegCreateKeyW(RemapRegKey(hk), ps, phk); } LONG APIENTRY User32Trampolines::InternalRegCreateKeyExW(HKEY a, PCWSTR b, DWORD c, PCWSTR d, DWORD e, REGSAM f, LPSECURITY_ATTRIBUTES g, PHKEY h, PDWORD i) { return Real_RegCreateKeyExW(RemapRegKey(a), b, c, d, e, f, g, h, i); } LONG APIENTRY User32Trampolines::InternalRegOpenKeyExA ( IN HKEY hKey, IN LPCSTR lpSubKey, IN DWORD ulOptions, IN REGSAM samDesired, OUT PHKEY phkResult ) { // // Opening a key remaps the hKey to our set of values if possible first. If that // fails, then try the unremapped version to read from the 'real' registry. // ULONG ulResult; ulResult = Real_RegOpenKeyExA(RemapRegKey(hKey), lpSubKey, ulOptions, samDesired, phkResult); if (ulResult != 0) { ulResult = Real_RegOpenKeyExA(hKey, lpSubKey, ulOptions, samDesired, phkResult); } return ulResult; } LONG APIENTRY User32Trampolines::InternalRegOpenKeyExW ( IN HKEY hKey, IN LPCWSTR lpSubKey, IN DWORD ulOptions, IN REGSAM samDesired, OUT PHKEY phkResult ) { // // Opening a key remaps the hKey to our set of values if possible first. If that // fails, then try the unremapped version to read from the 'real' registry. // ULONG ulResult; ulResult = Real_RegOpenKeyExW(RemapRegKey(hKey), lpSubKey, ulOptions, samDesired, phkResult); if (ulResult != 0) { ulResult = Real_RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult); } return ulResult; } LONG APIENTRY User32Trampolines::InternalRegQueryValueA( HKEY hKey, LPCSTR lpSubKey, LPSTR lpValue, PLONG pcbData) { ULONG ulResult; ulResult = Real_RegQueryValueA(RemapRegKey(hKey), lpSubKey, lpValue, pcbData); if (ulResult != 0) { ulResult = Real_RegQueryValueA(hKey, lpSubKey, lpValue, pcbData); } return ulResult; } LONG APIENTRY User32Trampolines::InternalRegQueryValueW( HKEY hKey, LPCWSTR lpSubKey, LPWSTR lpValue, PLONG pcbData) { ULONG ulResult; ulResult = Real_RegQueryValueW(RemapRegKey(hKey), lpSubKey, lpValue, pcbData); if (ulResult != 0) { ulResult = Real_RegQueryValueW(hKey, lpSubKey, lpValue, pcbData); } return ulResult; } LONG APIENTRY User32Trampolines::InternalRegQueryValueExA( HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD pdwData ) { ULONG ulResult; ulResult = Real_RegQueryValueExA(RemapRegKey(hKey), lpValueName, lpReserved, lpType, lpData, pdwData); if (ulResult != 0) { ulResult = Real_RegQueryValueExA(hKey, lpValueName, lpReserved, lpType, lpData, pdwData); } return ulResult; } LONG APIENTRY User32Trampolines::InternalRegQueryValueExW( HKEY hKey, LPCWSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD pdwData ) { ULONG ulResult; ulResult = Real_RegQueryValueExW(RemapRegKey(hKey), lpValueName, lpReserved, lpType, lpData, pdwData); if (ulResult != 0) { ulResult = Real_RegQueryValueExW(hKey, lpValueName, lpReserved, lpType, lpData, pdwData); } return ulResult; } BOOL User32Trampolines::GetRedirectedStrings(CSimpleList& Strings) { CSimpleList FoundStrings; m_ItemMap.GetKeys(FoundStrings); for ( SIZE_T sz = 0; sz < FoundStrings.Size(); sz++ ) { SIZE_T c = 0; for ( c = 0; c < Strings.Size(); c++ ) { if ( lstrcmpiW(Strings[c], FoundStrings[sz]) == 0 ) break; } // // Not found? // if ( c == Strings.Size() ) Strings.Append(FoundStrings[sz]); } return TRUE; } void User32Trampolines::ClearRedirections() { m_ItemMap.Clear(); } BOOL User32Trampolines::Initialize() { if ( fInitialized ) return TRUE; fInitialized = TRUE; // // Set up registry redirection before actually doing redirection. Generate // a virtual-root that all calls will be rerouted to first. Do this by // generating a GUIDized path off of HKCU. // LPOLESTR pstrGuid; WCHAR wchRegPath[MAX_PATH]; ULONG ulResult; HKEY hkDump; if (FAILED(CoCreateGuid(&g_uuidRedirection))) return FALSE; StringFromCLSID(g_uuidRedirection, &pstrGuid); _snwprintf(wchRegPath, MAX_PATH, L"ManBuilderRedirect\\%ls", pstrGuid); // // Create root key. For purposes of argument, we'll allow all access to all // subchildren, no matter what. If we were a real app, we'd take into account // propagating security down the heirarchy, but for now, we don't really care. // ulResult = ::RegCreateKeyExW( HKEY_CURRENT_USER, wchRegPath, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &User32Trampolines::g_hkRedirectionRoot, NULL); if (ulResult != 0) return FALSE; // // Generate all the pseudo-roots for the various keys // struct { HKEY* phkTarget; PCWSTR pcwszKeyName; } s_KeyRedirections[] = { { &User32Trampolines::g_hkClasses, L"HKEY_CLASSES_ROOT" }, { &User32Trampolines::g_hkCurrentUser, L"HKEY_CURRENT_USER" }, { &User32Trampolines::g_hkLocalMachine, L"HKEY_LOCAL_MACHINE" }, { &User32Trampolines::g_hkUsers, L"HKEY_USERS" } }; for (ULONG ul = 0; ul < NUMBER_OF(s_KeyRedirections); ul++) { ulResult = RegCreateKeyExW( g_hkRedirectionRoot, s_KeyRedirections[ul].pcwszKeyName, 0, NULL, 0, KEY_ALL_ACCESS, NULL, s_KeyRedirections[ul].phkTarget, NULL); if (ulResult != 0) return FALSE; } // // Let's also create (but close) the following 'special' keys for COM: // - CLSID // - Interface // ulResult = RegCreateKeyExW(User32Trampolines::g_hkClasses, L"CLSID", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkDump, NULL); if (ulResult != 0) RegCloseKey(hkDump); ulResult = RegCreateKeyExW(User32Trampolines::g_hkClasses, L"Interface", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkDump, NULL); if (ulResult != 0) RegCloseKey(hkDump); ulResult = RegCreateKeyExW(User32Trampolines::g_hkClasses, L"TypeLib", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkDump, NULL); if (ulResult != 0) RegCloseKey(hkDump); #define MAP(f) { (PBYTE)Real_##f, (PBYTE)Internal##f } PBYTE Remapping[][2] = { MAP(RegisterClassW), MAP(RegisterClassExW), MAP(RegisterClassA), MAP(RegisterClassExA), MAP(RegOpenKeyW), MAP(RegOpenKeyA), MAP(RegOpenKeyExW), MAP(RegOpenKeyExA), MAP(RegSetValueExA), MAP(RegSetValueExW), MAP(RegSetValueA), MAP(RegSetValueW), MAP(RegQueryValueExA), MAP(RegQueryValueExW), MAP(RegQueryValueW), MAP(RegQueryValueA), MAP(RegCreateKeyA), MAP(RegCreateKeyW), MAP(RegCreateKeyExA), MAP(RegCreateKeyExW) }; // // All redirections // for (int i = 0; i < NUMBER_OF(Remapping); i++) { DetourFunctionWithTrampoline(Remapping[i][0], Remapping[i][1]); } return TRUE; } ATOM WINAPI User32Trampolines::InternalRegisterClassA(CONST WNDCLASSA * lpWndClass) { m_ItemMap[CString(lpWndClass->lpszClassName)] = true; return Real_RegisterClassA(lpWndClass); } ATOM WINAPI User32Trampolines::InternalRegisterClassW(CONST WNDCLASSW * lpWndClass) { m_ItemMap[CString(lpWndClass->lpszClassName)] = true; return Real_RegisterClassW(lpWndClass); } ATOM WINAPI User32Trampolines::InternalRegisterClassExA(CONST WNDCLASSEXA * lpWndClass) { m_ItemMap[CString(lpWndClass->lpszClassName)] = true; return Real_RegisterClassExA(lpWndClass); } ATOM WINAPI User32Trampolines::InternalRegisterClassExW(CONST WNDCLASSEXW * lpWndClass) { m_ItemMap[CString(lpWndClass->lpszClassName)] = true; return Real_RegisterClassExW(lpWndClass); } namespace User32Trampolines { void RegDestroyKeyTree(HKEY hk, PWSTR pwszBuffer) { bool fCreatedBuffer = false; PWSTR strKeyName; if (pwszBuffer == NULL) { fCreatedBuffer = true; pwszBuffer = new WCHAR[MAX_PATH]; } pwszBuffer[0] = UNICODE_NULL; while (1) { HKEY hkSub; if (ERROR_SUCCESS == RegEnumKeyW(hk, 0, pwszBuffer, MAX_PATH)) { if ((strKeyName = new WCHAR[lstrlenW(pwszBuffer) + 1]) == NULL) break; lstrcpyW(strKeyName, pwszBuffer); if (ERROR_SUCCESS == RegOpenKeyW(hk, pwszBuffer, &hkSub)) { RegDestroyKeyTree(hkSub, pwszBuffer); } RegDeleteKeyW(hk, strKeyName); delete[] strKeyName; } else { break; } } if (fCreatedBuffer && pwszBuffer) { delete [] pwszBuffer; pwszBuffer = NULL; fCreatedBuffer = false; } } BOOL Stop() { PHKEY hkToClose[] = { &g_hkClasses, &g_hkLocalMachine, &g_hkUsers, &g_hkCurrentUser, }; PBYTE Remapping[][2] = { MAP(RegisterClassW), MAP(RegisterClassExW), MAP(RegisterClassA), MAP(RegisterClassExA), MAP(RegOpenKeyW), MAP(RegOpenKeyA), MAP(RegOpenKeyExW), MAP(RegOpenKeyExA), MAP(RegSetValueExA), MAP(RegSetValueExW), MAP(RegSetValueA), MAP(RegSetValueW), MAP(RegQueryValueExA), MAP(RegQueryValueExW), MAP(RegQueryValueW), MAP(RegQueryValueA), MAP(RegCreateKeyA), MAP(RegCreateKeyW), MAP(RegCreateKeyExA), MAP(RegCreateKeyExW) }; int i; // // Close open keys // for (i = 0; i < NUMBER_OF(hkToClose); i++) { if (*hkToClose[i] != NULL) { RegCloseKey(*hkToClose[i]); *hkToClose[i] = NULL; } } // // Undo detours // for (int i = 0; i < NUMBER_OF(Remapping); i++) { DetourRemove(Remapping[i][0], Remapping[i][1]); } LPOLESTR psz; WCHAR wchKeyNameBuffer[MAX_PATH]; if (SUCCEEDED(StringFromCLSID(User32Trampolines::g_uuidRedirection, &psz))) { _snwprintf(wchKeyNameBuffer, MAX_PATH, L"ManBuilderRedirect\\%ls", psz); // Destroy the tree pointed at RegDestroyKeyTree(User32Trampolines::g_hkRedirectionRoot, NULL); // Close our handle RegCloseKey(User32Trampolines::g_hkRedirectionRoot); // And try to delete the key RegDeleteKeyW(HKEY_CURRENT_USER, wchKeyNameBuffer); // And maybe delete the parent key RegDeleteKeyW(HKEY_CURRENT_USER, L"ManBuilderRedirect"); // Free guid string memory, too CoTaskMemFree(psz); } return TRUE; } }