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

[WIP] Implement PE & MDMP base relocations. DO NOT MERGE YET! #4711

Draft
wants to merge 13 commits into
base: dev
Choose a base branch
from

Conversation

Roeegg2
Copy link
Contributor

@Roeegg2 Roeegg2 commented Nov 11, 2024

Your checklist for this pull request

  • I've read the guidelines for contributing to this repository
  • I made sure to follow the project's coding style
  • I've documented or updated the documentation of every function and struct this PR changes. If not so I've explained why.
  • I've added tests that prove my fix is effective or that my feature works (if possible)
  • I've updated the rizin book with the relevant information (if needed)

Detailed description

Up until now PE relocations were implemented as imports, which is incorrect. The PE format uses base relocations for image files (ie executable files) and classic COFF relocations for object files.

Implemented so far:

  • PE base relocation parsing + assigning
  • PE base relocation tests

Still need to implement:

  • relocation patching - Rizin always loads the binary in it's expected base address, so no patching is needed (I added a detailed comment about this in relocs function in bin_pe.inc)
  • make MDMP use the new base relocation MDMP doesn't have relocations, only imports.
  • add COFF relocations for object files technically PE can have COFF relocations since it's based off of COFF, but COFF relocations are only used for for object files, and there is no reason to use PE for object files, so it's never actually used.

Notes:

  • There are no names printed with base relocations, since they don't have a symbol/name field associated with them. Simply a type and offset to apply the relocation in.
    ...

Test plan

All tests green, except the MDMP & and the ones dependent of relocation patching.

...

Closing issues

...

* Add missing `PE_IMAGE_FILE_MACHINE` types
* Add PE base relocation types

Everything is based off of Microsoft's PE format documentation [found here](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format)
@Roeegg2
Copy link
Contributor Author

Roeegg2 commented Nov 11, 2024

Some things I wasn't sure about:

  • The comment in has_canary seems to be from r2, and I don't see what leaks might be caused from this...
  • Should I add licensing to the new created files?
  • I deliberately didn't add COFF base relocations yet, since we could use the COFF format code for this. There is actually quite a handful of code we could use from COFF plugin instead of completely re implementing it for PE. It might be better to add that to a separate issue & PR since this seems like an issue on its own. This might take a lot of work though since it would require changing a lot of the internal structures used by PE plugin, as well as re implementing handling of many things in COFF to make it compatible with PE as well.

librz/bin/format/pe/pe_relocs.c Show resolved Hide resolved
librz/bin/format/pe/pe64_relocs.c Show resolved Hide resolved
librz/bin/format/pe/pe_relocs.c Outdated Show resolved Hide resolved
librz/bin/p/bin_pe.inc Outdated Show resolved Hide resolved
@@ -554,30 +638,16 @@ err:
}

static int has_canary(RzBinFile *bf) {
// XXX: We only need imports here but this causes leaks, we need to wait for the below. This is a horrible solution!
// TODO: use O(1) when imports sdbized
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this comment too

@XVilka XVilka changed the title Implement PE & MDMP base relocations. DO NOT MERGE YET! [WIP] Implement PE & MDMP base relocations. DO NOT MERGE YET! Nov 11, 2024
@XVilka
Copy link
Member

XVilka commented Nov 11, 2024

I deliberately didn't add COFF base relocations yet, since we could use the COFF format code for this. There is actually quite a handful of code we could use from COFF plugin instead of completely re implementing it for PE. It might be better to add that to a separate issue & PR since this seems like an issue on its own. This might take a lot of work though since it would require changing a lot of the internal structures used by PE plugin, as well as re implementing handling of many things in COFF to make it compatible with PE as well.

For now, I think some code duplication is fine. Sharing the code between two plugins is non-trivial at this point.

@Roeegg2 Roeegg2 force-pushed the pe-reloc branch 2 times, most recently from 64455b5 to 7cac944 Compare November 11, 2024 16:52
@wargio wargio marked this pull request as draft November 12, 2024 02:29
@@ -0,0 +1,74 @@
// SPDX-FileCopyrightText: 2024 Roee Toledano <roeetoledano10@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include "pe.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please leave 1 empty line between SPDX headers and first #include

@@ -0,0 +1,4 @@
// SPDX-FileCopyrightText: 2024 Roee Toledano <roeetoledano10@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#define RZ_BIN_PE64 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

librz/bin/format/pe/pe.h Show resolved Hide resolved
Copy link
Member

@wargio wargio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be tested via distro- branch before merge. @XVilka

Comment on lines 14 to 15
rz_buf_read(b, ent_buf, sizeof(ent_buf));
reloc = rz_read_ble16(ent_buf, big_endian);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe use the rz_buf_read_ble16_offset which is a special api which updates automatically the offset/address parameter and returns true on successful read.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also be sure to check the boolean return value to fail gracefully.

Comment on lines 42 to 45
rz_buf_read(b, block_buf, sizeof(block_buf));
RzBinPeRelocBlock block;
PE_READ_STRUCT_FIELD_L(block_buf, block, RzBinPeRelocBlock, page_rva, 32);
PE_READ_STRUCT_FIELD_L(block_buf, block, RzBinPeRelocBlock, block_size, 32);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can do the same here using rz_buf_read_bleXX_offset and checking the return value.
you can do this in a static function like this and fail when returns false.


do {
bytes_read += 2;
RzBinPeRelocEnt reloc;
Copy link
Member

@wargio wargio Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

always init this to 0.

Suggested change
RzBinPeRelocEnt reloc;
RzBinPeRelocEnt reloc = 0;

do {
ut8 block_buf[sizeof(RzBinPeRelocBlock)];
rz_buf_read(b, block_buf, sizeof(block_buf));
RzBinPeRelocBlock block;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
RzBinPeRelocBlock block;
RzBinPeRelocBlock block = { 0 };

Copy link
Member

@wargio wargio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please always initialize the variables to 0 or NULL so we can avoid random crashes and weird unintentional behavior.

* Remove old code of setting import entries to PE relocations
* Parse PE base relocations
* Implement converting from the specific RzBinPeRelocEnt type to the
  general RzBinReloc type
* Implement corrent type naming of PE relocations
* Removed a.exe and b.exe tests, as they don't contain base relocations
  and swapped them with good tests
* Corrected the olds tests

1. Need to add MDMP
2. Need to implement PE relocs patching to get the other tests to work
// encoding is 12 bits for type and 4 bits for offset.
// See: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#the-reloc-section-image-only
#define PE_RELOC_ENT_TYPE(val) ((val) >> 12)
#define PE_RELOC_ENT_OFFSET(val) ((val)&0b111111111111)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not sure how 0b111111111111 is portable. use hex (0xfff).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, totally forgot about that. I'll fix it

* Fix error caused when there are no relocations. Solved by adding a function checking whether relocation vector is empty or not
* Added freeing relocation vector and fixed correct setting of bin->relocs in
  case there has been an error
@Roeegg2
Copy link
Contributor Author

Roeegg2 commented Nov 16, 2024

Sorry for the ugly commit history. I could rebase interactive + squash with the old ones so it's easier to review.

@Roeegg2
Copy link
Contributor Author

Roeegg2 commented Nov 16, 2024

All tests are green except flagspace tests. Because there is no symbol associated with base relocations, they have no name associated with them, so I'm not sure how we can use flags here

librz/bin/p/bin_pe.inc Outdated Show resolved Hide resolved
librz/bin/p/bin_pe.inc Outdated Show resolved Hide resolved
rz_vector_foreach (bin->relocs, pe_reloc) {
RzBinReloc *general_reloc = RZ_NEW0(RzBinReloc);
if (!general_reloc) {
RZ_LOG_WARN("Failed to convert PE reloc to general reloc\n");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
RZ_LOG_WARN("Failed to convert PE reloc to general reloc\n");
RZ_LOG_ERROR("Failed to allocate PE RzBinReloc\n");

if (!general_reloc) {
RZ_LOG_WARN("Failed to convert PE reloc to general reloc\n");
rz_bin_reloc_free(general_reloc);
continue;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if alloc fails, then there is no memory left.
You should probably do the following instead of calling continue:

rz_vector_free(bin->relocs);
return ret;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants