merces/libpe

Handling of Exports slightly off

Closed this issue · 4 comments

From the comments:
// We want to use NumberOfFunctions for looping as it's the total number of functions/symbols
// exported by the module. On the other hand, NumberOfNames is the number of
// functions/symbols exported by name only.

exports->functions_count = exp->NumberOfFunctions;
const size_t functions_size = exp->NumberOfFunctions * sizeof(pe_exported_function_t);
exports->functions = malloc(functions_size);
if (exports->functions == NULL) {
	exports->err = LIBPE_E_ALLOCATION_FAILURE;
	return exports;
}
memset(exports->functions, 0, functions_size);

for (uint32_t i=0; i < exp->NumberOfFunctions; i++) {

This is slightly wrong. NumberOfNames can differ from NumberOfFunctions, either smaller or larger. You really have three things to deal with:
NumberOfFunctions - All Symbols exported
NumberOfNames - All names that match up to symbols
Function exported by ordinal only

For this last one, you need to take all functions, and subtract out the ones that got names. The ones that are left are the ones exported by ordinal only.

Also, the 'ordinal' is actually a 'hint', which is the actual offset into the first array, meaning it is zero based. Any usage of ordinals is purely on the input side, meaning, if a user calls get pointer and uses an ordinal, the ordinal base is subtracted to give you the 0-index. The named things don't actually do this calculation, they just use the offset directly (counter to MS documentation).

Hi @Wiladams! Thanks for the report.

I'm not sure if I fully understand your point. You mean the comment is slightly off, or the implementation, or both?

NumberOfNames can differ from NumberOfFunctions, either smaller or larger. - you mean that NumberOfNames can be larger than NumberOfFunctions?

My understanding is that NumberOfFunctions is the total number of exported functions, therefore NumberOfNames should never be larger than NumberOfFunctions. Both will have the same value if all functions are exported by name.

You could have a number of names bigger than NumberOfFunctions, if the names are aliases to functions. I can have 'funcA' and 'funcB' both point to index '3', or whatever. This might happen as a .dll ages, and you want to maintain old names while introducing new ones, pointing to the same actual code.

You can definitely have a case where NumberOfNames is less than NumberOfFunctions. In this case, you might have some exports that are only available through ordinal only (no associated name). To find those, refer to my comment.

I would suggest looking through a bunch of .dll files on Windows in the \Windows\System32 directory. You'll find all manner of edge cases, and things that don't match your expectations from the documentation.

Interesting. Will take a look. Thanks for sharing!

Hi @Wiladams!
A year has passed since you opened the issue but hopefully this issue was fixed recently by aec926a.

I'm closing the issue, but feel free to re-open if you find that it wasn't fixed properly or at all.