/*++ Copyright (c) 2000 Microsoft Corporation Module Name: HandleRegExpandSzRegistryKeys.cpp Abstract: This DLL catches REG_EXPAND_SZ registry keys and converts them to REG_SZ by expanding the embedded environment strings. History: 04/05/2000 markder Created 10/30/2000 maonis Bug fix --*/ #include "precomp.h" IMPLEMENT_SHIM_BEGIN(HandleRegExpandSzRegistryKeys) #include "ShimHookMacro.h" APIHOOK_ENUM_BEGIN APIHOOK_ENUM_ENTRY(RegQueryValueExA) APIHOOK_ENUM_ENTRY(RegQueryValueExW) APIHOOK_ENUM_END /*++ Expand REG_EXPAND_SZ strings. --*/ LONG APIHOOK(RegQueryValueExA)( HKEY hKey, // handle to key LPCSTR lpValueName, // value name LPDWORD lpReserved, // reserved LPDWORD lpType, // dwType buffer LPBYTE lpData, // data buffer LPDWORD lpcbData // size of data buffer ) { DWORD dwType; LONG uRet; DWORD cbPassedInBuffer = 0, cbExpandedBuffer = 0; LPSTR szLocalBuffer = NULL; if (lpcbData) { cbPassedInBuffer = *lpcbData; cbExpandedBuffer = *lpcbData; } uRet = ORIGINAL_API(RegQueryValueExA)( hKey, lpValueName, lpReserved, &dwType, lpData, &cbExpandedBuffer); if (lpcbData) { *lpcbData = cbExpandedBuffer; } if (lpType) { *lpType = dwType; } if (dwType != REG_EXPAND_SZ) { return uRet; } // // The type is REG_EXPAND_SZ. Change to REG_SZ so app doesn't try to expand // the string itself. // if (lpType) { *lpType = REG_SZ; } if ((uRet != ERROR_SUCCESS) && (uRet != ERROR_MORE_DATA)) { return uRet; } LOGN( eDbgLevelInfo, "[RegQueryValueExA] Caught REG_EXPAND_SZ key: \"%s\".", lpValueName); // // Allocate a local buffer and get the value. // szLocalBuffer = (LPSTR) malloc(cbExpandedBuffer); if (!szLocalBuffer) { LOGN( eDbgLevelError, "Out of memory.\n"); // The shim failed, so revert to the original behavior *lpType = REG_EXPAND_SZ; return uRet; } ORIGINAL_API(RegQueryValueExA)( hKey, lpValueName, lpReserved, NULL, (LPBYTE) szLocalBuffer, &cbExpandedBuffer); DPFN( eDbgLevelInfo, "[RegQueryValueExA] Value: \"%s\"", szLocalBuffer); // // Query for length of expanded string. // cbExpandedBuffer = ExpandEnvironmentStringsA((LPSTR)szLocalBuffer, NULL, 0); if (lpcbData) { *lpcbData = cbExpandedBuffer; } if (lpData != NULL) { if (cbExpandedBuffer > cbPassedInBuffer) { // // Buffer not big enough. // LOGN( eDbgLevelError, "[RegQueryValueExA] Buffer too small - Passed in: %d Needed: %d", cbPassedInBuffer, cbExpandedBuffer ); free(szLocalBuffer); // // We're pretending the value is already expanded, so to be // consistent, we must fail as if that were the case. If we simply // returned uRet here, the app would get inconsistent return // values, e.g. if the buffer is big enough then REG_SZ is returned // otherwise REG_EXPAND_SZ is returned - this doesn't make sense. // return ERROR_MORE_DATA; } // // If data buffer was passed in (and is big enough), copy the data into it. // ExpandEnvironmentStringsA(szLocalBuffer, (LPSTR)lpData, cbPassedInBuffer); DPFN( eDbgLevelInfo, "[RegQueryValueExA] Expanded to: \"%s\"\n", lpData); uRet = ERROR_SUCCESS; } free(szLocalBuffer); return uRet; } /*++ Expand REG_EXPAND_SZ strings. --*/ LONG APIHOOK(RegQueryValueExW)( HKEY hKey, // handle to key LPCWSTR lpValueName, // value name LPDWORD lpReserved, // reserved LPDWORD lpType, // dwType buffer LPBYTE lpData, // data buffer LPDWORD lpcbData // size of data buffer ) { DWORD dwType; LONG uRet; DWORD cbPassedInBuffer = 0, cbExpandedBuffer = 0; LPWSTR szLocalBuffer = NULL; if (lpcbData) { cbPassedInBuffer = *lpcbData; cbExpandedBuffer = *lpcbData; } uRet = ORIGINAL_API(RegQueryValueExW)( hKey, lpValueName, lpReserved, &dwType, lpData, &cbExpandedBuffer); if (lpcbData) { *lpcbData = cbExpandedBuffer; } if (lpType) { *lpType = dwType; } if (dwType != REG_EXPAND_SZ) { return uRet; } // // The type is REG_EXPAND_SZ. Change to REG_SZ so app doesn't try to expand // the string itself. // if (lpType) { *lpType = REG_SZ; } if ((uRet != ERROR_SUCCESS) && (uRet != ERROR_MORE_DATA)) { return uRet; } LOGN( eDbgLevelInfo, "[RegQueryValueExW] Caught REG_EXPAND_SZ key: \"%ws\".", lpValueName); // // Allocate a local buffer and get the value. // szLocalBuffer = (LPWSTR) malloc(cbExpandedBuffer); if (!szLocalBuffer) { LOGN( eDbgLevelError, "Out of memory.\n"); // The shim failed, so revert to the original behavior *lpType = REG_EXPAND_SZ; return uRet; } ORIGINAL_API(RegQueryValueExW)( hKey, lpValueName, lpReserved, NULL, (LPBYTE) szLocalBuffer, &cbExpandedBuffer); DPFN( eDbgLevelInfo, "[RegQueryValueExW] Value: \"%ws\".\n", szLocalBuffer); // // Query for length of expanded string. // cbExpandedBuffer = ExpandEnvironmentStringsW((LPWSTR)szLocalBuffer, NULL, 0) * sizeof(WCHAR); if (lpcbData) { *lpcbData = cbExpandedBuffer; } if (lpData != NULL) { if (cbExpandedBuffer > cbPassedInBuffer) { // // Buffer not big enough. // LOGN( eDbgLevelInfo, "[RegQueryValueExW] Buffer too small - Passed in: %d Needed: %d", cbPassedInBuffer, cbExpandedBuffer); free(szLocalBuffer); // // We're pretending the value is already expanded, so to be // consistent, we must fail as if that were the case. If we simply // returned uRet here, the app would get inconsistent return // values, e.g. if the buffer is big enough then REG_SZ is returned // otherwise REG_EXPAND_SZ is returned - this doesn't make sense. // return ERROR_MORE_DATA; } // // If data buffer was passed in (and is big enough), copy the data into it. // ExpandEnvironmentStringsW(szLocalBuffer, (LPWSTR)lpData, cbPassedInBuffer / sizeof(WCHAR)); DPFN( eDbgLevelInfo, "[RegQueryValueExW] Expanded to: \"%ws\".\n", lpData); uRet = ERROR_SUCCESS; } free(szLocalBuffer); return uRet; } /*++ Register hooked functions --*/ HOOK_BEGIN APIHOOK_ENTRY(ADVAPI32.DLL, RegQueryValueExA) APIHOOK_ENTRY(ADVAPI32.DLL, RegQueryValueExW) HOOK_END IMPLEMENT_SHIM_END