Skip to content

Commit

Permalink
initializes PLT GOT in Primus (#993)
Browse files Browse the repository at this point in the history
* initializes PLT GOT in Primus

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.

* removed unused code

* checks memory is mapped before load

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

* loads addresses directly from ".got.plt" section

* use Memory.get instead of Interpreter

* refactoring

* removed unused code
  • Loading branch information
gitoleg authored and ivg committed Oct 18, 2019
1 parent fe44212 commit d768f30
Showing 1 changed file with 58 additions and 7 deletions.
65 changes: 58 additions & 7 deletions plugins/primus_x86/primus_x86_loader.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,59 @@ open Bap.Std
open Bap_primus.Std
open X86_cpu

let is_section name v =
match Value.get Image.section v with
| Some x -> String.(x = name)
| _ -> false

module Make_unresolved(Machine : Primus.Machine.S) = struct
module Linker = Primus.Linker.Make(Machine)

let exec =
Linker.exec (`symbol Primus.Linker.unresolved_handler)
end

module Plt_jumps(Machine : Primus.Machine.S) = struct
module Linker = Primus.Linker.Make(Machine)
open Machine.Syntax

let section_memory sec_name =
Machine.get () >>| fun proj ->
Memmap.filter (Project.memory proj) ~f:(is_section sec_name) |>
Memmap.to_sequence |>
Seq.map ~f:fst

let load_table =
Machine.arch >>= fun arch ->
section_memory ".got.plt" >>| fun memory ->
Seq.fold memory ~init:[]
~f:(fun acc mem ->
let step = (Arch.addr_size arch :> size) in
Memory.fold mem ~word_size:step ~init:[] ~f:(fun a w -> a :: w))

let filter_plt addrs =
section_memory ".plt" >>= fun memory ->
Machine.return @@
List.filter addrs ~f:(fun a ->
Seq.exists memory ~f:(fun mem -> Memory.contains mem a))

let unlink addrs =
Machine.List.iter addrs ~f:(fun addr ->
Linker.link ~addr (module Make_unresolved))

let unresolve =
load_table >>=
filter_plt >>=
unlink

end

module Component(Machine : Primus.Machine.S) = struct
open Machine.Syntax
module Env = Primus.Env.Make(Machine)
module Value = Primus.Value.Make(Machine)
module Interpreter = Primus.Interpreter.Make(Machine)
module Plt_jumps = Plt_jumps(Machine)

let zero = Primus.Generator.static 0

Expand All @@ -23,18 +71,21 @@ module Component(Machine : Primus.Machine.S) = struct
match Var.typ sp with
| Mem _ -> Machine.return ()
| Imm width ->
Value.of_int ~width addend >>= fun addend ->
Primus.Linker.Trace.lisp_call_return >>> correct_sp sp addend

let seq = Machine.sequence
Value.of_int ~width addend >>= fun addend ->
Primus.Linker.Trace.lisp_call_return >>> correct_sp sp addend

let init () =
Machine.get () >>= fun proj ->
Machine.sequence @@
match Project.arch proj with
| `x86 ->
seq [initialize_flags IA32.flags; correct_sp IA32.sp 4]
[initialize_flags IA32.flags;
correct_sp IA32.sp 4;
Plt_jumps.unresolve ]
| `x86_64 ->
seq [initialize_flags AMD64.flags; correct_sp AMD64.sp 8]
| _ -> Machine.return ()
[initialize_flags AMD64.flags;
correct_sp AMD64.sp 8;
Plt_jumps.unresolve ]
| _ -> []

end

0 comments on commit d768f30

Please sign in to comment.