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 %ws name %ws lang %04lx level %lu\n",
222 BaseAddress
, (LPCWSTR
)ResourceInfo
->Type
,
223 Level
> 1 ? (LPCWSTR
)ResourceInfo
->Name
: L
"",
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 /* Fail if ResourceName2 is an ID */
312 if (Entry
->Id
<= USHRT_MAX
) return -1;
314 /* Get the resource string */
315 ResourceString
= (PIMAGE_RESOURCE_DIR_STRING_U
)(ResourceData
+
318 /* Get the string length */
319 ResourceStringLength
= ResourceString
->Length
;
321 String1
= ResourceString
->NameString
;
322 String2
= (PWSTR
)CompareName
;
324 /* Loop all characters of the resource string */
325 while (ResourceStringLength
--)
327 /* Get the next characters */
331 /* Check if they don't match, or if the compare string ends */
332 if ((Char1
!= Char2
) || (Char2
== 0))
334 /* They don't match, fail */
335 return Char2
- Char1
;
339 /* All characters match, check if the compare string ends here */
340 return (*String2
== 0) ? 0 : 1;
347 _In_ PVOID ImageBase
,
348 _In_ PLDR_RESOURCE_INFO ResourceInfo
,
350 _Inout_ ULONG
*ResourceCount
,
351 _Out_writes_to_(*ResourceCount
,*ResourceCount
) LDR_ENUM_RESOURCE_INFO
*Resources
)
356 ULONG NumberOfTypeEntries
, NumberOfNameEntries
, NumberOfLangEntries
;
357 ULONG Count
, MaxResourceCount
;
358 PIMAGE_RESOURCE_DIRECTORY TypeDirectory
, NameDirectory
, LangDirectory
;
359 PIMAGE_RESOURCE_DIRECTORY_ENTRY TypeEntry
, NameEntry
, LangEntry
;
360 PIMAGE_RESOURCE_DATA_ENTRY DataEntry
;
364 /* If the caller wants data, get the maximum count of entries */
365 MaxResourceCount
= (Resources
!= NULL
) ? *ResourceCount
: 0;
370 /* Locate the resource directory */
371 ResourceData
= RtlImageDirectoryEntryToData(ImageBase
,
373 IMAGE_DIRECTORY_ENTRY_RESOURCE
,
375 if (ResourceData
== NULL
)
376 return STATUS_RESOURCE_DATA_NOT_FOUND
;
378 /* The type directory is at the root, followed by the entries */
379 TypeDirectory
= (PIMAGE_RESOURCE_DIRECTORY
)ResourceData
;
380 TypeEntry
= (PIMAGE_RESOURCE_DIRECTORY_ENTRY
)(TypeDirectory
+ 1);
382 /* Get the number of entries in the type directory */
383 NumberOfTypeEntries
= TypeDirectory
->NumberOfNamedEntries
+
384 TypeDirectory
->NumberOfIdEntries
;
386 /* Start with 0 resources and status success */
387 Status
= STATUS_SUCCESS
;
390 /* Loop all entries in the type directory */
391 for (i
= 0; i
< NumberOfTypeEntries
; ++i
, ++TypeEntry
)
393 /* Check if comparison of types is requested */
394 if (Level
> RESOURCE_TYPE_LEVEL
)
396 /* Compare the type with the requested Type */
397 Result
= LdrpCompareResourceNames_U(ResourceData
,
401 /* Not equal, continue with next entry */
402 if (Result
!= 0) continue;
405 /* The entry must point to the name directory */
406 if (!TypeEntry
->DataIsDirectory
)
408 return STATUS_INVALID_IMAGE_FORMAT
;
411 /* Get a pointer to the name subdirectory and it's first entry */
412 NameDirectory
= (PIMAGE_RESOURCE_DIRECTORY
)(ResourceData
+
413 TypeEntry
->OffsetToDirectory
);
414 NameEntry
= (PIMAGE_RESOURCE_DIRECTORY_ENTRY
)(NameDirectory
+ 1);
416 /* Get the number of entries in the name directory */
417 NumberOfNameEntries
= NameDirectory
->NumberOfNamedEntries
+
418 NameDirectory
->NumberOfIdEntries
;
420 /* Loop all entries in the name directory */
421 for (j
= 0; j
< NumberOfNameEntries
; ++j
, ++NameEntry
)
423 /* Check if comparison of names is requested */
424 if (Level
> RESOURCE_NAME_LEVEL
)
426 /* Compare the name with the requested name */
427 Result
= LdrpCompareResourceNames_U(ResourceData
,
431 /* Not equal, continue with next entry */
432 if (Result
!= 0) continue;
435 /* The entry must point to the language directory */
436 if (!NameEntry
->DataIsDirectory
)
438 return STATUS_INVALID_IMAGE_FORMAT
;
441 /* Get a pointer to the language subdirectory and it's first entry */
442 LangDirectory
= (PIMAGE_RESOURCE_DIRECTORY
)(ResourceData
+
443 NameEntry
->OffsetToDirectory
);
444 LangEntry
= (PIMAGE_RESOURCE_DIRECTORY_ENTRY
)(LangDirectory
+ 1);
446 /* Get the number of entries in the language directory */
447 NumberOfLangEntries
= LangDirectory
->NumberOfNamedEntries
+
448 LangDirectory
->NumberOfIdEntries
;
450 /* Loop all entries in the language directory */
451 for (k
= 0; k
< NumberOfLangEntries
; ++k
, ++LangEntry
)
453 /* Check if comparison of languages is requested */
454 if (Level
> RESOURCE_LANGUAGE_LEVEL
)
456 /* Compare the language with the requested language */
457 Result
= LdrpCompareResourceNames_U(ResourceData
,
459 ResourceInfo
->Language
);
461 /* Not equal, continue with next entry */
462 if (Result
!= 0) continue;
465 /* This entry must point to data */
466 if (LangEntry
->DataIsDirectory
)
468 return STATUS_INVALID_IMAGE_FORMAT
;
471 /* Get a pointer to the data entry */
472 DataEntry
= (PIMAGE_RESOURCE_DATA_ENTRY
)(ResourceData
+
473 LangEntry
->OffsetToData
);
475 /* Check if there is still space to store the data */
476 if (Count
< MaxResourceCount
)
478 /* There is, fill the entry */
479 Resources
[Count
].Type
=
480 NAME_FROM_RESOURCE_ENTRY(ResourceData
, TypeEntry
);
481 Resources
[Count
].Name
=
482 NAME_FROM_RESOURCE_ENTRY(ResourceData
, NameEntry
);
483 Resources
[Count
].Language
=
484 NAME_FROM_RESOURCE_ENTRY(ResourceData
, LangEntry
);
485 Resources
[Count
].Data
= (PUCHAR
)ImageBase
+ DataEntry
->OffsetToData
;
486 Resources
[Count
].Reserved
= 0;
487 Resources
[Count
].Size
= DataEntry
->Size
;
491 /* There is not enough space, save error status */
492 Status
= STATUS_INFO_LENGTH_MISMATCH
;
495 /* Count this resource */
501 /* Return the number of matching resources */
502 *ResourceCount
= Count
;