1 /* 2 * PE file resources 3 * 4 * Copyright 1995 Thomas Sandford 5 * Copyright 1996 Martin von Loewis 6 * Copyright 2003 Alexandre Julliard 7 * Copyright 1993 Robert J. Amstadt 8 * Copyright 1997 Marcus Meissner 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 */ 24 25 /* INCLUDES *****************************************************************/ 26 27 #include <rtl.h> 28 29 #define NDEBUG 30 #include <debug.h> 31 32 NTSTATUS find_entry( PVOID BaseAddress, LDR_RESOURCE_INFO *info, 33 ULONG level, void **ret, int want_dir ); 34 35 /* FUNCTIONS ****************************************************************/ 36 37 int page_fault(ULONG ExceptionCode) 38 { 39 if (ExceptionCode == EXCEPTION_ACCESS_VIOLATION || 40 ExceptionCode == EXCEPTION_PRIV_INSTRUCTION) 41 return EXCEPTION_EXECUTE_HANDLER; 42 return EXCEPTION_CONTINUE_SEARCH; 43 } 44 45 /********************************************************************** 46 * is_data_file_module 47 * 48 * Check if a module handle is for a LOAD_LIBRARY_AS_DATAFILE module. 49 */ 50 static int is_data_file_module( PVOID BaseAddress ) 51 { 52 return (ULONG_PTR)BaseAddress & 1; 53 } 54 55 56 /********************************************************************** 57 * push_language 58 * 59 * push a language in the list of languages to try 60 */ 61 int push_language( USHORT *list, ULONG pos, WORD lang ) 62 { 63 ULONG i; 64 for (i = 0; i < pos; i++) if (list[i] == lang) return pos; 65 list[pos++] = lang; 66 return pos; 67 } 68 69 70 /********************************************************************** 71 * find_first_entry 72 * 73 * Find the first suitable entry in a resource directory 74 */ 75 IMAGE_RESOURCE_DIRECTORY *find_first_entry( IMAGE_RESOURCE_DIRECTORY *dir, 76 void *root, int want_dir ) 77 { 78 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1); 79 int pos; 80 81 for (pos = 0; pos < dir->NumberOfNamedEntries + dir->NumberOfIdEntries; pos++) 82 { 83 if (!entry[pos].DataIsDirectory == !want_dir) 84 return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].OffsetToDirectory); 85 } 86 return NULL; 87 } 88 89 90 /********************************************************************** 91 * find_entry_by_id 92 * 93 * Find an entry by id in a resource directory 94 */ 95 IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( IMAGE_RESOURCE_DIRECTORY *dir, 96 WORD id, void *root, int want_dir ) 97 { 98 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry; 99 int min, max, pos; 100 101 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1); 102 min = dir->NumberOfNamedEntries; 103 max = min + dir->NumberOfIdEntries - 1; 104 while (min <= max) 105 { 106 pos = (min + max) / 2; 107 if (entry[pos].Id == id) 108 { 109 if (!entry[pos].DataIsDirectory == !want_dir) 110 { 111 DPRINT("root %p dir %p id %04x ret %p\n", 112 root, dir, id, (const char*)root + entry[pos].OffsetToDirectory); 113 return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].OffsetToDirectory); 114 } 115 break; 116 } 117 if (entry[pos].Id > id) max = pos - 1; 118 else min = pos + 1; 119 } 120 DPRINT("root %p dir %p id %04x not found\n", root, dir, id ); 121 return NULL; 122 } 123 124 125 /********************************************************************** 126 * find_entry_by_name 127 * 128 * Find an entry by name in a resource directory 129 */ 130 IMAGE_RESOURCE_DIRECTORY *find_entry_by_name( IMAGE_RESOURCE_DIRECTORY *dir, 131 LPCWSTR name, void *root, 132 int want_dir ) 133 { 134 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry; 135 const IMAGE_RESOURCE_DIR_STRING_U *str; 136 int min, max, res, pos; 137 size_t namelen; 138 139 if (!((ULONG_PTR)name & 0xFFFF0000)) return find_entry_by_id( dir, (ULONG_PTR)name & 0xFFFF, root, want_dir ); 140 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1); 141 namelen = wcslen(name); 142 min = 0; 143 max = dir->NumberOfNamedEntries - 1; 144 while (min <= max) 145 { 146 pos = (min + max) / 2; 147 str = (const IMAGE_RESOURCE_DIR_STRING_U *)((const char *)root + entry[pos].NameOffset); 148 res = _wcsnicmp( name, str->NameString, str->Length ); 149 if (!res && namelen == str->Length) 150 { 151 if (!entry[pos].DataIsDirectory == !want_dir) 152 { 153 DPRINT("root %p dir %p name %ws ret %p\n", 154 root, dir, name, (const char*)root + entry[pos].OffsetToDirectory); 155 return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].OffsetToDirectory); 156 } 157 break; 158 } 159 if (res < 0) max = pos - 1; 160 else min = pos + 1; 161 } 162 DPRINT("root %p dir %p name %ws not found\n", root, dir, name); 163 return NULL; 164 } 165 166 #ifdef __i386__ 167 NTSTATUS NTAPI LdrpAccessResource( PVOID BaseAddress, IMAGE_RESOURCE_DATA_ENTRY *entry, 168 void **ptr, ULONG *size ) 169 #else 170 static NTSTATUS LdrpAccessResource( PVOID BaseAddress, IMAGE_RESOURCE_DATA_ENTRY *entry, 171 void **ptr, ULONG *size ) 172 #endif 173 { 174 NTSTATUS status = STATUS_SUCCESS; 175 176 _SEH2_TRY 177 { 178 ULONG dirsize; 179 180 if (!RtlImageDirectoryEntryToData( BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &dirsize )) 181 status = STATUS_RESOURCE_DATA_NOT_FOUND; 182 else 183 { 184 if (ptr) 185 { 186 if (is_data_file_module(BaseAddress)) 187 { 188 PVOID mod = (PVOID)((ULONG_PTR)BaseAddress & ~1); 189 *ptr = RtlImageRvaToVa( RtlImageNtHeader(mod), mod, entry->OffsetToData, NULL ); 190 } 191 else *ptr = (char *)BaseAddress + entry->OffsetToData; 192 } 193 if (size) *size = entry->Size; 194 } 195 } 196 _SEH2_EXCEPT(page_fault(_SEH2_GetExceptionCode())) 197 { 198 status = _SEH2_GetExceptionCode(); 199 } 200 _SEH2_END; 201 return status; 202 } 203 204 205 /* 206 * @implemented 207 */ 208 NTSTATUS NTAPI 209 LdrFindResource_U(PVOID BaseAddress, 210 PLDR_RESOURCE_INFO ResourceInfo, 211 ULONG Level, 212 PIMAGE_RESOURCE_DATA_ENTRY* ResourceDataEntry) 213 { 214 void *res; 215 NTSTATUS status = STATUS_SUCCESS; 216 217 _SEH2_TRY 218 { 219 if (ResourceInfo) 220 { 221 DPRINT( "module %p type %lx name %lx lang %04lx level %lu\n", 222 BaseAddress, ResourceInfo->Type, 223 Level > 1 ? ResourceInfo->Name : 0, 224 Level > 2 ? ResourceInfo->Language : 0, Level ); 225 } 226 227 status = find_entry( BaseAddress, ResourceInfo, Level, &res, FALSE ); 228 if (NT_SUCCESS(status)) 229 *ResourceDataEntry = res; 230 } 231 _SEH2_EXCEPT(page_fault(_SEH2_GetExceptionCode())) 232 { 233 status = _SEH2_GetExceptionCode(); 234 } 235 _SEH2_END; 236 return status; 237 } 238 239 #ifndef __i386__ 240 /* 241 * @implemented 242 */ 243 NTSTATUS NTAPI 244 LdrAccessResource(IN PVOID BaseAddress, 245 IN PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry, 246 OUT PVOID* Resource OPTIONAL, 247 OUT PULONG Size OPTIONAL) 248 { 249 return LdrpAccessResource( BaseAddress, ResourceDataEntry, Resource, Size ); 250 } 251 #endif 252 253 /* 254 * @implemented 255 */ 256 NTSTATUS NTAPI 257 LdrFindResourceDirectory_U(IN PVOID BaseAddress, 258 IN PLDR_RESOURCE_INFO info, 259 IN ULONG level, 260 OUT PIMAGE_RESOURCE_DIRECTORY* addr) 261 { 262 void *res; 263 NTSTATUS status = STATUS_SUCCESS; 264 265 _SEH2_TRY 266 { 267 if (info) 268 { 269 DPRINT( "module %p type %ws name %ws lang %04lx level %lu\n", 270 BaseAddress, (LPCWSTR)info->Type, 271 level > 1 ? (LPCWSTR)info->Name : L"", 272 level > 2 ? info->Language : 0, level ); 273 } 274 275 status = find_entry( BaseAddress, info, level, &res, TRUE ); 276 if (NT_SUCCESS(status)) 277 *addr = res; 278 } 279 _SEH2_EXCEPT(page_fault(_SEH2_GetExceptionCode())) 280 { 281 status = _SEH2_GetExceptionCode(); 282 } 283 _SEH2_END; 284 return status; 285 } 286 287 288 #define NAME_FROM_RESOURCE_ENTRY(RootDirectory, Entry) \ 289 ((Entry)->NameIsString ? (ULONG_PTR)(RootDirectory) + (Entry)->NameOffset : (Entry)->Id) 290 291 static 292 LONG 293 LdrpCompareResourceNames_U( 294 _In_ PUCHAR ResourceData, 295 _In_ PIMAGE_RESOURCE_DIRECTORY_ENTRY Entry, 296 _In_ ULONG_PTR CompareName) 297 { 298 PIMAGE_RESOURCE_DIR_STRING_U ResourceString; 299 PWSTR String1, String2; 300 USHORT ResourceStringLength; 301 WCHAR Char1, Char2; 302 303 /* Check if the resource name is an ID */ 304 if (CompareName <= USHRT_MAX) 305 { 306 /* Just compare the 2 IDs */ 307 return (CompareName - Entry->Id); 308 } 309 else 310 { 311 /* Get the resource string */ 312 ResourceString = (PIMAGE_RESOURCE_DIR_STRING_U)(ResourceData + 313 Entry->NameOffset); 314 315 /* Get the string length */ 316 ResourceStringLength = ResourceString->Length; 317 318 String1 = ResourceString->NameString; 319 String2 = (PWSTR)CompareName; 320 321 /* Loop all characters of the resource string */ 322 while (ResourceStringLength--) 323 { 324 /* Get the next characters */ 325 Char1 = *String1++; 326 Char2 = *String2++; 327 328 /* Check if they don't match, or if the compare string ends */ 329 if ((Char1 != Char2) || (Char2 == 0)) 330 { 331 /* They don't match, fail */ 332 return Char2 - Char1; 333 } 334 } 335 336 /* All characters match, check if the compare string ends here */ 337 return (*String2 == 0) ? 0 : 1; 338 } 339 } 340 341 NTSTATUS 342 NTAPI 343 LdrEnumResources( 344 _In_ PVOID ImageBase, 345 _In_ PLDR_RESOURCE_INFO ResourceInfo, 346 _In_ ULONG Level, 347 _Inout_ ULONG *ResourceCount, 348 _Out_writes_to_(*ResourceCount,*ResourceCount) LDR_ENUM_RESOURCE_INFO *Resources) 349 { 350 PUCHAR ResourceData; 351 NTSTATUS Status; 352 ULONG i, j, k; 353 ULONG NumberOfTypeEntries, NumberOfNameEntries, NumberOfLangEntries; 354 ULONG Count, MaxResourceCount; 355 PIMAGE_RESOURCE_DIRECTORY TypeDirectory, NameDirectory, LangDirectory; 356 PIMAGE_RESOURCE_DIRECTORY_ENTRY TypeEntry, NameEntry, LangEntry; 357 PIMAGE_RESOURCE_DATA_ENTRY DataEntry; 358 ULONG Size; 359 LONG Result; 360 361 /* If the caller wants data, get the maximum count of entries */ 362 MaxResourceCount = (Resources != NULL) ? *ResourceCount : 0; 363 364 /* Default to 0 */ 365 *ResourceCount = 0; 366 367 /* Locate the resource directory */ 368 ResourceData = RtlImageDirectoryEntryToData(ImageBase, 369 TRUE, 370 IMAGE_DIRECTORY_ENTRY_RESOURCE, 371 &Size); 372 if (ResourceData == NULL) 373 return STATUS_RESOURCE_DATA_NOT_FOUND; 374 375 /* The type directory is at the root, followed by the entries */ 376 TypeDirectory = (PIMAGE_RESOURCE_DIRECTORY)ResourceData; 377 TypeEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(TypeDirectory + 1); 378 379 /* Get the number of entries in the type directory */ 380 NumberOfTypeEntries = TypeDirectory->NumberOfNamedEntries + 381 TypeDirectory->NumberOfIdEntries; 382 383 /* Start with 0 resources and status success */ 384 Status = STATUS_SUCCESS; 385 Count = 0; 386 387 /* Loop all entries in the type directory */ 388 for (i = 0; i < NumberOfTypeEntries; ++i, ++TypeEntry) 389 { 390 /* Check if comparison of types is requested */ 391 if (Level > RESOURCE_TYPE_LEVEL) 392 { 393 /* Compare the type with the requested Type */ 394 Result = LdrpCompareResourceNames_U(ResourceData, 395 TypeEntry, 396 ResourceInfo->Type); 397 398 /* Not equal, continue with next entry */ 399 if (Result != 0) continue; 400 } 401 402 /* The entry must point to the name directory */ 403 if (!TypeEntry->DataIsDirectory) 404 { 405 return STATUS_INVALID_IMAGE_FORMAT; 406 } 407 408 /* Get a pointer to the name subdirectory and it's first entry */ 409 NameDirectory = (PIMAGE_RESOURCE_DIRECTORY)(ResourceData + 410 TypeEntry->OffsetToDirectory); 411 NameEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(NameDirectory + 1); 412 413 /* Get the number of entries in the name directory */ 414 NumberOfNameEntries = NameDirectory->NumberOfNamedEntries + 415 NameDirectory->NumberOfIdEntries; 416 417 /* Loop all entries in the name directory */ 418 for (j = 0; j < NumberOfNameEntries; ++j, ++NameEntry) 419 { 420 /* Check if comparison of names is requested */ 421 if (Level > RESOURCE_NAME_LEVEL) 422 { 423 /* Compare the name with the requested name */ 424 Result = LdrpCompareResourceNames_U(ResourceData, 425 NameEntry, 426 ResourceInfo->Name); 427 428 /* Not equal, continue with next entry */ 429 if (Result != 0) continue; 430 } 431 432 /* The entry must point to the language directory */ 433 if (!NameEntry->DataIsDirectory) 434 { 435 return STATUS_INVALID_IMAGE_FORMAT; 436 } 437 438 /* Get a pointer to the language subdirectory and it's first entry */ 439 LangDirectory = (PIMAGE_RESOURCE_DIRECTORY)(ResourceData + 440 NameEntry->OffsetToDirectory); 441 LangEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(LangDirectory + 1); 442 443 /* Get the number of entries in the language directory */ 444 NumberOfLangEntries = LangDirectory->NumberOfNamedEntries + 445 LangDirectory->NumberOfIdEntries; 446 447 /* Loop all entries in the language directory */ 448 for (k = 0; k < NumberOfLangEntries; ++k, ++LangEntry) 449 { 450 /* Check if comparison of languages is requested */ 451 if (Level > RESOURCE_LANGUAGE_LEVEL) 452 { 453 /* Compare the language with the requested language */ 454 Result = LdrpCompareResourceNames_U(ResourceData, 455 LangEntry, 456 ResourceInfo->Language); 457 458 /* Not equal, continue with next entry */ 459 if (Result != 0) continue; 460 } 461 462 /* This entry must point to data */ 463 if (LangEntry->DataIsDirectory) 464 { 465 return STATUS_INVALID_IMAGE_FORMAT; 466 } 467 468 /* Get a pointer to the data entry */ 469 DataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)(ResourceData + 470 LangEntry->OffsetToData); 471 472 /* Check if there is still space to store the data */ 473 if (Count < MaxResourceCount) 474 { 475 /* There is, fill the entry */ 476 Resources[Count].Type = 477 NAME_FROM_RESOURCE_ENTRY(ResourceData, TypeEntry); 478 Resources[Count].Name = 479 NAME_FROM_RESOURCE_ENTRY(ResourceData, NameEntry); 480 Resources[Count].Language = 481 NAME_FROM_RESOURCE_ENTRY(ResourceData, LangEntry); 482 Resources[Count].Data = (PUCHAR)ImageBase + DataEntry->OffsetToData; 483 Resources[Count].Reserved = 0; 484 Resources[Count].Size = DataEntry->Size; 485 } 486 else 487 { 488 /* There is not enough space, save error status */ 489 Status = STATUS_INFO_LENGTH_MISMATCH; 490 } 491 492 /* Count this resource */ 493 ++Count; 494 } 495 } 496 } 497 498 /* Return the number of matching resources */ 499 *ResourceCount = Count; 500 return Status; 501 } 502