Skip to content

Commit

Permalink
Added more unit tests for PE resources and a new test file.
Browse files Browse the repository at this point in the history
  • Loading branch information
JusticeRage committed Feb 14, 2016
1 parent 1fc201c commit 999ed24
Show file tree
Hide file tree
Showing 11 changed files with 195 additions and 31 deletions.
12 changes: 6 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,25 @@ if (WIN32)
set(Boost_USE_STATIC_RUNTIME ON)
endif()

if (GitHub STREQUAL ON)
if (GitHub MATCHES [Oo][Nn])
find_package(Git REQUIRED)
endif()
if (Tests STREQUAL OFF)
if (NOT Tests MATCHES [Oo][Nn])
find_package(Boost REQUIRED COMPONENTS regex system filesystem program_options)
else()
find_package(Boost REQUIRED COMPONENTS regex system filesystem program_options unit_test_framework)
endif()

# Download or update external projects
if (EXISTS external/yara AND GitHub STREQUAL ON)
if (EXISTS external/yara AND GitHub MATCHES [Oo][Nn])
message("Updating yara...")
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=external/yara/.git fetch)
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=external/yara/.git --work-tree=external/yara merge origin/master)

message("Updating hash-library...")
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=external/hash-library/.git fetch)
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=external/hash-library/.git --work-tree=external/hash-library merge origin/master)
elseif (GitHub STREQUAL ON)
elseif (GitHub MATCHES [Oo][Nn])
message("Checking out yara...")
execute_process(COMMAND ${GIT_EXECUTABLE} clone https://github.com/JusticeRage/yara.git external/yara)
message("Checking out hash-library...")
Expand Down Expand Up @@ -84,7 +84,7 @@ else()
if (CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]")
add_definitions("/D_DEBUG")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
if (Tests STREQUAL ON) # Add coverage option if unit tests were requested.
if (Tests MATCHES [Oo][Nn]) # Add coverage option if unit tests were requested.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage")
endif()
Expand All @@ -108,7 +108,7 @@ add_subdirectory(external/yara)
# hash-library dependency
add_subdirectory(external/hash-library)

if (Tests STREQUAL ON)
if (Tests MATCHES [Oo][Nn])
add_subdirectory(test)
endif()

Expand Down
4 changes: 2 additions & 2 deletions include/manape/resources.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ class Resource
*
* All the work is performed in one of the template specializations.
* Currently, the following interpretations are implemented:
* * std::string for RT_MANIFEST
* * std::vector<std::string> for RT_STRING
* * pString for RT_MANIFEST
* * const_shared_strings for RT_STRING
* * pgroup_icon_directory_t for RT_GROUP_ICON and RT_GROUP_CURSOR
* * pbitmap for RT_BITMAP
* * pversion_info for RT_VERSION
Expand Down
12 changes: 6 additions & 6 deletions manape/resources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,9 @@ DECLSPEC pString Resource::interpret_as()
// ----------------------------------------------------------------------------

template<>
std::vector<std::string> Resource::interpret_as()
DECLSPEC const_shared_strings Resource::interpret_as()
{
std::vector<std::string> res;
auto res = boost::make_shared<std::vector<std::string> >();
if (_type != "RT_STRING")
{
PRINT_WARNING << "Resources of type " << _type << " cannot be interpreted as vectors of strings." << DEBUG_INFO << std::endl;
Expand All @@ -294,7 +294,7 @@ std::vector<std::string> Resource::interpret_as()

// RT_STRING resources are made of 16 contiguous "unicode" strings.
for (int i = 0; i < 16; ++i) {
res.push_back(utils::read_prefixed_unicode_string(f));
res->push_back(utils::read_prefixed_unicode_string(f));
}

END:
Expand Down Expand Up @@ -681,8 +681,8 @@ bool PE::extract_resources(const std::string& destination_folder)
else if (*(*it)->get_type() == "RT_STRING")
{
// Append all the strings to the same file.
auto strings = (*it)->interpret_as<std::vector<std::string> >();
if (strings.size() == 0) {
auto strings = (*it)->interpret_as<const_shared_strings>();
if (strings->size() == 0) {
continue;
}

Expand All @@ -694,7 +694,7 @@ bool PE::extract_resources(const std::string& destination_folder)
continue;
}

for (auto it2 = strings.begin(); it2 != strings.end(); ++it2)
for (auto it2 = strings->begin(); it2 != strings->end(); ++it2)
{
if ((*it2) != "")
{
Expand Down
18 changes: 18 additions & 0 deletions test/pe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ BOOST_AUTO_TEST_CASE(parse_testfile)
mana::PE pe("testfiles/manatest.exe");
BOOST_CHECK_EQUAL(pe.get_filesize(), 16360);
BOOST_ASSERT(pe.is_valid());
mana::PE pe2("testfiles/manatest2.exe");
BOOST_CHECK_EQUAL(pe2.get_filesize(), 72704);
BOOST_ASSERT(pe2.is_valid());
}

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -220,6 +223,21 @@ void check_debug_directory_entry(mana::debug_directory_entry d,

// ----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(parse_exports)
{
mana::PE pe("testfiles/manatest2.exe");
auto pexports = pe.get_exports();
BOOST_ASSERT(pexports);
BOOST_ASSERT(pexports->size() == 1);
auto exported = pexports->at(0);
BOOST_CHECK_EQUAL(exported->Address, 0x1000);
BOOST_CHECK_EQUAL(exported->ForwardName, "");
BOOST_CHECK_EQUAL(exported->Name, "exported");
BOOST_CHECK_EQUAL(exported->Ordinal, 1);
}

// ----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(parse_debug_info)
{
mana::PE pe("testfiles/manatest.exe");
Expand Down
106 changes: 105 additions & 1 deletion test/resources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ You should have received a copy of the GNU General Public License
along with Manalyze. If not, see <http://www.gnu.org/licenses/>.
*/

#include <boost/assign.hpp>

#include "fixtures.h"
#include "manape/pe.h"
#include "manape/resources.h"
Expand Down Expand Up @@ -87,7 +89,109 @@ BOOST_AUTO_TEST_CASE(extract_manifest)
auto h = hash::hash_file(*hash::ALL_DIGESTS.at(ALL_DIGESTS_SHA256), "manatest_1_RT_MANIFEST.xml");
fs::remove("manatest_1_RT_MANIFEST.xml");
BOOST_ASSERT(h);
BOOST_CHECK(*h == "4bb79dcea0a901f7d9eac5aa05728ae92acb42e0cb22e5dd14134f4421a3d8df");
BOOST_CHECK_EQUAL(*h, "4bb79dcea0a901f7d9eac5aa05728ae92acb42e0cb22e5dd14134f4421a3d8df");
}

// ----------------------------------------------------------------------------

/**
* @brief Helper function which checks all of a Resource's fields against
* given values.
*/
void check_resource(mana::pResource r,
boost::uint32_t id,
const std::string& type,
const std::string& language,
boost::uint32_t size,
double entropy)
{
BOOST_ASSERT(r);
BOOST_CHECK_EQUAL(r->get_id(), id);
BOOST_ASSERT(r->get_type());
BOOST_CHECK_EQUAL(*r->get_type(), type);
BOOST_CHECK_EQUAL(r->get_codepage(), 0);
BOOST_ASSERT(r->get_language());
BOOST_CHECK_EQUAL(*r->get_language(), language);
BOOST_CHECK_EQUAL(r->get_size(), size);
double res_entropy = r->get_entropy();
BOOST_CHECK(entropy - 0.01 < res_entropy &&
res_entropy < entropy + 0.01);
}

// ----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(parse_resources_2)
{
mana::PE pe("testfiles/manatest2.exe");
auto resources = pe.get_resources();
BOOST_ASSERT(resources->size() == 14);
check_resource(resources->at(0), 102, "RC_DATA", "French - France", 16360, 6.18);
check_resource(resources->at(1), 1, "RT_ICON", "English - United States", 0xb13, 7.44);
check_resource(resources->at(2), 2, "RT_ICON", "English - United States", 0xea8, 2.14);
check_resource(resources->at(3), 3, "RT_ICON", "English - United States", 0x8a8, 1.94);
check_resource(resources->at(4), 4, "RT_ICON", "English - United States", 0x568, 1.24);
check_resource(resources->at(5), 5, "RT_ICON", "English - United States", 0xc4a, 7.48);
check_resource(resources->at(6), 6, "RT_ICON", "English - United States", 0x4228, 2.58);
check_resource(resources->at(7), 7, "RT_ICON", "English - United States", 0x25a8, 2.68);
check_resource(resources->at(8), 8, "RT_ICON", "English - United States", 0x10a8, 2.69);
check_resource(resources->at(9), 9, "RT_ICON", "English - United States", 0x468, 2.87);
check_resource(resources->at(10), 7, "RT_STRING", "German - Germany", 0x38, 1.54);
check_resource(resources->at(11), 101, "RT_GROUP_ICON", "English - United States", 0x84, 3.00);
check_resource(resources->at(12), 1, "RT_VERSION", "English - United States", 0x2d4, 3.31);
check_resource(resources->at(13), 1, "RT_MANIFEST", "English - United States", 0x17d, 4.91);
}

// ----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(interpret_stringtable)
{
mana::PE pe("testfiles/manatest2.exe");
auto resources = pe.get_resources();
BOOST_ASSERT(resources->size() == 14);
auto string_table = resources->at(10)->interpret_as<const_shared_strings>();
BOOST_ASSERT(string_table && string_table->size() == 16);
BOOST_CHECK_EQUAL(string_table->at(7), "Test 1");
BOOST_CHECK_EQUAL(string_table->at(8), "Test 2");
}

// ----------------------------------------------------------------------------

template<class T>
void check_pair(boost::shared_ptr<std::pair<T, T> > pair, const T& first, const T& second )
{
BOOST_CHECK_EQUAL(pair->first, first);
BOOST_CHECK_EQUAL(pair->second, second);
}

// ----------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE(interpret_versioninfo)
{
mana::PE pe("testfiles/manatest2.exe");
auto resources = pe.get_resources();
BOOST_ASSERT(resources->size() == 14);
auto vi = resources->at(12)->interpret_as<mana::pversion_info>();
BOOST_ASSERT(vi);
BOOST_CHECK_EQUAL(vi->Header.Key, "VS_VERSION_INFO");
BOOST_CHECK_EQUAL(vi->Value->Signature, 0xfeef04bd);
BOOST_CHECK_EQUAL(vi->Value->FileFlags, 0);
BOOST_CHECK_EQUAL(*nt::translate_to_flag(vi->Value->FileType, nt::FIXEDFILEINFO_FILETYPE), "VFT_APP");
std::vector<std::string> fileos_expected =
boost::assign::list_of("VOS_DOS_WINDOWS32")("VOS_NT")("VOS_NT_WINDOWS32")("VOS_WINCE")("VOS__WINDOWS32");
auto fileos = *nt::translate_to_flags(vi->Value->FileOs, nt::FIXEDFILEINFO_FILEOS);
BOOST_ASSERT(fileos.size() == fileos_expected.size());
BOOST_CHECK_EQUAL_COLLECTIONS(fileos.begin(), fileos.end(), fileos_expected.begin(), fileos_expected.end());

// VersionInfo string table
std::vector<mana::ppair> string_table = vi->StringTable;
check_pair<std::string>(string_table[0], "CompanyName", "manalyzer.org");
check_pair<std::string>(string_table[1], "FileDescription", "Manalyze test file.");
check_pair<std::string>(string_table[2], "FileVersion", "1.0.0.0");
check_pair<std::string>(string_table[3], "InternalName", "manatest2.exe");
check_pair<std::string>(string_table[4], "LegalCopyright", "Copyright (C) 2016");
check_pair<std::string>(string_table[5], "OriginalFilename", "manatest2.exe");
check_pair<std::string>(string_table[6], "ProductName", "manatest2.exe");
check_pair<std::string>(string_table[7], "ProductVersion", "1.0.0.0");
}

// ----------------------------------------------------------------------------
Expand Down
16 changes: 0 additions & 16 deletions test/testfiles/manatest.cpp

This file was deleted.

Binary file added test/testfiles/manatest2_src/Resource.rc
Binary file not shown.
Binary file added test/testfiles/manatest2_src/icon1.ico
Binary file not shown.
25 changes: 25 additions & 0 deletions test/testfiles/manatest2_src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
This file is part of Manalyze.
Manalyze is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Manalyze is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Manalyze. If not, see <http://www.gnu.org/licenses/>.
*/

extern "C" __declspec(dllexport) void exported() {
return;
}

int main(int argc, char** argv)
{

}
Binary file added test/testfiles/manatest2_src/resource.h
Binary file not shown.
33 changes: 33 additions & 0 deletions test/testfiles/manatest_src/manatest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
This file is part of Manalyze.
Manalyze is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Manalyze is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Manalyze. If not, see <http://www.gnu.org/licenses/>.
*/

#include <Windows.h>
#include <iostream>

int main(int argc, char** argv)
{
// Vanilla injection imports
OpenProcess(0, 0, 0);
CreateRemoteThread(0, 0, 0, 0, 0, 0, 0);
WriteProcessMemory(0, 0, 0, 0, 0);

std::cout << "cmd.exe" << std::endl;
// EICAR antivirus signature. ClamAV's rules only detect the standard signature at offset 0, but somehow has a rule
// for the base64-encoded string.
std::cout << "WDVPIVAlQEFQWzRcUFpYNTQoUF4pN0NDKTd9JEVJQ0FSLVNUQU5EQVJELUFOVElWSVJVUy1URVNU\nLUZJTEUhJEgrSCo=\n";
std::cout << std::hex;
}

0 comments on commit 999ed24

Please sign in to comment.