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
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.
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.
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
25 /* INCLUDES *****************************************************************/
32 NTSTATUS
find_entry( PVOID BaseAddress
, LDR_RESOURCE_INFO
*info
,
33 ULONG level
, void **ret
, int want_dir
);
35 /* FUNCTIONS ****************************************************************/
37 int page_fault(ULONG ExceptionCode
)
39 if (ExceptionCode
== EXCEPTION_ACCESS_VIOLATION
||
40 ExceptionCode
== EXCEPTION_PRIV_INSTRUCTION
)
41 return EXCEPTION_EXECUTE_HANDLER
;
42 return EXCEPTION_CONTINUE_SEARCH
;
45 /**********************************************************************
48 * Check if a module handle is for a LOAD_LIBRARY_AS_DATAFILE module.
50 static int is_data_file_module( PVOID BaseAddress
)
52 return (ULONG_PTR
)BaseAddress
& 1;
56 /**********************************************************************
59 * push a language in the list of languages to try
61 int push_language( USHORT
*list
, ULONG pos
, WORD lang
)
64 for (i
= 0; i
< pos
; i
++) if (list
[i
] == lang
) return pos
;
70 /**********************************************************************
73 * Find the first suitable entry in a resource directory
75 IMAGE_RESOURCE_DIRECTORY
*find_first_entry( IMAGE_RESOURCE_DIRECTORY
*dir
,
76 void *root
, int want_dir
)
78 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
= (const IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(dir
+ 1);
81 for (pos
= 0; pos
< dir
->NumberOfNamedEntries
+ dir
->NumberOfIdEntries
; pos
++)
83 if (!entry
[pos
].DataIsDirectory
== !want_dir
)
84 return (IMAGE_RESOURCE_DIRECTORY
*)((char *)root
+ entry
[pos
].OffsetToDirectory
);
90 /**********************************************************************
93 * Find an entry by id in a resource directory
95 IMAGE_RESOURCE_DIRECTORY
*find_entry_by_id( IMAGE_RESOURCE_DIRECTORY
*dir
,
96 WORD id
, void *root
, int want_dir
)
98 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
;
101 entry
= (const IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(dir
+ 1);
102 min
= dir
->NumberOfNamedEntries
;
103 max
= min
+ dir
->NumberOfIdEntries
- 1;
106 pos
= (min
+ max
) / 2;
107 if (entry
[pos
].Id
== id
)
109 if (!entry
[pos
].DataIsDirectory
== !want_dir
)
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
);
117 if (entry
[pos
].Id
> id
) max
= pos
- 1;
120 DPRINT("root %p dir %p id %04x not found\n", root
, dir
, id
);
125 /**********************************************************************
128 * Find an entry by name in a resource directory
130 IMAGE_RESOURCE_DIRECTORY
*find_entry_by_name( IMAGE_RESOURCE_DIRECTORY
*dir
,
131 LPCWSTR name
, void *root
,
134 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
;
135 const IMAGE_RESOURCE_DIR_STRING_U
*str
;
136 int min
, max
, res
, pos
;
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
);
143 max
= dir
->NumberOfNamedEntries
- 1;
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
)
151 if (!entry
[pos
].DataIsDirectory
== !want_dir
)
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
);
159 if (res
< 0) max
= pos
- 1;
162 DPRINT("root %p dir %p name %ws not found\n", root
, dir
, name
);
167 NTSTATUS NTAPI
LdrpAccessResource( PVOID BaseAddress
, IMAGE_RESOURCE_DATA_ENTRY
*entry
,
168 void **ptr
, ULONG
*size
)
170 static NTSTATUS
LdrpAccessResource( PVOID BaseAddress
, IMAGE_RESOURCE_DATA_ENTRY
*entry
,
171 void **ptr
, ULONG
*size
)
174 NTSTATUS status
= STATUS_SUCCESS
;
180 if (!RtlImageDirectoryEntryToData( BaseAddress
, TRUE
, IMAGE_DIRECTORY_ENTRY_RESOURCE
, &dirsize
))
181 status
= STATUS_RESOURCE_DATA_NOT_FOUND
;
186 if (is_data_file_module(BaseAddress
))
188 PVOID mod
= (PVOID
)((ULONG_PTR
)BaseAddress
& ~1);
189 *ptr
= RtlImageRvaToVa( RtlImageNtHeader(mod
), mod
, entry
->OffsetToData
, NULL
);
191 else *ptr
= (char *)BaseAddress
+ entry
->OffsetToData
;
193 if (size
) *size
= entry
->Size
;
196 _SEH2_EXCEPT(page_fault(_SEH2_GetExceptionCode()))
198 status
= _SEH2_GetExceptionCode();
209 LdrFindResource_U(PVOID BaseAddress
,
210 PLDR_RESOURCE_INFO ResourceInfo
,
212 PIMAGE_RESOURCE_DATA_ENTRY
* ResourceDataEntry
)
215 NTSTATUS status
= STATUS_SUCCESS
;
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
);
227 status
= find_entry( BaseAddress
, ResourceInfo
, Level
, &res
, FALSE
);
228 if (NT_SUCCESS(status
))
229 *ResourceDataEntry
= res
;
231 _SEH2_EXCEPT(page_fault(_SEH2_GetExceptionCode()))
233 status
= _SEH2_GetExceptionCode();
244 LdrAccessResource(IN PVOID BaseAddress
,
245 IN PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry
,
246 OUT PVOID
* Resource OPTIONAL
,
247 OUT PULONG Size OPTIONAL
)
249 return LdrpAccessResource( BaseAddress
, ResourceDataEntry
, Resource
, Size
);
257 LdrFindResourceDirectory_U(IN PVOID BaseAddress
,
258 IN PLDR_RESOURCE_INFO info
,
260 OUT PIMAGE_RESOURCE_DIRECTORY
* addr
)
263 NTSTATUS status
= STATUS_SUCCESS
;
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
);
275 status
= find_entry( BaseAddress
, info
, level
, &res
, TRUE
);
276 if (NT_SUCCESS(status
))
279 _SEH2_EXCEPT(page_fault(_SEH2_GetExceptionCode()))
281 status
= _SEH2_GetExceptionCode();
288 #define NAME_FROM_RESOURCE_ENTRY(RootDirectory, Entry) \
289 ((Entry)->NameIsString ? (ULONG_PTR)(RootDirectory) + (Entry)->NameOffset : (Entry)->Id)
293 LdrpCompareResourceNames_U(
294 _In_ PUCHAR ResourceData
,
295 _In_ PIMAGE_RESOURCE_DIRECTORY_ENTRY Entry
,
296 _In_ ULONG_PTR CompareName
)
298 PIMAGE_RESOURCE_DIR_STRING_U ResourceString
;
299 PWSTR String1
, String2
;
300 USHORT ResourceStringLength
;
303 /* Check if the resource name is an ID */
304 if (CompareName
<= USHRT_MAX
)
306 /* Just compare the 2 IDs */
307 return (CompareName
- Entry
->Id
);
311 /* Get the resource string */
312 ResourceString
= (PIMAGE_RESOURCE_DIR_STRING_U
)(ResourceData
+
315 /* Get the string length */
316 ResourceStringLength
= ResourceString
->Length
;
318 String1
= ResourceString
->NameString
;
319 String2
= (PWSTR
)CompareName
;
321 /* Loop all characters of the resource string */
322 while (ResourceStringLength
--)
324 /* Get the next characters */
328 /* Check if they don't match, or if the compare string ends */
329 if ((Char1
!= Char2
) || (Char2
== 0))
331 /* They don't match, fail */
332 return Char2
- Char1
;
336 /* All characters match, check if the compare string ends here */
337 return (*String2
== 0) ? 0 : 1;
344 _In_ PVOID ImageBase
,
345 _In_ PLDR_RESOURCE_INFO ResourceInfo
,
347 _Inout_ ULONG
*ResourceCount
,
348 _Out_writes_to_(*ResourceCount
,*ResourceCount
) LDR_ENUM_RESOURCE_INFO
*Resources
)
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
;
361 /* If the caller wants data, get the maximum count of entries */
362 MaxResourceCount
= (Resources
!= NULL
) ? *ResourceCount
: 0;
367 /* Locate the resource directory */
368 ResourceData
= RtlImageDirectoryEntryToData(ImageBase
,
370 IMAGE_DIRECTORY_ENTRY_RESOURCE
,
372 if (ResourceData
== NULL
)
373 return STATUS_RESOURCE_DATA_NOT_FOUND
;
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);
379 /* Get the number of entries in the type directory */
380 NumberOfTypeEntries
= TypeDirectory
->NumberOfNamedEntries
+
381 TypeDirectory
->NumberOfIdEntries
;
383 /* Start with 0 resources and status success */
384 Status
= STATUS_SUCCESS
;
387 /* Loop all entries in the type directory */
388 for (i
= 0; i
< NumberOfTypeEntries
; ++i
, ++TypeEntry
)
390 /* Check if comparison of types is requested */
391 if (Level
> RESOURCE_TYPE_LEVEL
)
393 /* Compare the type with the requested Type */
394 Result
= LdrpCompareResourceNames_U(ResourceData
,
398 /* Not equal, continue with next entry */
399 if (Result
!= 0) continue;
402 /* The entry must point to the name directory */
403 if (!TypeEntry
->DataIsDirectory
)
405 return STATUS_INVALID_IMAGE_FORMAT
;
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);
413 /* Get the number of entries in the name directory */
414 NumberOfNameEntries
= NameDirectory
->NumberOfNamedEntries
+
415 NameDirectory
->NumberOfIdEntries
;
417 /* Loop all entries in the name directory */
418 for (j
= 0; j
< NumberOfNameEntries
; ++j
, ++NameEntry
)
420 /* Check if comparison of names is requested */
421 if (Level
> RESOURCE_NAME_LEVEL
)
423 /* Compare the name with the requested name */
424 Result
= LdrpCompareResourceNames_U(ResourceData
,
428 /* Not equal, continue with next entry */
429 if (Result
!= 0) continue;
432 /* The entry must point to the language directory */
433 if (!NameEntry
->DataIsDirectory
)
435 return STATUS_INVALID_IMAGE_FORMAT
;
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);
443 /* Get the number of entries in the language directory */
444 NumberOfLangEntries
= LangDirectory
->NumberOfNamedEntries
+
445 LangDirectory
->NumberOfIdEntries
;
447 /* Loop all entries in the language directory */
448 for (k
= 0; k
< NumberOfLangEntries
; ++k
, ++LangEntry
)
450 /* Check if comparison of languages is requested */
451 if (Level
> RESOURCE_LANGUAGE_LEVEL
)
453 /* Compare the language with the requested language */
454 Result
= LdrpCompareResourceNames_U(ResourceData
,
456 ResourceInfo
->Language
);
458 /* Not equal, continue with next entry */
459 if (Result
!= 0) continue;
462 /* This entry must point to data */
463 if (LangEntry
->DataIsDirectory
)
465 return STATUS_INVALID_IMAGE_FORMAT
;
468 /* Get a pointer to the data entry */
469 DataEntry
= (PIMAGE_RESOURCE_DATA_ENTRY
)(ResourceData
+
470 LangEntry
->OffsetToData
);
472 /* Check if there is still space to store the data */
473 if (Count
< MaxResourceCount
)
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
;
488 /* There is not enough space, save error status */
489 Status
= STATUS_INFO_LENGTH_MISMATCH
;
492 /* Count this resource */
498 /* Return the number of matching resources */
499 *ResourceCount
= Count
;