From 96e6fc66adafdbfb7ed04e1c528c76dcdbd2a182 Mon Sep 17 00:00:00 2001 From: "Koh M. Nakagawa" <tsunekou1019@gmail.com> Date: Thu, 25 Jun 2020 23:06:27 +0900 Subject: [PATCH] fix ResourcesManager to parse string table entry Signed-off-by: Koh M. Nakagawa <tsunekou1019@gmail.com> --- api/python/PE/CMakeLists.txt | 1 + api/python/PE/objects/pyResourcesManager.cpp | 9 +++ .../resources/pyResourceStringTable.cpp | 65 +++++++++++++++++ api/python/PE/pyPE.cpp | 2 + api/python/PE/pyPE.hpp | 2 + doc/sphinx/api/cpp/pe.rst | 9 +++ doc/sphinx/api/python/pe.rst | 10 +++ include/LIEF/PE/ResourcesManager.hpp | 11 +++ include/LIEF/PE/hash.hpp | 2 + include/LIEF/PE/json.hpp | 2 + .../LIEF/PE/resources/ResourceStringTable.hpp | 63 +++++++++++++++++ include/LIEF/Visitor.hpp | 5 ++ src/PE/CMakeLists.txt | 1 + src/PE/ResourcesManager.cpp | 57 +++++++++++++++ src/PE/hash.cpp | 6 ++ src/PE/json.cpp | 20 ++++++ src/PE/resources/ResourceStringTable.cpp | 69 +++++++++++++++++++ tests/pe/test_resources.py | 14 ++++ 18 files changed, 348 insertions(+) create mode 100644 api/python/PE/objects/resources/pyResourceStringTable.cpp create mode 100644 include/LIEF/PE/resources/ResourceStringTable.hpp create mode 100644 src/PE/resources/ResourceStringTable.cpp diff --git a/api/python/PE/CMakeLists.txt b/api/python/PE/CMakeLists.txt index 9c5500a..7de6297 100644 --- a/api/python/PE/CMakeLists.txt +++ b/api/python/PE/CMakeLists.txt @@ -13,6 +13,7 @@ set(LIEF_PYTHON_PE_SRC "${CMAKE_CURRENT_LIST_DIR}/objects/resources/pyLangCodeItem.cpp" "${CMAKE_CURRENT_LIST_DIR}/objects/resources/pyResourceDialog.cpp" "${CMAKE_CURRENT_LIST_DIR}/objects/resources/pyResourceDialogItem.cpp" + "${CMAKE_CURRENT_LIST_DIR}/objects/resources/pyResourceStringTable.cpp" "${CMAKE_CURRENT_LIST_DIR}/objects/signature/pySignerInfo.cpp" "${CMAKE_CURRENT_LIST_DIR}/objects/signature/pyAuthenticatedAttributes.cpp" diff --git a/api/python/PE/objects/pyResourcesManager.cpp b/api/python/PE/objects/pyResourcesManager.cpp index d078e34..8da8921 100644 --- a/api/python/PE/objects/pyResourcesManager.cpp +++ b/api/python/PE/objects/pyResourcesManager.cpp @@ -1,5 +1,6 @@ /* Copyright 2017 R. Thomas * Copyright 2017 Quarkslab + * Copyright 2020 K. Nakagawa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -104,6 +105,14 @@ void create<ResourcesManager>(py::module& m) { "``True`` if the resource has the given " RST_CLASS_REF(lief.PE.RESOURCE_TYPES) "", "type"_a) + .def_property_readonly("has_string_table", + &ResourcesManager::has_string_table, + "``True`` if resources contain " RST_CLASS_REF(lief.PE.ResourceStringTable) "") + + .def_property_readonly("string_table", + &ResourcesManager::string_table, + "Return list of " RST_CLASS_REF(lief.PE.ResourceStringTable) " present in the resource") + .def("get_node_type", static_cast<no_const_func<ResourceNode&, RESOURCE_TYPES>>(&ResourcesManager::get_node_type), "Return " RST_CLASS_REF(lief.PE.ResourceNode) " with " diff --git a/api/python/PE/objects/resources/pyResourceStringTable.cpp b/api/python/PE/objects/resources/pyResourceStringTable.cpp new file mode 100644 index 0000000..d8529a4 --- /dev/null +++ b/api/python/PE/objects/resources/pyResourceStringTable.cpp @@ -0,0 +1,65 @@ +/* Copyright 2017 R. Thomas + * Copyright 2017 Quarkslab + * Copyright 2020 K. Nakagawa + * + * 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/hash.hpp" +#include "LIEF/PE/resources/ResourceStringTable.hpp" + +#include <string> +#include <sstream> + +namespace LIEF { +namespace PE { + +template<class T> +using getter_t = T (ResourceStringTable::*)(void) const; + +template<class T> +using setter_t = void (ResourceStringTable::*)(T); + + +template<> +void create<ResourceStringTable>(py::module& m) { + py::class_<ResourceStringTable, LIEF::Object>(m, "ResourceStringTable") + + .def_property_readonly("length", + static_cast<getter_t<int16_t>>(&ResourceStringTable::length), + "The size of the string, not including length field itself.") + + .def_property_readonly("name", + static_cast<getter_t<const std::u16string&>>(&ResourceStringTable::name), + "The variable-length Unicode string data, word-aligned." + ) + + .def("__eq__", &ResourceStringTable::operator==) + .def("__ne__", &ResourceStringTable::operator!=) + .def("__hash__", + [] (const ResourceStringTable& string_table) { + return Hash::hash(string_table); + }) + + .def("__str__", + [] (const ResourceStringTable& string_table) { + std::ostringstream stream; + stream << string_table; + std::string str = stream.str(); + return str; + }); +} + +} +} diff --git a/api/python/PE/pyPE.cpp b/api/python/PE/pyPE.cpp index 9c5d822..c82bef0 100644 --- a/api/python/PE/pyPE.cpp +++ b/api/python/PE/pyPE.cpp @@ -1,5 +1,6 @@ /* Copyright 2017 R. Thomas * Copyright 2017 Quarkslab + * Copyright 2020 K. Nakagawa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -73,6 +74,7 @@ void init_objects(py::module& m) { CREATE(ResourceIcon, m); CREATE(ResourceDialog, m); CREATE(ResourceDialogItem, m); + CREATE(ResourceStringTable, m); CREATE(Signature, m); CREATE(x509, m); CREATE(SignerInfo, m); diff --git a/api/python/PE/pyPE.hpp b/api/python/PE/pyPE.hpp index 8ed6366..20f68be 100644 --- a/api/python/PE/pyPE.hpp +++ b/api/python/PE/pyPE.hpp @@ -1,5 +1,6 @@ /* Copyright 2017 R. Thomas * Copyright 2017 Quarkslab + * Copyright 2020 K. Nakagawa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -76,6 +77,7 @@ SPECIALIZE_CREATE(ResourceFixedFileInfo); SPECIALIZE_CREATE(ResourceVarFileInfo); SPECIALIZE_CREATE(LangCodeItem); SPECIALIZE_CREATE(ResourceIcon); +SPECIALIZE_CREATE(ResourceStringTable); SPECIALIZE_CREATE(ResourceDialog); SPECIALIZE_CREATE(ResourceDialogItem); SPECIALIZE_CREATE(Signature); diff --git a/doc/sphinx/api/cpp/pe.rst b/doc/sphinx/api/cpp/pe.rst index ae4f825..be693b5 100644 --- a/doc/sphinx/api/cpp/pe.rst +++ b/doc/sphinx/api/cpp/pe.rst @@ -311,6 +311,15 @@ Lang code item :project: lief +---------- + +Resource String Table +********************* + +.. doxygenclass:: LIEF::PE::ResourceStringTable + :project: lief + + ---------- Rich Header diff --git a/doc/sphinx/api/python/pe.rst b/doc/sphinx/api/python/pe.rst index a7f868e..1719cd0 100644 --- a/doc/sphinx/api/python/pe.rst +++ b/doc/sphinx/api/python/pe.rst @@ -351,6 +351,16 @@ Lang code item ---------- +Resource String Table +********************* + +.. autoclass:: lief.PE.ResourceStringTable + :members: + :inherited-members: + :undoc-members: + +---------- + Rich Header *********** diff --git a/include/LIEF/PE/ResourcesManager.hpp b/include/LIEF/PE/ResourcesManager.hpp index 8142048..1f40351 100644 --- a/include/LIEF/PE/ResourcesManager.hpp +++ b/include/LIEF/PE/ResourcesManager.hpp @@ -1,5 +1,6 @@ /* Copyright 2017 R. Thomas * Copyright 2017 Quarkslab + * Copyright 2020 K. Nakagawa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +30,7 @@ #include "LIEF/PE/resources/ResourceVersion.hpp" #include "LIEF/PE/resources/ResourceIcon.hpp" #include "LIEF/PE/resources/ResourceDialog.hpp" +#include "LIEF/PE/resources/ResourceStringTable.hpp" namespace LIEF { namespace PE { @@ -116,6 +118,15 @@ class LIEF_API ResourcesManager : public Object { //! @brief Return the list of the dialogs present in the resource std::vector<ResourceDialog> dialogs(void) const; + // String table + // ===== + + //! @brief ``true`` if resources contain @link LIEF::PE::ResourceStringTable @endlink + bool has_string_table(void) const; + + //! @brief Return the list of the string table in the resource + std::vector<ResourceStringTable> string_table(void) const; + // Print // ===== diff --git a/include/LIEF/PE/hash.hpp b/include/LIEF/PE/hash.hpp index 515c109..bc17c53 100644 --- a/include/LIEF/PE/hash.hpp +++ b/include/LIEF/PE/hash.hpp @@ -1,5 +1,6 @@ /* Copyright 2017 R. Thomas * Copyright 2017 Quarkslab + * Copyright 2020 K. Nakagawa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -65,6 +66,7 @@ class LIEF_API Hash : public LIEF::Hash { virtual void visit(const ResourceIcon& resource_icon) override; virtual void visit(const ResourceDialog& dialog) override; virtual void visit(const ResourceDialogItem& dialog_item) override; + virtual void visit(const ResourceStringTable& string_table) override; virtual void visit(const Signature& signature) override; virtual void visit(const x509& x509) override; virtual void visit(const SignerInfo& signerinfo) override; diff --git a/include/LIEF/PE/json.hpp b/include/LIEF/PE/json.hpp index 0fb8623..30bf139 100644 --- a/include/LIEF/PE/json.hpp +++ b/include/LIEF/PE/json.hpp @@ -1,5 +1,6 @@ /* Copyright 2017 R. Thomas * Copyright 2017 Quarkslab + * Copyright 2020 K. Nakagawa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,6 +59,7 @@ class LIEF_API JsonVisitor : public LIEF::JsonVisitor { virtual void visit(const ResourceStringFileInfo& resource_sfi) override; virtual void visit(const ResourceFixedFileInfo& resource_ffi) override; virtual void visit(const ResourceVarFileInfo& resource_vfi) override; + virtual void visit(const ResourceStringTable& resource_st) override; virtual void visit(const LangCodeItem& resource_lci) override; virtual void visit(const ResourceIcon& resource_icon) override; virtual void visit(const ResourceDialog& dialog) override; diff --git a/include/LIEF/PE/resources/ResourceStringTable.hpp b/include/LIEF/PE/resources/ResourceStringTable.hpp new file mode 100644 index 0000000..d6c61c8 --- /dev/null +++ b/include/LIEF/PE/resources/ResourceStringTable.hpp @@ -0,0 +1,63 @@ +/* Copyright 2017 R. Thomas + * Copyright 2017 Quarkslab + * Copyright 2020 K. Nakagawa + * + * 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. + */ +#ifndef LIEF_PE_RESOURCE_STRING_TABLE_H_ +#define LIEF_PE_RESOURCE_STRING_TABLE_H_ +#include <string> + +#include "LIEF/visibility.h" + +#include "LIEF/Object.hpp" + +#include "LIEF/PE/Structures.hpp" +#include "LIEF/PE/type_traits.hpp" + +namespace LIEF { +namespace PE { +class ResourcesManager; + +class LIEF_API ResourceStringTable : public Object { + + friend class ResourcesManager; + public: + ResourceStringTable(void); + + ResourceStringTable(int16_t length, const std::u16string& name); + ResourceStringTable(const ResourceStringTable&); + + ResourceStringTable& operator=(const ResourceStringTable&); + + virtual ~ResourceStringTable(void); + + virtual void accept(Visitor& visitor) const override; + + int16_t length(void) const; + const std::u16string& name(void) const; + + bool operator==(const ResourceStringTable& rhs) const; + bool operator!=(const ResourceStringTable& rhs) const; + + LIEF_API friend std::ostream& operator<<(std::ostream& os, const ResourceStringTable& string_table); + + private: + std::u16string name_; + int16_t length_; +}; + +} +} + +#endif \ No newline at end of file diff --git a/include/LIEF/Visitor.hpp b/include/LIEF/Visitor.hpp index 635c3b9..c25477f 100644 --- a/include/LIEF/Visitor.hpp +++ b/include/LIEF/Visitor.hpp @@ -1,5 +1,6 @@ /* Copyright 2017 R. Thomas * Copyright 2017 Quarkslab + * Copyright 2020 K. Nakagawa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -76,6 +77,7 @@ LIEF_PE_FORWARD(LangCodeItem) LIEF_PE_FORWARD(ResourceIcon) LIEF_PE_FORWARD(ResourceDialog) LIEF_PE_FORWARD(ResourceDialogItem) +LIEF_PE_FORWARD(ResourceStringTable) LIEF_PE_FORWARD(Signature) LIEF_PE_FORWARD(x509) LIEF_PE_FORWARD(SignerInfo) @@ -338,6 +340,9 @@ class LIEF_API Visitor { //! Method to visit a LIEF::PE::ResourceVarFileInfo LIEF_PE_VISITABLE(ResourceVarFileInfo) + //! Method to visit a LIEF::PE::ResourceStringTable + LIEF_PE_VISITABLE(ResourceStringTable) + //! Method to visit a LIEF::PE::LangCodeItem LIEF_PE_VISITABLE(LangCodeItem) diff --git a/src/PE/CMakeLists.txt b/src/PE/CMakeLists.txt index c791ec2..693cb29 100644 --- a/src/PE/CMakeLists.txt +++ b/src/PE/CMakeLists.txt @@ -47,6 +47,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" + "${CMAKE_CURRENT_LIST_DIR}/resources/ResourceStringTable.cpp" ) set(LIEF_PE_LOAD_CONFIGURATION_SRC diff --git a/src/PE/ResourcesManager.cpp b/src/PE/ResourcesManager.cpp index fb16f86..23ca55c 100644 --- a/src/PE/ResourcesManager.cpp +++ b/src/PE/ResourcesManager.cpp @@ -1,5 +1,6 @@ /* Copyright 2017 R. Thomas * Copyright 2017 Quarkslab + * Copyright 2020 K. Nakagawa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +35,7 @@ #include "LIEF/PE/ResourceData.hpp" #include "LIEF/PE/resources/LangCodeItem.hpp" +#include "LIEF/PE/resources/ResourceStringTable.hpp" namespace LIEF { namespace PE { @@ -1191,6 +1193,61 @@ bool ResourcesManager::has_dialogs(void) const { return this->has_type(RESOURCE_TYPES::DIALOG); } +// String table entry +std::vector<ResourceStringTable> ResourcesManager::string_table(void) const { + it_childs nodes = this->resources_->childs(); + auto&& it_string_table = std::find_if( + std::begin(nodes), + std::end(nodes), + [] (const ResourceNode& node) { + return static_cast<RESOURCE_TYPES>(node.id()) == RESOURCE_TYPES::STRING; + } + ); + + if (it_string_table == std::end(nodes)) { + throw not_found(std::string("Missing '") + to_string(RESOURCE_TYPES::STRING) + "' entry"); + } + + std::vector<ResourceStringTable> string_table; + for (const ResourceNode& child_l1 : it_string_table->childs()) { + + for (const ResourceNode& child_l2 : child_l1.childs()) { + const ResourceData* string_table_node = dynamic_cast<const ResourceData*>(&child_l2); + if (!string_table_node) { + LOG(ERROR) << "String table node is null"; + continue; + } + + const std::vector<uint8_t>& content = string_table_node->content(); + if (content.empty()) { + LOG(ERROR) << "String table content is empty"; + continue; + } + + const auto content_size = content.size(); + VectorStream stream{content}; + stream.setpos(0); + VLOG(VDEBUG) << "Will parse content whoose size is " << content_size; + while (stream.pos() < content_size) { + if (not stream.can_read<int16_t>()) break; + + const auto len = stream.read<uint16_t>(); + if (len > 0 && ((len * 2) < content_size)) { + const auto name = stream.read_u16string(len); + string_table.emplace_back(ResourceStringTable(len, name)); + } + } + } + + } + + return string_table; +} + +bool ResourcesManager::has_string_table(void) const { + return this->has_type(RESOURCE_TYPES::STRING); +} + // Prints // ====== diff --git a/src/PE/hash.cpp b/src/PE/hash.cpp index 38523c6..cac3651 100644 --- a/src/PE/hash.cpp +++ b/src/PE/hash.cpp @@ -1,5 +1,6 @@ /* Copyright 2017 R. Thomas * Copyright 2017 Quarkslab + * Copyright 2020 K. Nakagawa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -423,6 +424,11 @@ void Hash::visit(const ResourceDialogItem& dialog_item) { } } +void Hash::visit(const ResourceStringTable& string_table) { + this->process(string_table.length()); + this->process(string_table.name()); +} + void Hash::visit(const Signature& signature) { this->process(signature.version()); diff --git a/src/PE/json.cpp b/src/PE/json.cpp index 8f651d2..2a04c7f 100644 --- a/src/PE/json.cpp +++ b/src/PE/json.cpp @@ -1,5 +1,6 @@ /* Copyright 2017 R. Thomas * Copyright 2017 Quarkslab + * Copyright 2020 K. Nakagawa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -543,6 +544,20 @@ void JsonVisitor::visit(const ResourcesManager& resources_manager) { LOG(WARNING) << e.what(); } } + + if (resources_manager.has_string_table()) { + std::vector<json> string_table_json; + try { + for (const ResourceStringTable& string_table : resources_manager.string_table()) { + JsonVisitor string_table_visitor; + string_table_visitor(string_table); + string_table_json.emplace_back(string_table_visitor.get()); + this->node_["string_table"] = string_table_json; + } + } catch (const LIEF::exception& e) { + LOG(WARNING) << e.what(); + } + } } void JsonVisitor::visit(const ResourceStringFileInfo& resource_sfi) { @@ -683,6 +698,11 @@ void JsonVisitor::visit(const ResourceDialogItem& dialog_item) { } +void JsonVisitor::visit(const ResourceStringTable& string_table) { + this->node_["length"] = string_table.length(); + this->node_["name"] = u16tou8(string_table.name()); +} + void JsonVisitor::visit(const Signature& signature) { JsonVisitor content_info_visitor; diff --git a/src/PE/resources/ResourceStringTable.cpp b/src/PE/resources/ResourceStringTable.cpp new file mode 100644 index 0000000..0393411 --- /dev/null +++ b/src/PE/resources/ResourceStringTable.cpp @@ -0,0 +1,69 @@ +/* Copyright 2017 R. Thomas + * Copyright 2017 Quarkslab + * Copyright 2020 K. Nakagawa + * + * 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 "LIEF/PE/utils.hpp" +#include "LIEF/PE/hash.hpp" +#include "LIEF/PE/EnumToString.hpp" + +#include "LIEF/PE/resources/ResourceStringTable.hpp" + +namespace LIEF { +namespace PE { + +ResourceStringTable::ResourceStringTable(const ResourceStringTable&) = default; +ResourceStringTable& ResourceStringTable::operator=(const ResourceStringTable&) = default; +ResourceStringTable::~ResourceStringTable(void) = default; + +ResourceStringTable::ResourceStringTable(void) : + name_{}, + length_{0} +{} + +ResourceStringTable::ResourceStringTable(int16_t length, const std::u16string& name) : + name_{name}, + length_{length} +{} + +int16_t ResourceStringTable::length(void) const { + return this->length_; +} + +const std::u16string& ResourceStringTable::name(void) const { + return this->name_; +} + +void ResourceStringTable::accept(Visitor& visitor) const { + visitor.visit(*this); +} + +bool ResourceStringTable::operator==(const ResourceStringTable& rhs) const { + return Hash::hash(*this) == Hash::hash(rhs); +} + +bool ResourceStringTable::operator!=(const ResourceStringTable& rhs) const { + return not (*this == rhs); +} + + +std::ostream& operator<<(std::ostream& os, const ResourceStringTable& string_table) { + os << std::dec << "Length: " << string_table.length() << std::endl; + os << "Name: \"" << u16tou8(string_table.name()) << "\"" << std::endl; + return os; +} + +} +} \ No newline at end of file diff --git a/tests/pe/test_resources.py b/tests/pe/test_resources.py index df28d84..503b1bf 100644 --- a/tests/pe/test_resources.py +++ b/tests/pe/test_resources.py @@ -71,6 +71,20 @@ class TestResource(TestCase): self.assertEqual(q.returncode, 0) + def test_resource_string_table(self): + sample_path = get_sample('PE/PE64_x86-64_binary_WinApp.exe') + mfc = lief.parse(sample_path) + resources_manager = mfc.resources_manager + + self.assertEqual(resources_manager.has_string_table, True) + + string_table = resources_manager.string_table + self.assertEqual(string_table[0].name, "WinApp") + self.assertEqual(string_table[0].length, 6) + + self.assertEqual(string_table[1].name, "WINAPP") + self.assertEqual(string_table[1].length, 6) + def test_resource_version(self): sample_path = get_sample('PE/PE64_x86-64_binary_mfc-application.exe')