Skip to content
Permalink
Browse files
Enable PE hooking
  * Add 'hook_function' to hook a PE imported function
  * Add tutorial on PE hooking (resolve #5)
  * Add 'PE::get_import' and 'PE::has_import' to retrieve import
  • Loading branch information
romainthomas committed May 1, 2017
1 parent 9508d22 commit 24f6b72
Show file tree
Hide file tree
Showing 22 changed files with 558 additions and 13 deletions.
@@ -67,7 +67,6 @@ install:
- ps: if ($env:PLATFORM -eq "x86") { $env:PYTHON_INCLUDE = $env:PYTHON32_INCLUDE }
- ps: if ($env:PLATFORM -eq "x86") { $env:PYTHON_BINARY = $env:PYTHON32_BINARY }
- ps: if ($env:PLATFORM -eq "x86") { $env:PYTHON_LIBRARY = $env:PYTHON32_LIBRARY }

- set PATH=%PYTHON_PATH%;%PATH%

build_script:
@@ -76,13 +75,14 @@ build_script:
- cmake --build . --config Release --target ALL_BUILD -- /v:m /logger:%MSBuildLogger%

test_script:
- cmake --build . --config Release --target RUN_TESTS -- /v:m /logger:%MSBuildLogger%
- cmake --build . --config Release --target RUN_TESTS -- /v:m /logger:%MSBuildLogger%

after_build:
- cmake --build . --config Release --target package -- /v:m /logger:%MSBuildLogger%
- cd api\python
- python.exe .\setup.py sdist --formats=zip && exit 0 # Ignore warnings...
- ps: gci dist\*.zip | % { rename-item –path $_.Fullname –Newname ( "windows_" + $env:PLATFORM + "_" + $_.basename + "_py" + $env:PYTHON_VERSION + $_.extension) }
- cd ..\..

artifacts:
- path: '*.zip'
@@ -148,7 +148,7 @@ void init_PE_Binary_class(py::module& m) {
.def("add_section",
&Binary::add_section,
"Add a " RST_CLASS_REF(lief.PE.Section) " to the binary.",
"section"_a, py::arg("type"),
"section"_a, py::arg("type") = SECTION_TYPES::UNKNOWN,
py::return_value_policy::reference)

//.def("delete_section", (void (Binary::*)(const std::string&)) &Binary::delete_section)
@@ -184,6 +184,17 @@ void init_PE_Binary_class(py::module& m) {
"Return an iterator to the " RST_CLASS_REF(lief.PE.Import) " libraries",
py::return_value_policy::reference)

.def("has_import",
&Binary::has_import,
"``True`` if the binary import the given library name",
"import_name"_a)

.def("get_import",
static_cast<no_const_func<Import&, const std::string&>>(&Binary::get_import),
"Returns the " RST_CLASS_REF(lief.PE.Import) " from the given name",
"import_name"_a,
py::return_value_policy::reference)

.def_property_readonly("resources_manager",
static_cast<no_const_getter<ResourcesManager>>(&Binary::get_resources_manager),
"Return the " RST_CLASS_REF(lief.PE.ResourcesManager) " to manage resources")
@@ -210,6 +221,21 @@ void init_PE_Binary_class(py::module& m) {
"Remove the " RST_CLASS_REF(lief.PE.Import) " from the given name",
"import_name"_a)

.def("hook_function",
static_cast<void (Binary::*)(const std::string&, uint64_t)>(&Binary::hook_function),
"Hook the given function name\n\n"
".. note:: \n\n"
"\tWhen using this function, the :class:`~lief.PE.Builder` should be configured as follow:\n\n"
"\t.. code-block:: python\n\n"
"\t\t\n\n"
"\t\tbuilder.build_imports(True).patch_imports(True)\n\n",
"function_name"_a, "hook_address"_a)

.def("hook_function",
static_cast<void (Binary::*)(const std::string&, const std::string&, uint64_t)>(&Binary::hook_function),
"Hook the function name from the given library name",
"library_name"_a, "function_name"_a, "hook_address"_a)

.def("remove_all_libraries",
&Binary::remove_all_libraries,
"Remove all libraries imported")
@@ -28,6 +28,9 @@ void init_PE_module(py::module& m) {
"Convert an OID to a human-readable string");


// Enums
init_PE_Structures_enum(LIEF_PE_module);

// Objects
init_PE_Parser_class(LIEF_PE_module);
init_PE_Binary_class(LIEF_PE_module);
@@ -54,6 +57,4 @@ void init_PE_module(py::module& m) {
init_PE_SignerInfo_class(LIEF_PE_module);
init_PE_AuthenticatedAttributes_class(LIEF_PE_module);

// Enums
init_PE_Structures_enum(LIEF_PE_module);
}
@@ -104,7 +104,7 @@ void init_PE_Structures_enum(py::module& m) {
.value(PY_ENUM(LIEF::PE::DATA_DIRECTORY::CLR_RUNTIME_HEADER))
.export_values();

py::enum_<LIEF::PE::DLL_CHARACTERISTICS>(m, "DLL_CHARACTERISTICS")
py::enum_<LIEF::PE::DLL_CHARACTERISTICS>(m, "DLL_CHARACTERISTICS", py::arithmetic())
.value(PY_ENUM(LIEF::PE::DLL_CHARACTERISTICS::IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA))
.value(PY_ENUM(LIEF::PE::DLL_CHARACTERISTICS::IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE))
.value(PY_ENUM(LIEF::PE::DLL_CHARACTERISTICS::IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY))
@@ -119,7 +119,7 @@ void init_PE_Structures_enum(py::module& m) {
.export_values();


py::enum_<LIEF::PE::SECTION_CHARACTERISTICS>(m, "SECTION_CHARACTERISTICS")
py::enum_<LIEF::PE::SECTION_CHARACTERISTICS>(m, "SECTION_CHARACTERISTICS", py::arithmetic())
.value(PY_ENUM(LIEF::PE::SECTION_CHARACTERISTICS::IMAGE_SCN_TYPE_NO_PAD))
.value(PY_ENUM(LIEF::PE::SECTION_CHARACTERISTICS::IMAGE_SCN_CNT_CODE))
.value(PY_ENUM(LIEF::PE::SECTION_CHARACTERISTICS::IMAGE_SCN_CNT_INITIALIZED_DATA))
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -1,3 +1,5 @@
.. _02-pe-from-scratch:

02 - Create a PE from scratch
-----------------------------

@@ -1,5 +1,5 @@
04 - Hooking
------------
04 - ELF Hooking
----------------

The objective of this tutorial is to hook a library function

@@ -0,0 +1,171 @@
06 - PE Hooking
---------------

The objective of this tutorial is show how we can hook imported functions

Scripts and materials are available here: `materials <https://github.com/lief-project/tutorials/tree/master/06_PE_hooking>`_

------

The targeted binary is a simple ``PE64`` *HelloWorld* which prints the first argument on in the console:

.. code-block:: cpp
#include "stdafx.h"
#include <stdio.h>
int main(int argc, char** argv) {
printf("Hello: %s\n", argv[1]);
return 0;
}
.. code-block:: console
$ PE64_x86-64_binary_HelloWorld.exe World
$ Hello: World
Using LIEF, we will replace the function that prints the message in the console with a ``MessageBox``

By disassembling the binary we can see that the *print* occurs in the function ``sub_140001030`` and it uses two
external functions: ``__acrt_iob_func`` and ``__stdio_common_vfprintf``.


.. figure:: ../_static/tutorial/06/06_hooking_1.png
:scale: 80 %
:align: center


.. figure:: ../_static/tutorial/06/06_hooking_2.png
:scale: 80 %
:align: center

Due to the Microsoft x64 calling convention, the format is located in the ``rcx`` and the input message in the ``rdx`` register.

Basically the :ref:`hooking-code` replaces the ``__acrt_iob_func`` function and shows a ``MessageBox`` with the ``rdx`` message.

.. code-block:: nasm
:caption: hooking code
:name: hooking-code
add rsp, 0x48 ; Stack unwind
xor rcx, rcx ; hWnd
mov rdx, rdx ; Message
mov r8, 0x0140009000 ; Title
xor r9, r9 ; MB_OK
mov rax, 0x014000A3E4 ; MessageBoxA address
call [rax] ; MessageBoxA(hWnd, Message, Title, MB_OK)
xor rcx, rcx ; exit value
mov rax, 0x014000A3d4 ; ExitProcess address
call [rax] ; ExitProcess(0)
ret ; Never reached
.. note::

As for tutorial :ref:`02-pe-from-scratch`, the address of ``MessageBoxA`` and ``ExitProcess`` can be found
with the function:

.. automethod:: lief.PE.Binary.predict_function_rva
:noindex:





First we create the ``.htext`` section which will hold the hooking code:

.. code-block:: python
section_text = lief.PE.Section(".htext")
section_text.content = code
section_text.virtual_address = 0x7000
section_text.characteristics = lief.PE.SECTION_CHARACTERISTICS.CNT_CODE | lief.PE.SECTION_CHARACTERISTICS.MEM_READ | lief.PE.SECTION_CHARACTERISTICS.MEM_EXECUTE
section_text = pe.add_section(section_text)
Then the ``.hdata`` section for the ``MessageBox`` title:

.. code-block:: python
title = "LIEF is awesome\0"
data = list(map(ord, title))
section_data = lief.PE.Section(".hdata")
section_data.content = data
section_data.virtual_address = 0x8000
section_data.characteristics = lief.PE.SECTION_CHARACTERISTICS.CNT_INITIALIZED_DATA | lief.PE.SECTION_CHARACTERISTICS.MEM_READ
section_data = pe.add_section(section_data)
As the ASLR is enabled we will disable it to avoid to deal with relocations:

.. code-block:: python
binary.optional_header.dll_characteristics &= ~lief.PE.DLL_CHARACTERISTICS.DYNAMIC_BASE
We will also disable the ``NX`` protection:


.. code-block:: python
binary.optional_header.dll_characteristics &= ~lief.PE.DLL_CHARACTERISTICS.NX_COMPAT
As ``ExitProcess`` is not imported in ``KERNEL32.dll`` we need to add it:

.. code-block:: python
kernel32 = binary.get_import("KERNEL32.dll")
kernel32.add_entry("ExitProcess")
The ``MessageBoxA`` function is located in the ``user32.dll`` thus we have to add it:


.. code-block:: python
user32 = binary.add_library("user32.dll")
user32.add_entry("MessageBoxA")
Then we proceed to the hook of the ``__acrt_iob_func`` function:

.. code-block:: python
pe.hook_function("__acrt_iob_func", binary.optional_header.imagebase + section_text.virtual_address)
And finally we configure the :class:`~lief.PE.Builder` to create a new import table and to patch the original one with trampolines.

.. code-block:: python
builder = lief.PE.Builder(binary)
builder.build_imports(True).patch_imports(True)
builder.build()
builder.write("lief_pe_hooking.exe")
Now we can run the final executable:

.. code-block:: console
$ lief_pe_hooking.exe "Hooking World"
.. figure:: ../_static/tutorial/06/06_hooking_3.png
:scale: 80 %
:align: center
















@@ -9,6 +9,7 @@ Tutorials
03_elf_change_symbols.rst
04_elf_hooking.rst
05_elf_infect_plt_got.rst
06_pe_hooking.rst



@@ -14,7 +14,7 @@
# >>> nm("C:\\Windows\\explorer.exe")

import sys
from lief import *
from lief import parse

def nm(filename):
""" Return symbols from *filename* binary """
@@ -16,6 +16,8 @@
#ifndef LIEF_PE_BINARY_H_
#define LIEF_PE_BINARY_H_

#include <map>

#include "LIEF/PE/Structures.hpp"
#include "LIEF/PE/Header.hpp"
#include "LIEF/PE/OptionalHeader.hpp"
@@ -232,6 +234,17 @@ class DLL_PUBLIC Binary : public LIEF::Binary {
it_imports imports(void);
it_const_imports imports(void) const;

//! @brief Returns the PE::Import from the given name
//!
//! @param[in] import_name Name of the import
Import& get_import(const std::string& import_name);
const Import& get_import(const std::string& import_name) const;

//! @brief ``True`` if the binary import the given library name
//!
//! @param[in] import_name Name of the import
bool has_import(const std::string& import_name) const;

//! @brief Add the function @p function of the library @p library
//!
//! @param[in] library library name of the function
@@ -247,6 +260,26 @@ class DLL_PUBLIC Binary : public LIEF::Binary {
//! @brief Remove all libraries in the binary
void remove_all_libraries(void);

//! @brief Hook an imported function
//!
//! When using this function, LIEF::PE::Builder::build_imports and LIEF::PE::Builder::patch_imports
//! should be set to ``true``
//!
//! @param[in] function Function name to hook
//! @param[in] address Address of the hook
void hook_function(const std::string& function, uint64_t address);


//! @brief Hook an imported function
//!
//! When using this function, LIEF::PE::Builder::build_imports(true) and LIEF::PE::Builder::patch_imports
//! should be set to ``true``
//!
//! @param[in] library Library name in which the function is located
//! @param[in] function Function name to hook
//! @param[in] address Address of the hook
void hook_function(const std::string& library, const std::string& function, uint64_t address);

//! @brief Reconstruct the binary object and write it in `filename`
//!
//! Rebuild a PE binary from the current Binary object.
@@ -330,6 +363,7 @@ class DLL_PUBLIC Binary : public LIEF::Binary {
Debug debug_;
std::vector<uint8_t> overlay_;

std::map<std::string, std::map<std::string, uint64_t>> hooks_;
};

}
@@ -49,6 +49,9 @@ class DLL_PUBLIC Builder
template<typename PE_T>
static std::vector<uint8_t> build_jmp(uint64_t address);

template<typename PE_T>
static std::vector<uint8_t> build_jmp_hook(uint64_t address);

Builder& build_imports(bool flag = true);
Builder& patch_imports(bool flag = true);
Builder& build_relocations(bool flag = true);

0 comments on commit 24f6b72

Please sign in to comment.