Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PE file: Forwarded exports #307

Closed
neitsa opened this issue Jun 23, 2019 · 4 comments · Fixed by sthagen/lief-project-LIEF#16
Closed

PE file: Forwarded exports #307

neitsa opened this issue Jun 23, 2019 · 4 comments · Fixed by sthagen/lief-project-LIEF#16

Comments

@neitsa
Copy link

neitsa commented Jun 23, 2019

Is your feature request related to a problem? Please describe.

Unless I'm mistaken, as of LIEF 0.10.0-9dd9ded when parsing exported functions from a PE shared library, there's no (easy) way to know - when an export is forwarded - to which DLL the export function is forwarded to and what its name in the forwarded DLL.

[edit] This export forwarding mechanism is explained in the PE specification here and is called "Forwarder RVA".

As of now the address member is just 0.


Example with kernel32 from windows 10.

>>> binary = lief.parse(r"c:\windows\system32\kernel32.dll")
>>> exports = binary.get_export()
>>> entry = next(exports.entries)
>>> entry.name
'AcquireSRWLockExclusive'
>>> entry.address
0

The same goes for the exported_functions member.

>>> entry = binary.exported_functions[0]
>>> entry.name
'AcquireSRWLockExclusive'
>>> entry.address
0

Let's take the same example with AcquireSRWLockExclusive.

The function RVA is 0x92c6f:

image

Given the export table start and size:

>>> exp_table = binary.data_directory(lief.PE.DATA_DIRECTORY.EXPORT_TABLE)
>>> f"table: {exp_table.rva:#x} ; size: {exp_table.size:#x} (end: {exp_table.rva + exp_table.size:#x})"
'table: 0x8ec80 ; size: 0xdd40 (end: 0x9c9c0)'

We can see that the exported function RVA (0x92c6f) is within the bounds of the export table.

The RVA translated to an offset gives 0x9186f, at which we find the forwarder string (namely NTDLL.RtlAcquireSRWLockExclusive:

image

Describe the solution you'd like

  1. Simple solution: Give the function RVA, even if it's an external function (at least this gives the user a way to parse the forwarder string by themselves).
  2. Complete Solution A boolean in lief.Function indicating if the export is actually forwarded (external) or not (this flag exist in lief.PE.ExportEntry but it's tricky because exported_functions is a list of lief.Function and not of PE.ExportEntry). If the export is forwarded, the name of the DLL and the forwaded function name.

Describe alternatives you've considered

Parsing everything manually as demonstrated above. It's a bit tedious.

Additional context

I can give you a hand on implementing this if you want to talk about it 👍

@romainthomas
Copy link
Member

Thanks @neitsa, I was not aware of this feature on PE files.

I would consider the second solution in which we could extend the ExportEntry class with the following information:

struct forward_information_t {
  std::string library;
  std::string function;
};

class LIEF_API ExportEntry : public Object {
  ...
  bool is_forwarded() const;
  forward_information_t forward_information() const;
  ...
};

It can also be a good idea to export the function RVA.

@neitsa
Copy link
Author

neitsa commented Jun 24, 2019

Yes! It looks great Romain. I think your example + the function RVA should be enough.

Thanks a lot!

@romainthomas
Copy link
Member

Work in progress branch: https://github.com/lief-project/LIEF/tree/issue/307

romainthomas pushed a commit that referenced this issue Jul 13, 2019
@romainthomas
Copy link
Member

@neitsa you can check commit 30d6d1e
It enables this kind of script:

import lief

binary = lief.parse("./kernel32.dll")
exports = binary.get_export()

for e in filter(lambda e: e.is_forwarded, exports.entries):
    fwd = e.forward_information
    print(f"{e.name:<35} -> {fwd.library}.{fwd.function}")

b

romainthomas pushed a commit that referenced this issue Nov 28, 2019
romainthomas pushed a commit that referenced this issue Jan 17, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants