-
Notifications
You must be signed in to change notification settings - Fork 274
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
Conversation
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.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Use the memory iterators.
- 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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
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.
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:
which does the following
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
orret
instruction, so the following execution is wrong. The underlying problem is that the runtime function that performs lazy resolution of thePLT 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.