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

initializes PLT GOT in Primus #993

Merged
merged 11 commits into from
Oct 18, 2019

Conversation

gitoleg
Copy link
Contributor

@gitoleg gitoleg commented Sep 12, 2019

In BAP 2.0 we have a more conservative disassembler. And while previously, only the first instruction of a PLT entry was disassembled, now we're getting the whole entry.

Unfortunately, the more precise disassembly hits analyses based on Primus. At least on x86/x86-64 targets. Previously, we got an unresolved call exception as soon as we enter a PLT entry, while now Primus follows to the whole chain of PLT resolution mechanism, like in the next example with call to free:

400560: pushq  0x200aa2(%rip)     # 601008 <_GLOBAL_OFFSET_TABLE_+0x8
400566: jmpq   *0x200aa4(%rip)    # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
40056c: nopl   0x0(%rax)<free@plt>:
400570: jmpq   *0x200aa2(%rip)        # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>
400576: pushq  $0x040057b: jmpq   400560 <_init+0x28>>

which does the following

400576: increase the stack
40057b: jump to the very first plt entry
400560: increase the stack
400566: load the target from GOT table

Finally, at 400566 we're getting an unresolved call exception. And in the Primus promiscuous mode, we continue the execution.
Here comes the crux of the problem, the stack is now broken: stack was increased twice and there wasn't any pop or ret instruction, so the following execution is wrong. The underlying problem is that the runtime function that performs lazy resolution of the
PLT GOT entry is not stubbed and therefore the arguments that are passed to it via the stack are not popped.

Probably the right solution would be to stub this function, but this is quite and involved process and it is not immediately obvious how to link it correctly (we do not know neither name nor an address).

Another option, which is proposed in this PR, is to stop Primus from going that deep into the PLT entry by linking all GOT entries, by default, as unresolved. The ratification is simple -- if we got to the PLT entry, then the address wasn't linked, so there is no need to
continue and we can raise an unresolved function asap.

In BAP 2.0 we have a more conservative disassembler. And while
previously, only the first instruction of a PLT entry was disassembled,
now we're getting the whole entry.

Unfortunately, the more precise disassembly hits analyses based on
Primus. At least on x86/x86-64 targets. Previously, we got an unresolved
call exception as soon as we enter a PLT entry, while now Primus follows
to the whole chain of PLT resolution mechanism, like in the next example
with call to free:

```
400560: pushq  0x200aa2(%rip)     # 601008 <_GLOBAL_OFFSET_TABLE_+0x8
400566: jmpq   *0x200aa4(%rip)    # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
40056c: nopl   0x0(%rax)<free@plt>:
400570: jmpq   *0x200aa2(%rip)        # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>
400576: pushq  $0x040057b: jmpq   400560 <_init+0x28>>

```

which does the following

```
400576: increase the stack
40057b: jump to the very first plt entry
400560: increase the stack
400566: load the target from GOT tablek
```

Finally, at `400566` we're getting an unresolved call exception.
And in the Primus promiscuous mode, we continue the execution.

Here comes the crux of the problem, the stack is now broken:

stack was increased twice and there wasn't any pop or ret

instruction, so the following execution is wrong. The underlying problem
is that the runtime function that performs lazy resolution of the
PLT GOT entry is not stubbed and therefore the arguments that are passed
to it via the stack are not popped.

Probably the right solution would be to stub this function, but this is
quite and involved process and it is not immediately obvious how to link
it correctly (we do not know neither name nor an address).

Another option, which is proposed in this PR, is to stop Primus from
going that deep into the PLT entry by linking all GOT entries, by
default, as unresolved. The ratification is simple -- if we got to the
PLT entry, then the address wasn't linked, so there is no need to
continue and we can raise an unresolved function asap.
@gitoleg gitoleg changed the title Init primus plt initializes PLT GOT in Primus Sep 12, 2019
for some reasons we don't get the whole got table with llvm 3.8
so we need to check is the memory is mapped before load
plugins/primus_x86/primus_x86_loader.ml Outdated Show resolved Hide resolved
plugins/primus_x86/primus_x86_loader.ml Outdated Show resolved Hide resolved
plugins/primus_x86/primus_x86_loader.ml Outdated Show resolved Hide resolved
plugins/primus_x86/primus_x86_loader.ml Outdated Show resolved Hide resolved
plugins/primus_x86/primus_x86_loader.ml Outdated Show resolved Hide resolved
@ivg ivg added changes-requested the PR was reviewed and needs reporter actions and removed ready for review labels Sep 19, 2019
@gitoleg gitoleg added ready for review and removed changes-requested the PR was reviewed and needs reporter actions labels Sep 27, 2019
Copy link
Member

@ivg ivg left a comment

Choose a reason for hiding this comment

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

  1. Use the memory iterators.
  2. Use the Seq module, when possible.

List.fold memory ~init:[]
~f:(fun acc mem ->
let range = address_range mem arch in
acc @ List.filter_map range
Copy link
Member

Choose a reason for hiding this comment

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

There are plenty of iterators in the Memory module, e.g., Memory.fold, Memory.iter, etc.

Copy link
Member

Choose a reason for hiding this comment

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

also, it is usually better to use the Seq module instead of List if you just need an iteration.

Memmap.filter (Project.memory proj) ~f:(is_section sec_name) |>
Memmap.to_sequence |>
Seq.map ~f:fst |>
Seq.to_list
Copy link
Member

Choose a reason for hiding this comment

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

there is no need to switch to the list from the sequence.

plugins/primus_x86/primus_x86_loader.ml Outdated Show resolved Hide resolved
@ivg ivg added changes-requested the PR was reviewed and needs reporter actions and removed ready for review labels Oct 10, 2019
@ivg ivg removed the changes-requested the PR was reviewed and needs reporter actions label Oct 18, 2019
@ivg ivg merged commit d768f30 into BinaryAnalysisPlatform:master Oct 18, 2019
gitoleg added a commit to gitoleg/bap that referenced this pull request Jan 22, 2020
The problem arises when we deal with libraries: even if a callee is
in the same library, a call will be done through a plt entry first, meaning that
we won't visit the target subroutine.

Our solution is based on the function name: we would better to call the
function itself, but not the corresponding plt entry, so since a
function and a plt entry has the same name, then any call to a plt entry
we substitute to a call to the function.

Also, this PR fixes SP in the `primus-x86-loader` (again).

The history.

PR BinaryAnalysisPlatform#993 introduced a proper initialization of PLT entries, but
unfortunately, introduced a bug as well.

Briefly, we didn't want primus to dive deep into PLT entries because
anyway, we would fail with unresolved call exception, but to that moment
a SP would be changed several times.
The solution was to raise an unresolved call exception as soon as
possible, right after a primus machine entered an entry and restore SP
by `unresolved-call` observation.
Unfortunately, the implementation was wrong: we linked an unresolved
call handler to the address of a jump in a plt entry, so actually we
just call a handler instead, and no actual unresolved call happens, no
observation made, and no steps to restore SP taken.

This PR fixes it and now we just unlink all the addresses of plt
entries, so we get a fair unresolved call exception.
@gitoleg gitoleg deleted the init-primus-plt branch May 13, 2020 21:09
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 this pull request may close these issues.

2 participants