mirror of
https://github.com/QuasarApp/LIEF.git
synced 2025-05-13 19:59:33 +00:00
It enables to build documentation out-of-tree. It also introduces the following build targets: - lief-doc - lief-doxygen - lief-sphinx - lief-quick-sphinx See doc/CMakeLists.txt for the details
187 lines
4.9 KiB
ReStructuredText
187 lines
4.9 KiB
ReStructuredText
05 - Infecting the plt/got
|
|
--------------------------
|
|
|
|
The objective of this tutorial is to hook an imported function in an ELF binary.
|
|
|
|
Scripts and materials are available here: `materials <https://github.com/lief-project/tutorials/tree/master/05_ELF_infect_plt-got>`_
|
|
|
|
|
|
By Romain Thomas - `@rh0main <https://twitter.com/rh0main>`_
|
|
|
|
-----
|
|
|
|
Hooking imported functions by infecting the ``.got`` section is a well-known technique [#f1]_ [#f2]_ and this tutorial will be focused
|
|
on its implementation using LIEF.
|
|
|
|
These figures illustrate the ``plt/got`` mechanism:
|
|
|
|
.. figure:: ../_static/tutorial/05/pltgot.png
|
|
:scale: 40 %
|
|
:align: center
|
|
|
|
With lazy binding, the first time that the function is called the ``got`` entry redirects to the plt instruction.
|
|
|
|
|
|
.. figure:: ../_static/tutorial/05/pltgot3.png
|
|
:scale: 40 %
|
|
:align: center
|
|
|
|
The Second time, ``got`` entry holds the address in the shared library.
|
|
|
|
|
|
|
|
Basically the infection is done in two steps:
|
|
|
|
* Firstly, we inject our hook
|
|
* Secondly, we redirect the targeted function to our hook by patching the ``got``
|
|
|
|
It can be summed up by the following figure:
|
|
|
|
.. figure:: ../_static/tutorial/05/pltgot2.png
|
|
:scale: 50 %
|
|
:align: center
|
|
|
|
|
|
As example, we will use a basic *crackme* which performs a :manpage:`memcmp(3)` on the flag and user's input.
|
|
|
|
.. code-block:: cpp
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
// Damn_YoU_Got_The_Flag
|
|
char password[] = "\x18\x3d\x31\x32\x03\x05\x33\x09\x03\x1b\x33\x28\x03\x08\x34\x39\x03\x1a\x30\x3d\x3b";
|
|
|
|
inline int check(char* input);
|
|
|
|
int check(char* input) {
|
|
for (int i = 0; i < sizeof(password) - 1; ++i) {
|
|
password[i] ^= 0x5c;
|
|
}
|
|
return memcmp(password, input, sizeof(password) - 1);
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
if (argc != 2) {
|
|
printf("Usage: %s <password>\n", argv[0]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (strlen(argv[1]) == (sizeof(password) - 1) && check(argv[1]) == 0) {
|
|
puts("You got it !!");
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
puts("Wrong");
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
The flag is *xored* with ``0x5C``. To validate the *crackme*, the user has to enter ``Damn_YoU_Got_The_Flag``:
|
|
|
|
.. code-block:: console
|
|
|
|
$ crackme.bin foo
|
|
Wrong
|
|
$ crackme.bin Damn_YoU_Got_The_Flag
|
|
You got it !!
|
|
|
|
The hook will consist in printing arguments of ``memcmp`` and returning ``0``:
|
|
|
|
.. code-block:: cpp
|
|
|
|
#include "arch/x86_64/syscall.c"
|
|
#define stdout 1
|
|
|
|
int my_memcmp(const void* lhs, const void* rhs, int n) {
|
|
const char msg[] = "Hook memcmp\n";
|
|
_write(stdout, msg, sizeof(msg));
|
|
_write(stdout, (const char*)lhs, n);
|
|
_write(stdout, "\n", 2);
|
|
_write(stdout, (const char*)rhs, n);
|
|
_write(stdout, "\n", 2);
|
|
return 0;
|
|
}
|
|
|
|
As the hook is going to be injected into the crackme, it must have the following requirements:
|
|
|
|
* Assembly code must be *position independant* (compiled with ``-fPIC`` or ``-pie/-fPIE`` flags)
|
|
* Don't use external libraries such as ``libc.so`` (``-nostdlib -nodefaultlibs`` flags)
|
|
|
|
|
|
Due to the requirements, the hook is compiled with: :code:`gcc -nostdlib -nodefaultlibs -fPIC -Wl,-shared hook.c -o hook`.
|
|
|
|
Injecting the hook
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
The first step is to inject the hook into the binary. To do so we will add a :class:`~lief.ELF.Segment`:
|
|
|
|
.. code-block:: python
|
|
|
|
import lief
|
|
|
|
crackme = lief.parse("crackme.bin")
|
|
hook = lief.parse("hook")
|
|
|
|
segment_added = crackme.add(hook.segments[0])
|
|
|
|
All assembly code of the hook stands in the first :attr:`~lief.ELF.SEGMENT_TYPES.LOAD` segment of ``hook``.
|
|
|
|
Once the hook added, its virtual address is :attr:`~lief.ELF.Segment.virtual_address` of ``segment_added`` and we can processed to the ``got`` patching.
|
|
|
|
Patching the ``got``
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
LIEF provides a function to easily patch the ``got`` entry associated with a :class:`~lief.ELF.Symbol`:
|
|
|
|
|
|
.. automethod:: lief.ELF.Binary.patch_pltgot
|
|
:noindex:
|
|
|
|
The offset of the ``memcmp`` function is stored in the :attr:`~lief.ELF.Symbol.value` attribute of the associated dynamic symbol. Thus its virtual address will be:
|
|
|
|
* ``my_memcpy``: :attr:`~lief.ELF.Symbol.value` + ``segment_added.virtual_address``
|
|
|
|
.. code-block:: python
|
|
|
|
my_memcmp = hook.get_symbol("my_memcmp")
|
|
my_memcmp_addr = segment_added.virtual_address + my_memcmp.value
|
|
|
|
Finally we can patch the ``memcmp`` from the crakme with this value:
|
|
|
|
.. code-block:: python
|
|
|
|
crackme.patch_pltgot('memcmp', my_memcmp_addr)
|
|
|
|
And rebuild it:
|
|
|
|
.. code-block:: python
|
|
|
|
crackme.write("crackme.hooked")
|
|
|
|
|
|
Run
|
|
~~~
|
|
|
|
As a check on the input size is performed before checking the flag value, we have to provide an input with the correct length (no matter its content):
|
|
|
|
.. code-block:: console
|
|
|
|
$ crackme.hooked XXXXXXXXXXXXXXXXXXXXX
|
|
Hook add
|
|
Damn_YoU_Got_The_Flag
|
|
XXXXXXXXXXXXXXXXXXXXX
|
|
You got it !!
|
|
|
|
|
|
.. rubric:: References
|
|
|
|
.. role:: strike
|
|
:class: strike
|
|
|
|
.. [#f1] :strike:`hxxp://vxheaven.org/lib/vrn00.html`
|
|
.. [#f2] http://phrack.org/issues/56/7.html
|
|
|
|
|