1 /* 2 * Copyright 2008 Juan Lang 3 * Copyright 2010 Andrey Turkin 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 18 */ 19 #include <stdio.h> 20 #include <stdarg.h> 21 22 #define NONAMELESSUNION 23 #include <windef.h> 24 #include <winbase.h> 25 #include <winver.h> 26 #include <winnt.h> 27 #include <imagehlp.h> 28 29 #include "wine/test.h" 30 31 static HMODULE hImageHlp; 32 33 static BOOL (WINAPI *pImageGetDigestStream)(HANDLE, DWORD, DIGEST_FUNCTION, DIGEST_HANDLE); 34 static BOOL (WINAPI *pBindImageEx)(DWORD Flags, const char *ImageName, const char *DllPath, 35 const char *SymbolPath, PIMAGEHLP_STATUS_ROUTINE StatusRoutine); 36 37 /* minimal PE file image */ 38 #define VA_START 0x400000 39 #define FILE_PE_START 0x50 40 #define NUM_SECTIONS 3 41 #define FILE_TEXT 0x200 42 #define RVA_TEXT 0x1000 43 #define RVA_BSS 0x2000 44 #define FILE_IDATA 0x400 45 #define RVA_IDATA 0x3000 46 #define FILE_TOTAL 0x600 47 #define RVA_TOTAL 0x4000 48 #include <pshpack1.h> 49 struct Imports { 50 IMAGE_IMPORT_DESCRIPTOR descriptors[2]; 51 IMAGE_THUNK_DATA32 original_thunks[2]; 52 IMAGE_THUNK_DATA32 thunks[2]; 53 struct __IMPORT_BY_NAME { 54 WORD hint; 55 char funcname[0x20]; 56 } ibn; 57 char dllname[0x10]; 58 }; 59 #ifdef __REACTOS__ 60 #define EXIT_PROCESS (VA_START+RVA_IDATA+FIELD_OFFSET(struct Imports, thunks)) 61 #else 62 #define EXIT_PROCESS (VA_START+RVA_IDATA+FIELD_OFFSET(struct Imports, thunks[0])) 63 #endif 64 65 static struct _PeImage { 66 IMAGE_DOS_HEADER dos_header; 67 char __alignment1[FILE_PE_START - sizeof(IMAGE_DOS_HEADER)]; 68 IMAGE_NT_HEADERS32 nt_headers; 69 IMAGE_SECTION_HEADER sections[NUM_SECTIONS]; 70 char __alignment2[FILE_TEXT - FILE_PE_START - sizeof(IMAGE_NT_HEADERS32) - 71 NUM_SECTIONS * sizeof(IMAGE_SECTION_HEADER)]; 72 unsigned char text_section[FILE_IDATA-FILE_TEXT]; 73 struct Imports idata_section; 74 char __alignment3[FILE_TOTAL-FILE_IDATA-sizeof(struct Imports)]; 75 } bin = { 76 /* dos header */ 77 {IMAGE_DOS_SIGNATURE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, 0, {0}, FILE_PE_START}, 78 /* alignment before PE header */ 79 {0}, 80 /* nt headers */ 81 {IMAGE_NT_SIGNATURE, 82 /* basic headers - 3 sections, no symbols, EXE file */ 83 {IMAGE_FILE_MACHINE_I386, NUM_SECTIONS, 0, 0, 0, sizeof(IMAGE_OPTIONAL_HEADER32), 84 IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_EXECUTABLE_IMAGE}, 85 /* optional header */ 86 {IMAGE_NT_OPTIONAL_HDR32_MAGIC, 4, 0, FILE_IDATA-FILE_TEXT, 87 FILE_TOTAL-FILE_IDATA + FILE_IDATA-FILE_TEXT, 0x400, 88 RVA_TEXT, RVA_TEXT, RVA_BSS, VA_START, 0x1000, 0x200, 4, 0, 1, 0, 4, 0, 0, 89 RVA_TOTAL, FILE_TEXT, 0, IMAGE_SUBSYSTEM_WINDOWS_GUI, 0, 90 0x200000, 0x1000, 0x100000, 0x1000, 0, 0x10, 91 {{0, 0}, 92 {RVA_IDATA, sizeof(struct Imports)} 93 } 94 } 95 }, 96 /* sections */ 97 { 98 {".text", {0x100}, RVA_TEXT, FILE_IDATA-FILE_TEXT, FILE_TEXT, 99 0, 0, 0, 0, IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ}, 100 {".bss", {0x400}, RVA_BSS, 0, 0, 0, 0, 0, 0, 101 IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE}, 102 {".idata", {sizeof(struct Imports)}, RVA_IDATA, FILE_TOTAL-FILE_IDATA, FILE_IDATA, 0, 103 0, 0, 0, IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE} 104 }, 105 /* alignment before first section */ 106 {0}, 107 /* .text section */ 108 { 109 0x31, 0xC0, /* xor eax, eax */ 110 0xFF, 0x25, EXIT_PROCESS&0xFF, (EXIT_PROCESS>>8)&0xFF, (EXIT_PROCESS>>16)&0xFF, 111 (EXIT_PROCESS>>24)&0xFF, /* jmp ExitProcess */ 112 0 113 }, 114 /* .idata section */ 115 { 116 { 117 {{RVA_IDATA + FIELD_OFFSET(struct Imports, original_thunks)}, 0, 0, 118 RVA_IDATA + FIELD_OFFSET(struct Imports, dllname), 119 RVA_IDATA + FIELD_OFFSET(struct Imports, thunks) 120 }, 121 {{0}, 0, 0, 0, 0} 122 }, 123 {{{RVA_IDATA+FIELD_OFFSET(struct Imports, ibn)}}, {{0}}}, 124 {{{RVA_IDATA+FIELD_OFFSET(struct Imports, ibn)}}, {{0}}}, 125 {0,"ExitProcess"}, 126 "KERNEL32.DLL" 127 }, 128 /* final alignment */ 129 {0} 130 }; 131 #include <poppack.h> 132 133 struct blob 134 { 135 DWORD cb; 136 BYTE *pb; 137 }; 138 139 struct expected_blob 140 { 141 DWORD cb; 142 const void *pb; 143 }; 144 145 struct update_accum 146 { 147 DWORD cUpdates; 148 struct blob *updates; 149 }; 150 151 struct expected_update_accum 152 { 153 DWORD cUpdates; 154 const struct expected_blob *updates; 155 BOOL todo; 156 }; 157 158 static int status_routine_called[BindSymbolsNotUpdated+1]; 159 160 161 static BOOL WINAPI accumulating_stream_output(DIGEST_HANDLE handle, BYTE *pb, 162 DWORD cb) 163 { 164 struct update_accum *accum = (struct update_accum *)handle; 165 BOOL ret = FALSE; 166 167 if (accum->cUpdates) 168 accum->updates = HeapReAlloc(GetProcessHeap(), 0, accum->updates, 169 (accum->cUpdates + 1) * sizeof(struct blob)); 170 else 171 accum->updates = HeapAlloc(GetProcessHeap(), 0, sizeof(struct blob)); 172 if (accum->updates) 173 { 174 struct blob *blob = &accum->updates[accum->cUpdates]; 175 176 blob->pb = HeapAlloc(GetProcessHeap(), 0, cb); 177 if (blob->pb) 178 { 179 memcpy(blob->pb, pb, cb); 180 blob->cb = cb; 181 ret = TRUE; 182 } 183 accum->cUpdates++; 184 } 185 return ret; 186 } 187 188 static void check_updates(LPCSTR header, const struct expected_update_accum *expected, 189 const struct update_accum *got) 190 { 191 DWORD i; 192 193 todo_wine_if (expected->todo) 194 ok(expected->cUpdates == got->cUpdates, "%s: expected %d updates, got %d\n", 195 header, expected->cUpdates, got->cUpdates); 196 for (i = 0; i < min(expected->cUpdates, got->cUpdates); i++) 197 { 198 ok(expected->updates[i].cb == got->updates[i].cb, "%s, update %d: expected %d bytes, got %d\n", 199 header, i, expected->updates[i].cb, got->updates[i].cb); 200 if (expected->updates[i].cb && expected->updates[i].cb == got->updates[i].cb) 201 ok(!memcmp(expected->updates[i].pb, got->updates[i].pb, got->updates[i].cb), 202 "%s, update %d: unexpected value\n", header, i); 203 } 204 } 205 206 /* Frees the updates stored in accum */ 207 static void free_updates(struct update_accum *accum) 208 { 209 DWORD i; 210 211 for (i = 0; i < accum->cUpdates; i++) 212 HeapFree(GetProcessHeap(), 0, accum->updates[i].pb); 213 HeapFree(GetProcessHeap(), 0, accum->updates); 214 accum->updates = NULL; 215 accum->cUpdates = 0; 216 } 217 218 static const struct expected_blob b1[] = { 219 {FILE_PE_START, &bin}, 220 /* with zeroed Checksum/SizeOfInitializedData/SizeOfImage fields */ 221 {sizeof(bin.nt_headers), &bin.nt_headers}, 222 {sizeof(bin.sections), &bin.sections}, 223 {FILE_IDATA-FILE_TEXT, &bin.text_section}, 224 {sizeof(bin.idata_section.descriptors[0].u.OriginalFirstThunk), 225 &bin.idata_section.descriptors[0].u.OriginalFirstThunk}, 226 #ifdef __REACTOS__ 227 {FIELD_OFFSET(struct Imports, thunks)- 228 (FIELD_OFFSET(struct Imports, descriptors)+FIELD_OFFSET(IMAGE_IMPORT_DESCRIPTOR, Name)), 229 #else 230 {FIELD_OFFSET(struct Imports, thunks)-FIELD_OFFSET(struct Imports, descriptors[0].Name), 231 #endif 232 &bin.idata_section.descriptors[0].Name}, 233 {FILE_TOTAL-FILE_IDATA-FIELD_OFFSET(struct Imports, ibn), 234 &bin.idata_section.ibn} 235 }; 236 static const struct expected_update_accum a1 = { sizeof(b1) / sizeof(b1[0]), b1, TRUE }; 237 238 static const struct expected_blob b2[] = { 239 {FILE_PE_START, &bin}, 240 /* with zeroed Checksum/SizeOfInitializedData/SizeOfImage fields */ 241 {sizeof(bin.nt_headers), &bin.nt_headers}, 242 {sizeof(bin.sections), &bin.sections}, 243 {FILE_IDATA-FILE_TEXT, &bin.text_section}, 244 {FILE_TOTAL-FILE_IDATA, &bin.idata_section} 245 }; 246 static const struct expected_update_accum a2 = { sizeof(b2) / sizeof(b2[0]), b2, FALSE }; 247 248 /* Creates a test file and returns a handle to it. The file's path is returned 249 * in temp_file, which must be at least MAX_PATH characters in length. 250 */ 251 static HANDLE create_temp_file(char *temp_file) 252 { 253 HANDLE file = INVALID_HANDLE_VALUE; 254 char temp_path[MAX_PATH]; 255 256 if (GetTempPathA(sizeof(temp_path), temp_path)) 257 { 258 if (GetTempFileNameA(temp_path, "img", 0, temp_file)) 259 file = CreateFileA(temp_file, GENERIC_READ | GENERIC_WRITE, 0, NULL, 260 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 261 } 262 return file; 263 } 264 265 static void update_checksum(void) 266 { 267 WORD const * ptr; 268 DWORD size; 269 DWORD sum = 0; 270 271 bin.nt_headers.OptionalHeader.CheckSum = 0; 272 273 for(ptr = (WORD const *)&bin, size = (sizeof(bin)+1)/sizeof(WORD); size > 0; ptr++, size--) 274 { 275 sum += *ptr; 276 if (HIWORD(sum) != 0) 277 { 278 sum = LOWORD(sum) + HIWORD(sum); 279 } 280 } 281 sum = (WORD)(LOWORD(sum) + HIWORD(sum)); 282 sum += sizeof(bin); 283 284 bin.nt_headers.OptionalHeader.CheckSum = sum; 285 } 286 287 static BOOL CALLBACK testing_status_routine(IMAGEHLP_STATUS_REASON reason, const char *ImageName, 288 const char *DllName, ULONG_PTR Va, ULONG_PTR Parameter) 289 { 290 char kernel32_path[MAX_PATH]; 291 292 if (0 <= (int)reason && reason <= BindSymbolsNotUpdated) 293 status_routine_called[reason]++; 294 else 295 ok(0, "expected reason between 0 and %d, got %d\n", BindSymbolsNotUpdated+1, reason); 296 297 switch(reason) 298 { 299 case BindImportModule: 300 ok(!strcmp(DllName, "KERNEL32.DLL"), "expected DllName to be KERNEL32.DLL, got %s\n", 301 DllName); 302 break; 303 304 case BindImportProcedure: 305 case BindForwarderNOT: 306 GetSystemDirectoryA(kernel32_path, MAX_PATH); 307 strcat(kernel32_path, "\\KERNEL32.DLL"); 308 ok(!lstrcmpiA(DllName, kernel32_path), "expected DllName to be %s, got %s\n", 309 kernel32_path, DllName); 310 ok(!strcmp((char *)Parameter, "ExitProcess"), 311 "expected Parameter to be ExitProcess, got %s\n", (char *)Parameter); 312 break; 313 314 default: 315 ok(0, "got unexpected reason %d\n", reason); 316 break; 317 } 318 return TRUE; 319 } 320 321 static void test_get_digest_stream(void) 322 { 323 BOOL ret; 324 HANDLE file; 325 char temp_file[MAX_PATH]; 326 DWORD count; 327 struct update_accum accum = { 0, NULL }; 328 329 if (!pImageGetDigestStream) 330 { 331 win_skip("ImageGetDigestStream function is not available\n"); 332 return; 333 } 334 SetLastError(0xdeadbeef); 335 ret = pImageGetDigestStream(NULL, 0, NULL, NULL); 336 ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, 337 "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); 338 file = create_temp_file(temp_file); 339 if (file == INVALID_HANDLE_VALUE) 340 { 341 skip("couldn't create temp file\n"); 342 return; 343 } 344 SetLastError(0xdeadbeef); 345 ret = pImageGetDigestStream(file, 0, NULL, NULL); 346 ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, 347 "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); 348 SetLastError(0xdeadbeef); 349 ret = pImageGetDigestStream(NULL, 0, accumulating_stream_output, &accum); 350 ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, 351 "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); 352 /* Even with "valid" parameters, it fails with an empty file */ 353 SetLastError(0xdeadbeef); 354 ret = pImageGetDigestStream(file, 0, accumulating_stream_output, &accum); 355 ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, 356 "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); 357 /* Finally, with a valid executable in the file, it succeeds. Note that 358 * the file pointer need not be positioned at the beginning. 359 */ 360 update_checksum(); 361 WriteFile(file, &bin, sizeof(bin), &count, NULL); 362 FlushFileBuffers(file); 363 364 /* zero out some fields ImageGetDigestStream would zero out */ 365 bin.nt_headers.OptionalHeader.CheckSum = 0; 366 bin.nt_headers.OptionalHeader.SizeOfInitializedData = 0; 367 bin.nt_headers.OptionalHeader.SizeOfImage = 0; 368 369 ret = pImageGetDigestStream(file, 0, accumulating_stream_output, &accum); 370 ok(ret, "ImageGetDigestStream failed: %d\n", GetLastError()); 371 check_updates("flags = 0", &a1, &accum); 372 free_updates(&accum); 373 ret = pImageGetDigestStream(file, CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO, 374 accumulating_stream_output, &accum); 375 ok(ret, "ImageGetDigestStream failed: %d\n", GetLastError()); 376 check_updates("flags = CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO", &a2, &accum); 377 free_updates(&accum); 378 CloseHandle(file); 379 DeleteFileA(temp_file); 380 } 381 382 static void test_bind_image_ex(void) 383 { 384 BOOL ret; 385 HANDLE file; 386 char temp_file[MAX_PATH]; 387 DWORD count; 388 389 if (!pBindImageEx) 390 { 391 win_skip("BindImageEx function is not available\n"); 392 return; 393 } 394 395 /* call with a non-existent file */ 396 SetLastError(0xdeadbeef); 397 ret = pBindImageEx(BIND_NO_BOUND_IMPORTS | BIND_NO_UPDATE | BIND_ALL_IMAGES, "nonexistent.dll", 0, 0, 398 testing_status_routine); 399 todo_wine ok(!ret && ((GetLastError() == ERROR_FILE_NOT_FOUND) || 400 (GetLastError() == ERROR_INVALID_PARAMETER)), 401 "expected ERROR_FILE_NOT_FOUND or ERROR_INVALID_PARAMETER, got %d\n", 402 GetLastError()); 403 404 file = create_temp_file(temp_file); 405 if (file == INVALID_HANDLE_VALUE) 406 { 407 skip("couldn't create temp file\n"); 408 return; 409 } 410 411 WriteFile(file, &bin, sizeof(bin), &count, NULL); 412 CloseHandle(file); 413 414 /* call with a proper PE file, but with StatusRoutine set to NULL */ 415 ret = pBindImageEx(BIND_NO_BOUND_IMPORTS | BIND_NO_UPDATE | BIND_ALL_IMAGES, temp_file, 0, 0, 416 NULL); 417 ok(ret, "BindImageEx failed: %d\n", GetLastError()); 418 419 /* call with a proper PE file and StatusRoutine */ 420 ret = pBindImageEx(BIND_NO_BOUND_IMPORTS | BIND_NO_UPDATE | BIND_ALL_IMAGES, temp_file, 0, 0, 421 testing_status_routine); 422 ok(ret, "BindImageEx failed: %d\n", GetLastError()); 423 424 todo_wine ok(status_routine_called[BindImportModule] == 1, 425 "StatusRoutine was called %d times\n", status_routine_called[BindImportModule]); 426 427 todo_wine ok((status_routine_called[BindImportProcedure] == 1) 428 #if defined(_WIN64) 429 || broken(status_routine_called[BindImportProcedure] == 0) /* < Win8 */ 430 #endif 431 , "StatusRoutine was called %d times\n", status_routine_called[BindImportProcedure]); 432 433 DeleteFileA(temp_file); 434 } 435 436 START_TEST(image) 437 { 438 hImageHlp = LoadLibraryA("imagehlp.dll"); 439 440 if (!hImageHlp) 441 { 442 win_skip("ImageHlp unavailable\n"); 443 return; 444 } 445 446 pImageGetDigestStream = (void *) GetProcAddress(hImageHlp, "ImageGetDigestStream"); 447 pBindImageEx = (void *) GetProcAddress(hImageHlp, "BindImageEx"); 448 449 test_get_digest_stream(); 450 test_bind_image_ex(); 451 452 FreeLibrary(hImageHlp); 453 } 454