|
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