|
19 | 19 |
|
20 | 20 | #include <cstdlib> |
21 | 21 | #include <cstring> |
22 | | -// #include <limits> // No longer needed |
23 | 22 | #include <string> |
24 | 23 | #include <vector> |
25 | 24 |
|
@@ -89,212 +88,211 @@ static std::wstring GetExecutablePath() { |
89 | 88 | } |
90 | 89 | } |
91 | 90 | } |
| 91 | +} |
| 92 | + |
| 93 | +// Helper function to calculate SHA256 hash of a file. |
| 94 | +static std::vector<BYTE> CalculateFileSha256(HANDLE hFile) { |
| 95 | + HCRYPTPROV hProv = 0; |
| 96 | + HCRYPTHASH hHash = 0; |
| 97 | + std::vector<BYTE> result_hash_value; |
| 98 | + |
| 99 | + if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { |
| 100 | + DWORD dwError = GetLastError(); |
| 101 | + LogError(LOG_TAG "CalculateFileSha256.SetFilePointer failed. Error: %u", |
| 102 | + dwError); |
| 103 | + return result_hash_value; |
| 104 | + } |
92 | 105 |
|
93 | | - // Helper function to calculate SHA256 hash of a file. |
94 | | - static std::vector<BYTE> CalculateFileSha256(HANDLE hFile) { |
95 | | - HCRYPTPROV hProv = 0; |
96 | | - HCRYPTHASH hHash = 0; |
97 | | - std::vector<BYTE> result_hash_value; |
98 | | - |
99 | | - if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == |
100 | | - INVALID_SET_FILE_POINTER) { |
101 | | - DWORD dwError = GetLastError(); |
102 | | - LogError(LOG_TAG "CalculateFileSha256.SetFilePointer failed. Error: %u", |
103 | | - dwError); |
104 | | - return result_hash_value; |
105 | | - } |
106 | | - |
107 | | - // Acquire Crypto Provider. |
108 | | - // Using CRYPT_VERIFYCONTEXT for operations that don't require private key |
109 | | - // access. |
110 | | - if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, |
111 | | - CRYPT_VERIFYCONTEXT)) { |
112 | | - DWORD dwError = GetLastError(); |
113 | | - LogError(LOG_TAG |
114 | | - "CalculateFileSha256.CryptAcquireContextW failed. Error: %u", |
115 | | - dwError); |
116 | | - return result_hash_value; |
117 | | - } |
118 | | - |
119 | | - if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) { |
120 | | - DWORD dwError = GetLastError(); |
121 | | - LogError(LOG_TAG "CalculateFileSha256.CryptCreateHash failed. Error: %u", |
122 | | - dwError); |
123 | | - CryptReleaseContext(hProv, 0); |
124 | | - return result_hash_value; |
125 | | - } |
| 106 | + // Acquire Crypto Provider. |
| 107 | + // Using CRYPT_VERIFYCONTEXT for operations that don't require private key |
| 108 | + // access. |
| 109 | + if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, |
| 110 | + CRYPT_VERIFYCONTEXT)) { |
| 111 | + DWORD dwError = GetLastError(); |
| 112 | + LogError(LOG_TAG |
| 113 | + "CalculateFileSha256.CryptAcquireContextW failed. Error: %u", |
| 114 | + dwError); |
| 115 | + return result_hash_value; |
| 116 | + } |
126 | 117 |
|
127 | | - BYTE rgbFile[1024]; |
128 | | - DWORD cbRead = 0; |
129 | | - BOOL bReadSuccessLoop = TRUE; |
| 118 | + if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) { |
| 119 | + DWORD dwError = GetLastError(); |
| 120 | + LogError(LOG_TAG "CalculateFileSha256.CryptCreateHash failed. Error: %u", |
| 121 | + dwError); |
| 122 | + CryptReleaseContext(hProv, 0); |
| 123 | + return result_hash_value; |
| 124 | + } |
130 | 125 |
|
131 | | - while (true) { |
132 | | - bReadSuccessLoop = |
133 | | - ReadFile(hFile, rgbFile, sizeof(rgbFile), &cbRead, NULL); |
134 | | - if (!bReadSuccessLoop) { |
135 | | - DWORD dwError = GetLastError(); |
136 | | - LogError(LOG_TAG "CalculateFileSha256.ReadFile failed. Error: %u", |
137 | | - dwError); |
138 | | - CryptDestroyHash(hHash); |
139 | | - CryptReleaseContext(hProv, 0); |
140 | | - return result_hash_value; |
141 | | - } |
142 | | - if (cbRead == 0) { |
143 | | - break; |
144 | | - } |
145 | | - if (!CryptHashData(hHash, rgbFile, cbRead, 0)) { |
146 | | - DWORD dwError = GetLastError(); |
147 | | - LogError(LOG_TAG "CalculateFileSha256.CryptHashData failed. Error: %u", |
148 | | - dwError); |
149 | | - CryptDestroyHash(hHash); |
150 | | - CryptReleaseContext(hProv, 0); |
151 | | - return result_hash_value; |
152 | | - } |
153 | | - } |
| 126 | + BYTE rgbFile[1024]; |
| 127 | + DWORD cbRead = 0; |
| 128 | + BOOL bReadSuccessLoop = TRUE; |
154 | 129 |
|
155 | | - DWORD cbHashValue = 0; |
156 | | - DWORD dwCount = sizeof(DWORD); |
157 | | - if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&cbHashValue, &dwCount, |
158 | | - 0)) { |
| 130 | + while (true) { |
| 131 | + bReadSuccessLoop = ReadFile(hFile, rgbFile, sizeof(rgbFile), &cbRead, NULL); |
| 132 | + if (!bReadSuccessLoop) { |
159 | 133 | DWORD dwError = GetLastError(); |
160 | | - LogError(LOG_TAG |
161 | | - "CalculateFileSha256.CryptGetHashParam " |
162 | | - "(HP_HASHSIZE) failed. Error: " |
163 | | - "%u", |
| 134 | + LogError(LOG_TAG "CalculateFileSha256.ReadFile failed. Error: %u", |
164 | 135 | dwError); |
165 | 136 | CryptDestroyHash(hHash); |
166 | 137 | CryptReleaseContext(hProv, 0); |
167 | 138 | return result_hash_value; |
168 | 139 | } |
169 | | - |
170 | | - result_hash_value.resize(cbHashValue); |
171 | | - if (!CryptGetHashParam(hHash, HP_HASHVAL, result_hash_value.data(), |
172 | | - &cbHashValue, 0)) { |
| 140 | + if (cbRead == 0) { |
| 141 | + break; |
| 142 | + } |
| 143 | + if (!CryptHashData(hHash, rgbFile, cbRead, 0)) { |
173 | 144 | DWORD dwError = GetLastError(); |
174 | | - LogError(LOG_TAG |
175 | | - "CalculateFileSha256.CryptGetHashParam (HP_HASHVAL) failed. " |
176 | | - "Error: %u", |
| 145 | + LogError(LOG_TAG "CalculateFileSha256.CryptHashData failed. Error: %u", |
177 | 146 | dwError); |
178 | | - result_hash_value.clear(); |
179 | 147 | CryptDestroyHash(hHash); |
180 | 148 | CryptReleaseContext(hProv, 0); |
181 | 149 | return result_hash_value; |
182 | 150 | } |
| 151 | + } |
183 | 152 |
|
| 153 | + DWORD cbHashValue = 0; |
| 154 | + DWORD dwCount = sizeof(DWORD); |
| 155 | + if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&cbHashValue, &dwCount, |
| 156 | + 0)) { |
| 157 | + DWORD dwError = GetLastError(); |
| 158 | + LogError(LOG_TAG |
| 159 | + "CalculateFileSha256.CryptGetHashParam " |
| 160 | + "(HP_HASHSIZE) failed. Error: " |
| 161 | + "%u", |
| 162 | + dwError); |
184 | 163 | CryptDestroyHash(hHash); |
185 | 164 | CryptReleaseContext(hProv, 0); |
186 | 165 | return result_hash_value; |
187 | 166 | } |
188 | 167 |
|
189 | | - HMODULE VerifyAndLoadAnalyticsLibrary( |
190 | | - const wchar_t* library_filename, // This is expected to be just the DLL |
191 | | - // filename e.g. "analytics_win.dll" |
192 | | - const unsigned char* expected_hash, size_t expected_hash_size) { |
193 | | - if (library_filename == nullptr || library_filename[0] == L'\0') { |
194 | | - LogError(LOG_TAG "Invalid arguments."); |
195 | | - return nullptr; |
196 | | - } |
197 | | - if (expected_hash == nullptr || expected_hash_size == 0) { |
198 | | - // Don't check the hash, just load the library. |
199 | | - return LoadLibraryExW(library_filename, NULL, |
200 | | - LOAD_LIBRARY_SEARCH_APPLICATION_DIR); |
201 | | - } |
| 168 | + result_hash_value.resize(cbHashValue); |
| 169 | + if (!CryptGetHashParam(hHash, HP_HASHVAL, result_hash_value.data(), |
| 170 | + &cbHashValue, 0)) { |
| 171 | + DWORD dwError = GetLastError(); |
| 172 | + LogError(LOG_TAG |
| 173 | + "CalculateFileSha256.CryptGetHashParam (HP_HASHVAL) failed. " |
| 174 | + "Error: %u", |
| 175 | + dwError); |
| 176 | + result_hash_value.clear(); |
| 177 | + CryptDestroyHash(hHash); |
| 178 | + CryptReleaseContext(hProv, 0); |
| 179 | + return result_hash_value; |
| 180 | + } |
202 | 181 |
|
203 | | - std::wstring executable_path_str = GetExecutablePath(); |
| 182 | + CryptDestroyHash(hHash); |
| 183 | + CryptReleaseContext(hProv, 0); |
| 184 | + return result_hash_value; |
| 185 | +} |
| 186 | + |
| 187 | +HMODULE VerifyAndLoadAnalyticsLibrary( |
| 188 | + const wchar_t* library_filename, // This is expected to be just the DLL |
| 189 | + // filename e.g. "analytics_win.dll" |
| 190 | + const unsigned char* expected_hash, size_t expected_hash_size) { |
| 191 | + if (library_filename == nullptr || library_filename[0] == L'\0') { |
| 192 | + LogError(LOG_TAG "Invalid arguments."); |
| 193 | + return nullptr; |
| 194 | + } |
| 195 | + if (expected_hash == nullptr || expected_hash_size == 0) { |
| 196 | + // Don't check the hash, just load the library. |
| 197 | + return LoadLibraryExW(library_filename, NULL, |
| 198 | + LOAD_LIBRARY_SEARCH_APPLICATION_DIR); |
| 199 | + } |
204 | 200 |
|
205 | | - if (executable_path_str.empty()) { |
206 | | - // GetExecutablePath() is expected to log specific errors. |
207 | | - // This log indicates the failure to proceed within this function. |
208 | | - LogError(LOG_TAG "Can't determine executable path."); |
209 | | - return nullptr; |
210 | | - } |
| 201 | + std::wstring executable_path_str = GetExecutablePath(); |
211 | 202 |
|
212 | | - size_t last_slash_pos = executable_path_str.find_last_of(L"\\"); |
213 | | - if (last_slash_pos == std::wstring::npos) { |
214 | | - // Log message updated to avoid using %ls for executable_path_str |
215 | | - LogError(LOG_TAG "Could not determine executable directory."); |
216 | | - return nullptr; |
217 | | - } |
| 203 | + if (executable_path_str.empty()) { |
| 204 | + // GetExecutablePath() is expected to log specific errors. |
| 205 | + // This log indicates the failure to proceed within this function. |
| 206 | + LogError(LOG_TAG "Can't determine executable path."); |
| 207 | + return nullptr; |
| 208 | + } |
218 | 209 |
|
219 | | - std::wstring full_dll_path_str = |
220 | | - executable_path_str.substr(0, last_slash_pos + 1); |
221 | | - full_dll_path_str += library_filename; // library_filename is the filename |
| 210 | + size_t last_slash_pos = executable_path_str.find_last_of(L"\\"); |
| 211 | + if (last_slash_pos == std::wstring::npos) { |
| 212 | + // Log message updated to avoid using %ls for executable_path_str |
| 213 | + LogError(LOG_TAG "Could not determine executable directory."); |
| 214 | + return nullptr; |
| 215 | + } |
222 | 216 |
|
223 | | - HANDLE hFile = |
224 | | - CreateFileW(full_dll_path_str.c_str(), GENERIC_READ, FILE_SHARE_READ, |
225 | | - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
226 | | - if (hFile == INVALID_HANDLE_VALUE) { |
227 | | - DWORD dwError = GetLastError(); |
228 | | - // If the DLL is simply not found, silently proceed to stub mode without |
229 | | - // logging an error. For other errors (e.g., access denied on an existing |
230 | | - // file), log them as it's an unexpected issue. |
231 | | - if (dwError != ERROR_FILE_NOT_FOUND && dwError != ERROR_PATH_NOT_FOUND) { |
232 | | - LogError(LOG_TAG "Failed to open Analytics DLL. Error: %u", dwError); |
233 | | - } |
234 | | - return nullptr; // In all CreateFileW failure cases, return nullptr to |
235 | | - // fall back to stub mode. |
| 217 | + std::wstring full_dll_path_str = |
| 218 | + executable_path_str.substr(0, last_slash_pos + 1); |
| 219 | + full_dll_path_str += library_filename; // library_filename is the filename |
| 220 | + |
| 221 | + HANDLE hFile = |
| 222 | + CreateFileW(full_dll_path_str.c_str(), GENERIC_READ, FILE_SHARE_READ, |
| 223 | + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
| 224 | + if (hFile == INVALID_HANDLE_VALUE) { |
| 225 | + DWORD dwError = GetLastError(); |
| 226 | + // If the DLL is simply not found, silently proceed to stub mode without |
| 227 | + // logging an error. For other errors (e.g., access denied on an existing |
| 228 | + // file), log them as it's an unexpected issue. |
| 229 | + if (dwError != ERROR_FILE_NOT_FOUND && dwError != ERROR_PATH_NOT_FOUND) { |
| 230 | + LogError(LOG_TAG "Failed to open Analytics DLL. Error: %u", dwError); |
236 | 231 | } |
| 232 | + return nullptr; // In all CreateFileW failure cases, return nullptr to |
| 233 | + // fall back to stub mode. |
| 234 | + } |
237 | 235 |
|
238 | | - OVERLAPPED overlapped = {0}; |
239 | | - // Attempt to lock the entire file exclusively (LOCKFILE_EXCLUSIVE_LOCK). |
240 | | - // This helps ensure no other process modifies the file while we are |
241 | | - // verifying and loading it. |
242 | | - BOOL bFileLocked = LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 0xFFFFFFFF, |
243 | | - 0xFFFFFFFF, &overlapped); |
244 | | - if (!bFileLocked) { |
245 | | - DWORD dwError = GetLastError(); |
246 | | - LogError(LOG_TAG "Failed to lock Analytics DLL. Error: %u", dwError); |
247 | | - CloseHandle(hFile); |
248 | | - return nullptr; |
249 | | - } |
| 236 | + OVERLAPPED overlapped = {0}; |
| 237 | + // Attempt to lock the entire file exclusively (LOCKFILE_EXCLUSIVE_LOCK). |
| 238 | + // This helps ensure no other process modifies the file while we are |
| 239 | + // verifying and loading it. |
| 240 | + BOOL bFileLocked = LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 0xFFFFFFFF, |
| 241 | + 0xFFFFFFFF, &overlapped); |
| 242 | + if (!bFileLocked) { |
| 243 | + DWORD dwError = GetLastError(); |
| 244 | + LogError(LOG_TAG "Failed to lock Analytics DLL. Error: %u", dwError); |
| 245 | + CloseHandle(hFile); |
| 246 | + return nullptr; |
| 247 | + } |
250 | 248 |
|
251 | | - HMODULE hModule = nullptr; |
| 249 | + HMODULE hModule = nullptr; |
252 | 250 |
|
253 | | - std::vector<BYTE> calculated_hash = CalculateFileSha256(hFile); |
| 251 | + std::vector<BYTE> calculated_hash = CalculateFileSha256(hFile); |
254 | 252 |
|
255 | | - if (calculated_hash.empty()) { |
256 | | - LogError(LOG_TAG "Hash failed for Analytics DLL."); |
| 253 | + if (calculated_hash.empty()) { |
| 254 | + LogError(LOG_TAG "Hash failed for Analytics DLL."); |
| 255 | + } else { |
| 256 | + if (calculated_hash.size() != expected_hash_size) { |
| 257 | + LogError(LOG_TAG |
| 258 | + "Hash size mismatch for Analytics DLL. Expected: %zu, " |
| 259 | + "Calculated: %zu.", |
| 260 | + expected_hash_size, calculated_hash.size()); |
| 261 | + } else if (memcmp(calculated_hash.data(), expected_hash, |
| 262 | + expected_hash_size) != 0) { |
| 263 | + LogError(LOG_TAG "Hash mismatch for Analytics DLL."); |
257 | 264 | } else { |
258 | | - if (calculated_hash.size() != expected_hash_size) { |
259 | | - LogError(LOG_TAG |
260 | | - "Hash size mismatch for Analytics DLL. Expected: %zu, " |
261 | | - "Calculated: %zu.", |
262 | | - expected_hash_size, calculated_hash.size()); |
263 | | - } else if (memcmp(calculated_hash.data(), expected_hash, |
264 | | - expected_hash_size) != 0) { |
265 | | - LogError(LOG_TAG "Hash mismatch for Analytics DLL."); |
266 | | - } else { |
267 | | - // Load the library. LOAD_LIBRARY_SEARCH_APPLICATION_DIR is a security |
268 | | - // measure to help ensure that the DLL is loaded from the application's |
269 | | - // installation directory, mitigating risks of DLL preloading attacks |
270 | | - // from other locations. Crucially, LoadLibraryExW with this flag needs |
271 | | - // the DLL *filename only* (library_filename), not the full path we |
272 | | - // constructed for CreateFileW. |
273 | | - hModule = LoadLibraryExW(library_filename, NULL, |
274 | | - LOAD_LIBRARY_SEARCH_APPLICATION_DIR); |
275 | | - if (hModule == NULL) { |
276 | | - DWORD dwError = GetLastError(); |
277 | | - LogError(LOG_TAG "Library load failed for Analytics DLL. Error: %u", |
278 | | - dwError); |
279 | | - } |
| 265 | + // Load the library. LOAD_LIBRARY_SEARCH_APPLICATION_DIR is a security |
| 266 | + // measure to help ensure that the DLL is loaded from the application's |
| 267 | + // installation directory, mitigating risks of DLL preloading attacks |
| 268 | + // from other locations. Crucially, LoadLibraryExW with this flag needs |
| 269 | + // the DLL *filename only* (library_filename), not the full path we |
| 270 | + // constructed for CreateFileW. |
| 271 | + hModule = LoadLibraryExW(library_filename, NULL, |
| 272 | + LOAD_LIBRARY_SEARCH_APPLICATION_DIR); |
| 273 | + if (hModule == NULL) { |
| 274 | + DWORD dwError = GetLastError(); |
| 275 | + LogError(LOG_TAG "Library load failed for Analytics DLL. Error: %u", |
| 276 | + dwError); |
280 | 277 | } |
281 | 278 | } |
| 279 | + } |
282 | 280 |
|
283 | | - if (bFileLocked) { |
284 | | - if (!UnlockFileEx(hFile, 0, 0xFFFFFFFF, 0xFFFFFFFF, &overlapped)) { |
285 | | - DWORD dwError = GetLastError(); |
286 | | - LogError(LOG_TAG "Failed to unlock Analytics DLL. Error: %u", dwError); |
287 | | - } |
| 281 | + if (bFileLocked) { |
| 282 | + if (!UnlockFileEx(hFile, 0, 0xFFFFFFFF, 0xFFFFFFFF, &overlapped)) { |
| 283 | + DWORD dwError = GetLastError(); |
| 284 | + LogError(LOG_TAG "Failed to unlock Analytics DLL. Error: %u", dwError); |
288 | 285 | } |
| 286 | + } |
289 | 287 |
|
290 | | - if (hFile != INVALID_HANDLE_VALUE) { |
291 | | - if (!CloseHandle(hFile)) { |
292 | | - DWORD dwError = GetLastError(); |
293 | | - LogError(LOG_TAG "Failed to close Analytics DLL. Error: %u", dwError); |
294 | | - } |
| 288 | + if (hFile != INVALID_HANDLE_VALUE) { |
| 289 | + if (!CloseHandle(hFile)) { |
| 290 | + DWORD dwError = GetLastError(); |
| 291 | + LogError(LOG_TAG "Failed to close Analytics DLL. Error: %u", dwError); |
295 | 292 | } |
296 | | - return hModule; |
297 | 293 | } |
| 294 | + return hModule; |
| 295 | +} |
298 | 296 |
|
299 | 297 | } // namespace internal |
300 | 298 | } // namespace analytics |
|
0 commit comments