Skip to content

Commit 81045a0

Browse files
committed
Update script and code to handle known hashes for multiple DLLs.
1 parent 56de502 commit 81045a0

File tree

5 files changed

+121
-60
lines changed

5 files changed

+121
-60
lines changed

analytics/generate_windows_stubs.py

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,36 @@ def generate_function_pointers(dll_file_path, header_file_path, output_h_path, o
5555
initialized pointers, and a dynamic loading function for Windows.
5656
5757
Args:
58-
header_file_path (str): The path to the DLL file.
58+
dll_file_path (str): The path to the DLL file.
5959
header_file_path (str): The path to the input C header file.
6060
output_h_path (str): The path for the generated C header output file.
6161
output_c_path (str): The path for the generated C source output file.
6262
"""
6363
print(f"Reading DLL file: {dll_file_path}")
64-
dll_hash = hash_file(dll_file_path)
64+
dll_hash = hash_file(dll_file_path) # This is binary
65+
66+
# --- Manage known hashes ---
67+
hash_file_path = os.path.join(os.path.dirname(dll_file_path), "known_dll_hashes.txt")
68+
known_hex_hashes = []
69+
try:
70+
with open(hash_file_path, 'r') as f:
71+
for line in f:
72+
known_hex_hashes.append(line.strip())
73+
except FileNotFoundError:
74+
print(f"Info: '{hash_file_path}' not found, will be created.")
75+
pass # File doesn't exist, list remains empty
76+
77+
current_dll_hex_hash = dll_hash.hex()
78+
if current_dll_hex_hash not in known_hex_hashes:
79+
known_hex_hashes.append(current_dll_hex_hash)
80+
# Sort for consistency, although not strictly required by the prompt
81+
# known_hex_hashes.sort() # Decided against sorting to maintain order of addition for now
82+
83+
with open(hash_file_path, 'w') as f:
84+
for hex_hash in known_hex_hashes:
85+
f.write(hex_hash + '\n')
86+
print(f"Updated known hashes in: {hash_file_path}")
87+
# --- End of manage known hashes ---
6588

6689
print(f"Reading header file: {header_file_path}")
6790
try:
@@ -157,8 +180,10 @@ def generate_function_pointers(dll_file_path, header_file_path, output_h_path, o
157180
f.write("\n// --- Dynamic Loader Declaration for Windows ---\n")
158181
f.write("#if defined(_WIN32)\n\n")
159182
f.write('#include <windows.h>\n')
160-
f.write(f'\n// Google Analytics Windows DLL SHA256 hash, to be verified before loading.')
161-
f.write(f'\nextern const unsigned char FirebaseAnalytics_WindowsDllHash[{len(dll_hash)}];\n\n');
183+
f.write('\n// Array of known Google Analytics Windows DLL SHA256 hashes (hex strings).\n')
184+
f.write('extern const char* FirebaseAnalytics_KnownWindowsDllHashes[];\n')
185+
f.write('// Count of known Google Analytics Windows DLL SHA256 hashes.\n')
186+
f.write('extern const int FirebaseAnalytics_KnownWindowsDllHashCount;\n\n')
162187
f.write('// Load Analytics functions from the given DLL handle into function pointers.\n')
163188
f.write(f'// Returns the number of functions successfully loaded.\n')
164189
f.write("int FirebaseAnalytics_LoadDynamicFunctions(HMODULE dll_handle);\n\n")
@@ -182,11 +207,25 @@ def generate_function_pointers(dll_file_path, header_file_path, output_h_path, o
182207
f.write("// clang-format off\n")
183208
f.write(f'\n// Number of Google Analytics functions expected to be loaded from the DLL.')
184209
f.write(f'\nconst int FirebaseAnalytics_DynamicFunctionCount = {len(function_details_for_loader)};\n\n');
210+
# f.write("#if defined(_WIN32)\n")
211+
# f.write('// Google Analytics Windows DLL SHA256 hash, to be verified before loading.\n')
212+
# f.write('const unsigned char FirebaseAnalytics_WindowsDllHash[] = {\n ')
213+
# f.write(', '.join(["0x%02x" % s for s in dll_hash]))
214+
# f.write('\n};\n')
215+
# f.write("#endif // defined(_WIN32)\n")
216+
185217
f.write("#if defined(_WIN32)\n")
186-
f.write('// Google Analytics Windows DLL SHA256 hash, to be verified before loading.\n')
187-
f.write('const unsigned char FirebaseAnalytics_WindowsDllHash[] = {\n ')
188-
f.write(', '.join(["0x%02x" % s for s in dll_hash]))
189-
f.write('\n};\n')
218+
f.write('// Array of known Google Analytics Windows DLL SHA256 hashes (hex strings).\n')
219+
f.write('const char* FirebaseAnalytics_KnownWindowsDllHashes[] = {\n')
220+
if known_hex_hashes:
221+
for i, hex_hash in enumerate(known_hex_hashes):
222+
f.write(f' "{hex_hash}"')
223+
if i < len(known_hex_hashes) - 1:
224+
f.write(',')
225+
f.write('\n')
226+
f.write('};\n\n')
227+
f.write('// Count of known Google Analytics Windows DLL SHA256 hashes.\n')
228+
f.write(f'const int FirebaseAnalytics_KnownWindowsDllHashCount = {len(known_hex_hashes)};\n')
190229
f.write("#endif // defined(_WIN32)\n")
191230
f.write("\n// --- Stub Function Definitions ---\n")
192231
f.write("\n\n".join(stub_functions))
@@ -231,25 +270,21 @@ def generate_function_pointers(dll_file_path, header_file_path, output_h_path, o
231270
parser.add_argument(
232271
"--windows_dll",
233272
default = os.path.join(os.path.dirname(sys.argv[0]), "windows/analytics_win.dll"),
234-
#required=True,
235273
help="Path to the DLL file to calculate a hash."
236274
)
237275
parser.add_argument(
238276
"--windows_header",
239277
default = os.path.join(os.path.dirname(sys.argv[0]), "windows/include/public/c/analytics.h"),
240-
#required=True,
241278
help="Path to the input C header file."
242279
)
243280
parser.add_argument(
244281
"--output_header",
245282
default = os.path.join(os.path.dirname(sys.argv[0]), INCLUDE_PATH, "analytics_desktop_dynamic.h"),
246-
#required=True,
247283
help="Path for the generated output header file."
248284
)
249285
parser.add_argument(
250286
"--output_source",
251287
default = os.path.join(os.path.dirname(sys.argv[0]), INCLUDE_PATH, "analytics_desktop_dynamic.c"),
252-
#required=True,
253288
help="Path for the generated output source file."
254289
)
255290
args = parser.parse_args()

analytics/src/analytics_desktop.cc

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,10 @@ void Initialize(const App& app) {
6161

6262
#if defined(_WIN32)
6363
if (!g_analytics_module) {
64-
std::vector<std::vector<unsigned char>> allowed_hashes;
65-
std::vector<unsigned char> current_hash;
66-
current_hash.assign(FirebaseAnalytics_WindowsDllHash,
67-
FirebaseAnalytics_WindowsDllHash +
68-
sizeof(FirebaseAnalytics_WindowsDllHash));
69-
allowed_hashes.push_back(current_hash);
64+
std::vector<std::string> allowed_hashes;
65+
for (int i=0; i < FirebaseAnalytics_KnownWindowsDllHashCount; i++) {
66+
allowed_hashes.push_back(FirebaseAnalytics_KnownWindowsDllHash[i]);
67+
}
7068

7169
g_analytics_module =
7270
firebase::analytics::internal::VerifyAndLoadAnalyticsLibrary(

analytics/src/analytics_desktop_dynamic.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,13 @@ static void* g_stub_memory = NULL;
2626
const int FirebaseAnalytics_DynamicFunctionCount = 19;
2727

2828
#if defined(_WIN32)
29-
// Google Analytics Windows DLL SHA256 hash, to be verified on load.
30-
const unsigned char FirebaseAnalytics_WindowsDllHash[] = {
31-
0xc1, 0xb9, 0xff, 0x6e, 0x91, 0x19, 0xc3, 0x0b, 0xbe, 0xb7, 0x47, 0x23, 0x26, 0xdc, 0xde, 0x41, 0x8f, 0x45, 0x68, 0x2e, 0x6b, 0x82, 0x2e, 0x25, 0xee, 0xd9, 0x22, 0xfe, 0x6e, 0x3c, 0xc6, 0x98
29+
// Array of known Google Analytics Windows DLL SHA256 hashes (hex strings).
30+
const char* FirebaseAnalytics_KnownWindowsDllHashes[] = {
31+
"c1b9ff6e9119c30bbeb7472326dcde418f45682e6b822e25eed922fe6e3cc698"
3232
};
33+
34+
// Count of known Google Analytics Windows DLL SHA256 hashes.
35+
const int FirebaseAnalytics_KnownWindowsDllHashCount = 1;
3336
#endif // defined(_WIN32)
3437

3538
// --- Stub Function Definitions ---

analytics/src/analytics_desktop_dynamic.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,10 @@ extern const int FirebaseAnalytics_DynamicFunctionCount;
120120

121121
#include <windows.h>
122122

123-
// Google Analytics Windows DLL SHA256 hash, to be verified before loading.
124-
extern const unsigned char FirebaseAnalytics_WindowsDllHash[32];
123+
// Array of known Google Analytics Windows DLL SHA256 hashes (hex strings).
124+
extern const char* FirebaseAnalytics_KnownWindowsDllHashes[];
125+
// Count of known Google Analytics Windows DLL SHA256 hashes.
126+
extern const int FirebaseAnalytics_KnownWindowsDllHashCount;
125127

126128
// Load Analytics functions from the given DLL handle into function pointers.
127129
// Returns the number of functions successfully loaded.

analytics/src/analytics_windows.cc

Lines changed: 60 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -88,103 +88,134 @@ static std::wstring GetExecutablePath() {
8888
}
8989
}
9090

91-
// Helper function to calculate SHA256 hash of a file.
92-
static std::vector<BYTE> CalculateFileSha256(HANDLE hFile) {
91+
// Helper function to calculate the SHA256 hash of a file and return it as a hex
92+
// string (upper-case).
93+
static std::string CalculateFileSha256(HANDLE hFile) {
9394
HCRYPTPROV hProv = 0;
9495
HCRYPTHASH hHash = 0;
95-
std::vector<BYTE> result_hash_value;
9696

97+
// Ensure the file pointer is at the beginning of the file.
9798
if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
9899
DWORD dwError = GetLastError();
99100
LogError(LOG_TAG "CalculateFileSha256.SetFilePointer failed. Error: %u",
100101
dwError);
101-
return result_hash_value;
102+
return ""; // Return empty string on failure
102103
}
103104

104105
// Acquire Crypto Provider.
105-
// Using CRYPT_VERIFYCONTEXT for operations that don't require private key
106-
// access.
106+
// Using CRYPT_VERIFYCONTEXT for operations that don't require private key access.
107107
if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES,
108108
CRYPT_VERIFYCONTEXT)) {
109109
DWORD dwError = GetLastError();
110110
LogError(LOG_TAG
111111
"CalculateFileSha256.CryptAcquireContextW failed. Error: %u",
112112
dwError);
113-
return result_hash_value;
113+
return "";
114114
}
115115

116+
// Create a hash object.
116117
if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) {
117118
DWORD dwError = GetLastError();
118119
LogError(LOG_TAG "CalculateFileSha256.CryptCreateHash failed. Error: %u",
119120
dwError);
120121
CryptReleaseContext(hProv, 0);
121-
return result_hash_value;
122+
return "";
122123
}
123124

125+
// Read the file in chunks and hash the data.
124126
BYTE rgbFile[1024];
125127
DWORD cbRead = 0;
126-
BOOL bReadSuccessLoop = TRUE;
127-
128128
while (true) {
129-
bReadSuccessLoop = ReadFile(hFile, rgbFile, sizeof(rgbFile), &cbRead, NULL);
130-
if (!bReadSuccessLoop) {
129+
if (!ReadFile(hFile, rgbFile, sizeof(rgbFile), &cbRead, NULL)) {
131130
DWORD dwError = GetLastError();
132131
LogError(LOG_TAG "CalculateFileSha256.ReadFile failed. Error: %u",
133132
dwError);
134133
CryptDestroyHash(hHash);
135134
CryptReleaseContext(hProv, 0);
136-
return result_hash_value;
135+
return "";
137136
}
137+
// End of file
138138
if (cbRead == 0) {
139139
break;
140140
}
141+
// Add the chunk to the hash object.
141142
if (!CryptHashData(hHash, rgbFile, cbRead, 0)) {
142143
DWORD dwError = GetLastError();
143144
LogError(LOG_TAG "CalculateFileSha256.CryptHashData failed. Error: %u",
144145
dwError);
145146
CryptDestroyHash(hHash);
146147
CryptReleaseContext(hProv, 0);
147-
return result_hash_value;
148+
return "";
148149
}
149150
}
150151

152+
// --- Get the binary hash value ---
151153
DWORD cbHashValue = 0;
152154
DWORD dwCount = sizeof(DWORD);
153-
if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&cbHashValue, &dwCount,
154-
0)) {
155+
if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&cbHashValue, &dwCount, 0)) {
155156
DWORD dwError = GetLastError();
156157
LogError(LOG_TAG
157-
"CalculateFileSha256.CryptGetHashParam "
158-
"(HP_HASHSIZE) failed. Error: "
159-
"%u",
158+
"CalculateFileSha256.CryptGetHashParam (HP_HASHSIZE) failed. "
159+
"Error: %u",
160160
dwError);
161161
CryptDestroyHash(hHash);
162162
CryptReleaseContext(hProv, 0);
163-
return result_hash_value;
163+
return "";
164164
}
165165

166-
result_hash_value.resize(cbHashValue);
167-
if (!CryptGetHashParam(hHash, HP_HASHVAL, result_hash_value.data(),
166+
std::vector<BYTE> binary_hash_value(cbHashValue);
167+
if (!CryptGetHashParam(hHash, HP_HASHVAL, binary_hash_value.data(),
168168
&cbHashValue, 0)) {
169169
DWORD dwError = GetLastError();
170170
LogError(LOG_TAG
171171
"CalculateFileSha256.CryptGetHashParam (HP_HASHVAL) failed. "
172172
"Error: %u",
173173
dwError);
174-
result_hash_value.clear();
175174
CryptDestroyHash(hHash);
176175
CryptReleaseContext(hProv, 0);
177-
return result_hash_value;
176+
return "";
177+
}
178+
179+
// --- Convert the binary hash to a hex string ---
180+
DWORD hex_string_size = 0;
181+
if (!CryptBinaryToStringA(binary_hash_value.data(), binary_hash_value.size(),
182+
CRYPT_STRING_HEXRAW | CRYPT_STRING_NOCRLF,
183+
NULL, &hex_string_size)) {
184+
DWORD dwError = GetLastError();
185+
LogError(LOG_TAG
186+
"CalculateFileSha256.CryptBinaryToStringA (size) failed. Error: %u",
187+
dwError);
188+
CryptDestroyHash(hHash);
189+
CryptReleaseContext(hProv, 0);
190+
return "";
178191
}
179192

193+
std::string hex_hash_string(hex_string_size, '\0');
194+
if (!CryptBinaryToStringA(binary_hash_value.data(), binary_hash_value.size(),
195+
CRYPT_STRING_HEXRAW | CRYPT_STRING_NOCRLF,
196+
&hex_hash_string[0], &hex_string_size)) {
197+
DWORD dwError = GetLastError();
198+
LogError(LOG_TAG
199+
"CalculateFileSha256.CryptBinaryToStringA (conversion) failed. Error: %u",
200+
dwError);
201+
CryptDestroyHash(hHash);
202+
CryptReleaseContext(hProv, 0);
203+
return "";
204+
}
205+
206+
// Remove the null terminator from the string.
207+
hex_hash_string.resize(hex_string_size - 1);
208+
209+
// --- Final Cleanup ---
180210
CryptDestroyHash(hHash);
181211
CryptReleaseContext(hProv, 0);
182-
return result_hash_value;
212+
213+
return hex_hash_string;
183214
}
184215

185216
HMODULE VerifyAndLoadAnalyticsLibrary(
186217
const wchar_t* library_filename,
187-
const std::vector<std::vector<unsigned char>>& allowed_hashes) {
218+
const std::vector<std::string>& allowed_hashes) {
188219
if (library_filename == nullptr || library_filename[0] == L'\0') {
189220
LogError(LOG_TAG "Invalid arguments.");
190221
return nullptr;
@@ -245,22 +276,14 @@ HMODULE VerifyAndLoadAnalyticsLibrary(
245276

246277
HMODULE hModule = nullptr;
247278

248-
std::vector<BYTE> calculated_hash = CalculateFileSha256(hFile);
279+
std::string calculated_hash = CalculateFileSha256(hFile);
249280

250-
if (calculated_hash.empty()) {
281+
if (calculated_hash.length() == 0) {
251282
LogError(LOG_TAG "Hash failed for Analytics DLL.");
252283
} else {
253284
bool hash_matched = false;
254285
for (const auto& expected_hash : allowed_hashes) {
255-
if (calculated_hash.size() != expected_hash.size()) {
256-
LogDebug(LOG_TAG
257-
"Hash size mismatch for Analytics DLL. Expected: %zu, "
258-
"Calculated: %zu. Trying next allowed hash.",
259-
expected_hash.size(), calculated_hash.size());
260-
continue;
261-
}
262-
if (memcmp(calculated_hash.data(), expected_hash.data(),
263-
expected_hash.size()) == 0) {
286+
if (calculated_hash == expected_hash) {
264287
hash_matched = true;
265288
break;
266289
}

0 commit comments

Comments
 (0)