Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add import hash function as well as ordinal resolver functions (PE part)
For PE binary it's now possible get the *imphash* value through the
lief.PE.get_imphash function. It's also possible to resolve ordinals
imports by using the lief.PE.resolve_ordinals

Resolve #54
  • Loading branch information
romainthomas committed Jul 16, 2017
1 parent 893f9fb commit a89bc6d
Show file tree
Hide file tree
Showing 34 changed files with 16,586 additions and 18 deletions.
2 changes: 2 additions & 0 deletions api/python/PE/CMakeLists.txt
@@ -1,4 +1,6 @@
set(LIEF_PYTHON_PE_SRC
"${CMAKE_CURRENT_LIST_DIR}/pyUtils.cpp"

"${CMAKE_CURRENT_LIST_DIR}/objects/pyResourceNode.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyResourceData.cpp"
"${CMAKE_CURRENT_LIST_DIR}/objects/pyResourceDirectory.cpp"
Expand Down
9 changes: 9 additions & 0 deletions api/python/PE/objects/pyImportEntry.cpp
Expand Up @@ -31,6 +31,15 @@ using setter_t = void (ImportEntry::*)(T);
void init_PE_ImportEntry_class(py::module& m) {
py::class_<ImportEntry>(m, "ImportEntry")
.def(py::init<>())

.def(py::init<const std::string&>(),
"Constructor by :attr:`~lief.PE.ImportEntry.name`",
"import_name"_a)

.def(py::init<uint64_t, const std::string&>(),
"Constructor by :attr:`~lief.PE.ImportEntry.data` and optionally :attr:`~lief.PE.ImportEntry.name`",
"data"_a, "name"_a = "")

.def_property("name",
[] (const ImportEntry& obj) {
return safe_string_converter(obj.name());
Expand Down
3 changes: 3 additions & 0 deletions api/python/PE/pyPE.cpp
Expand Up @@ -31,6 +31,9 @@ void init_PE_module(py::module& m) {
// Enums
init_PE_Structures_enum(LIEF_PE_module);

// utils
init_PE_utils(LIEF_PE_module);

// Objects
init_PE_Parser_class(LIEF_PE_module);
init_PE_Binary_class(LIEF_PE_module);
Expand Down
3 changes: 3 additions & 0 deletions api/python/PE/pyPE.hpp
Expand Up @@ -26,6 +26,9 @@

using namespace LIEF::PE;

// utils
void init_PE_utils(py::module&);

void init_PE_Parser_class(py::module&);
void init_PE_Binary_class(py::module&);
void init_PE_DataDirectory_class(py::module&);
Expand Down
65 changes: 65 additions & 0 deletions api/python/PE/pyUtils.cpp
@@ -0,0 +1,65 @@
/* Copyright 2017 R. Thomas
* Copyright 2017 Quarkslab
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "pyPE.hpp"

#include "LIEF/PE/utils.hpp"

void init_PE_utils(py::module& m) {


m.def("is_pe",
static_cast<bool (*)(const std::string&)>(&is_pe),
"Check if the given file is a ``PE``",
"file"_a);

m.def("is_pe",
static_cast<bool (*)(const std::vector<uint8_t>&)>(&is_pe),
"Check if the given raw data is a ``PE``",
"raw"_a);

m.def("get_type",
static_cast<PE_TYPE (*)(const std::string&)>(&get_type),
"If the input file is a ``PE`` one, return the " RST_CLASS_REF(lief.PE.PE_TYPE) "",
"file"_a);


m.def("get_type",
static_cast<PE_TYPE (*)(const std::vector<uint8_t>&)>(&get_type),
"If the input *raw* data represent a ``PE`` file, return the " RST_CLASS_REF(lief.PE.PE_TYPE) "",
"raw"_a);

m.def("get_imphash",
&get_imphash,
"Compute the hash of imported functions\n\n"

"Properties of the hash generated:\n"
"\t* Order agnostic\n"
"\t* Casse agnostic\n"
"\t* Ordinal (**in some extent**) agnostic\n\n"

".. seealso::\n\n"
"\thttps://www.fireeye.com/blog/threat-research/2014/01/tracking-malware-import-hashing.html\n",
"binary"_a);

m.def("resolve_ordinals",
&resolve_ordinals,
"Take an " RST_CLASS_REF(lief.PE.Import) " as entry and try to resolve its ordinal imports\n\n"

"The ``strict`` boolean parameter enables to throw a " RST_CLASS_REF(lief.not_found) " exception "
"if the ordinal can't be resolved. Otherwise it skips the entry.",
"import"_a, "strict"_a = false,
py::return_value_policy::copy);
}
4 changes: 3 additions & 1 deletion api/python/pyUtils.cpp
Expand Up @@ -34,7 +34,9 @@ void init_utils_functions(py::module& m) {
#endif

#if defined(LIEF_MACHO_MODULE)
m.def("is_macho", &LIEF::MachO::is_macho, "Check if the given binary is ``MachO``");
m.def("is_macho",
&LIEF::MachO::is_macho,
"Check if the given binary is ``MachO``");
#endif

}
32 changes: 32 additions & 0 deletions doc/sphinx/api/cpp/pe.rst
Expand Up @@ -304,6 +304,38 @@ Rich Entry

----------

Utilities
*********

.. doxygenfunction:: LIEF::PE::get_type(const std::string &)
:project: lief

.. doxygenfunction:: LIEF::PE::get_type(const std::vector< uint8_t > &)
:project: lief

.. doxygenfunction:: LIEF::PE::is_pe(const std::string &)
:project: lief

.. doxygenfunction:: LIEF::PE::is_pe(const std::vector< uint8_t > &)
:project: lief

.. doxygenfunction:: LIEF::PE::u16tou8
:project: lief

.. doxygenfunction:: LIEF::PE::u8tou16
:project: lief

.. doxygenfunction:: LIEF::PE::get_imphash
:project: lief

.. doxygenfunction:: LIEF::PE::resolve_ordinals
:project: lief

.. doxygenfunction:: LIEF::PE::oid_to_string
:project: lief

----------

Enums
*****

Expand Down
15 changes: 13 additions & 2 deletions doc/sphinx/api/python/pe.rst
@@ -1,8 +1,6 @@
PE
--

.. autofunction:: lief.PE.oid_to_string

Parser
*******

Expand Down Expand Up @@ -371,6 +369,19 @@ Debug

----------

Utilities
*********


.. autofunction:: lief.PE.is_pe

.. autofunction:: lief.PE.get_type

.. autofunction:: lief.PE.get_imphash

.. autofunction:: lief.PE.resolve_ordinals

-----------

Enums
*****
Expand Down
23 changes: 21 additions & 2 deletions examples/python/pe_reader.py
Expand Up @@ -36,6 +36,16 @@ def __call__(self, *args, **kwargs):
traceback.print_tb(exc_traceback)
print("-" * 60)

@exceptions_handler(Exception)
def print_information(binary):
print("== Information ==\n")
format_str = "{:<30} {:<30}"
format_hex = "{:<30} 0x{:<28x}"
format_dec = "{:<30} {:<30d}"
print(format_str.format("Name:", binary.name))
print(format_hex.format("Virtual size:", binary.virtual_size))
print(format_str.format("Imphash:", PE.get_imphash(binary)))

@exceptions_handler(Exception)
def print_header(binary):
dos_header = binary.dos_header
Expand Down Expand Up @@ -179,11 +189,14 @@ def print_symbols(binary):
str(symbol.storage_class).split(".")[-1]))

@exceptions_handler(Exception)
def print_imports(binary):
def print_imports(binary, resolve=False):
print("== Imports ==")
imports = binary.imports

for import_ in imports:
if resolve:
import_ = lief.PE.resolve_ordinals(import_)

print(import_.name)
entries = import_.entries
f_value = " {:<33} 0x{:<14x} 0x{:<14x} 0x{:<16x}"
Expand Down Expand Up @@ -345,6 +358,10 @@ def main():
action='store_true', dest='show_imports',
help='Display imported functions and libraries')

optparser.add_option('--resolve-ordinals',
action='store_true', dest='resolve_ordinals',
help="When used with --import, it attempts to resolve names of ordinal imports")

optparser.add_option('-r', '--relocs',
action='store_true', dest='show_relocs',
help='Display the relocations (if present)')
Expand Down Expand Up @@ -385,14 +402,16 @@ def main():
sys.exit(1)


print_information(binary)

if options.show_data_directories or options.show_all:
print_data_directories(binary)

if options.show_headers or options.show_all:
print_header(binary)

if (options.show_imports or options.show_all) and binary.has_imports:
print_imports(binary)
print_imports(binary, resolve=options.resolve_ordinals)

if (options.show_relocs or options.show_all) and binary.has_relocations:
print_relocations(binary)
Expand Down
27 changes: 24 additions & 3 deletions include/LIEF/PE/utils.hpp
Expand Up @@ -34,17 +34,38 @@ DLL_PUBLIC bool is_pe(const std::string& file);
//! @brief check if the raw data is a PE file
DLL_PUBLIC bool is_pe(const std::vector<uint8_t>& raw);

//! @brief if the `file` is PE, return `PE32` or `PE32+`
//! @brief if the input `file` is a PE one, return `PE32` or `PE32+`
DLL_PUBLIC PE_TYPE get_type(const std::string& file);

//! @brief Return `PE32` or `PE32+`
DLL_PUBLIC PE_TYPE get_type(const std::vector<uint8_t>& raw);

//! Convert a UTF-16 string to a UTF-8 one
//! @brief Convert a UTF-16 string to a UTF-8 one
DLL_PUBLIC std::string u16tou8(const std::u16string& string);

//! Convert a UTF-8 string to a UTF-16 one
//! @brief Convert a UTF-8 string to a UTF-16 one
DLL_PUBLIC std::u16string u8tou16(const std::string& string);

//! @brief Compute the hash of imported functions
//!
//! Properties of the hash generated:
//! * Order agnostic
//! * Casse agnostic
//! * Ordinal (**in some extent**) agnostic
//!
//! @see https://www.fireeye.com/blog/threat-research/2014/01/tracking-malware-import-hashing.html
DLL_PUBLIC std::string get_imphash(const Binary& binary);

//! @brief Take a PE::Import as entry and try to resolve imports
//! by ordinal.
//!
//! The ``strict`` boolean parameter enables to throw an LIEF::not_found exception
//! if the ordinal can't be resolved. Otherwise it skips the entry.
//!
//! @param[in] import Import to resolve
//! @param[in] strict If set to ``true``, throw an exception if the import can't be resolved
//! @param[out] Import The import resolved: PE::ImportEntry::name is set
DLL_PUBLIC Import resolve_ordinals(const Import& import, bool strict=false);
}
}
#endif
31 changes: 21 additions & 10 deletions src/PE/CMakeLists.txt
Expand Up @@ -42,7 +42,7 @@ set(LIEF_PE_SRC
"${CMAKE_CURRENT_LIST_DIR}/resources/ResourceStringFileInfo.cpp"
"${CMAKE_CURRENT_LIST_DIR}/resources/LangCodeItem.cpp"
"${CMAKE_CURRENT_LIST_DIR}/resources/ResourceIcon.cpp"
)
)

file(READ "${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/enums.inc" LIEF_PE_ENUMS)
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/structures.inc" LIEF_PE_STRUCTURES)
Expand Down Expand Up @@ -89,7 +89,8 @@ set(LIEF_PE_INCLUDE_FILES
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/TLS.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/type_traits.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/undef.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/utils.hpp")
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/utils.hpp"
)

set(LIEF_PE_SIG_INCLUDE_FILES
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/signature/AuthenticatedAttributes.hpp"
Expand All @@ -100,7 +101,8 @@ set(LIEF_PE_SIG_INCLUDE_FILES
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/signature/SignerInfo.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/signature/types.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/signature/x509.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/PE/signature/pkcs7.h")
"${CMAKE_CURRENT_SOURCE_DIR}/src/PE/signature/pkcs7.h"
)

set(LIEF_PE_RESOURCES_INCLUDE_FILES
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/resources/ResourceVersion.hpp"
Expand All @@ -111,12 +113,21 @@ set(LIEF_PE_RESOURCES_INCLUDE_FILES
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/resources/ResourceVarFileInfo.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/resources/ResourceStringFileInfo.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/resources/LangCodeItem.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/resources/ResourceIcon.hpp")
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/PE/resources/ResourceIcon.hpp"
)

set(LIEF_PE_UTILS_INCLUDE_FILES
"${CMAKE_CURRENT_SOURCE_DIR}/src/PE/utils/ordinals_lookup_tables/libraries_table.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/PE/utils/ordinals_lookup_tables/kernel32_dll_lookup.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/PE/utils/ordinals_lookup_tables/ntdll_dll_lookup.hpp"
)


source_group("Source Files\\PE" FILES ${LIEF_PE_SRC})
source_group("Header Files\\PE" FILES ${LIEF_PE_INCLUDE_FILES})
source_group("Header Files\\PE\\signature" FILES ${LIEF_PE_SIG_INCLUDE_FILES})
source_group("Header Files\\PE\\resources" FILES ${LIEF_PE_RESOURCES_INCLUDE_FILES})
source_group("Source Files\\PE" FILES ${LIEF_PE_SRC})
source_group("Header Files\\PE" FILES ${LIEF_PE_INCLUDE_FILES})
source_group("Header Files\\PE\\signature" FILES ${LIEF_PE_SIG_INCLUDE_FILES})
source_group("Header Files\\PE\\resources" FILES ${LIEF_PE_RESOURCES_INCLUDE_FILES})
source_group("Header Files\\PE\\utils\\Ordinals Lookup Tables" FILES ${LIEF_PE_UTILS_INCLUDE_FILES})

target_sources(LIB_LIEF_STATIC PRIVATE ${LIEF_PE_SRC} ${LIEF_PE_INCLUDE_FILES} ${LIEF_PE_SIG_INCLUDE_FILES} ${LIEF_PE_RESOURCES_INCLUDE_FILES})
target_sources(LIB_LIEF_SHARED PRIVATE ${LIEF_PE_SRC} ${LIEF_PE_INCLUDE_FILES} ${LIEF_PE_SIG_INCLUDE_FILES} ${LIEF_PE_RESOURCES_INCLUDE_FILES})
target_sources(LIB_LIEF_STATIC PRIVATE ${LIEF_PE_SRC} ${LIEF_PE_INCLUDE_FILES} ${LIEF_PE_SIG_INCLUDE_FILES} ${LIEF_PE_RESOURCES_INCLUDE_FILES} ${LIEF_PE_UTILS_INCLUDE_FILES})
target_sources(LIB_LIEF_SHARED PRIVATE ${LIEF_PE_SRC} ${LIEF_PE_INCLUDE_FILES} ${LIEF_PE_SIG_INCLUDE_FILES} ${LIEF_PE_RESOURCES_INCLUDE_FILES} ${LIEF_PE_UTILS_INCLUDE_FILES})

0 comments on commit a89bc6d

Please sign in to comment.