Skip to content

Commit

Permalink
Resource timestamps will be interpreted as posix time if they cannot be
Browse files Browse the repository at this point in the history
converted to a dosdate or if they are very close to the compilation
date. This was implemented because some compilers do not set this
timestamp as DosDates.
Added unit tests for those conversion functions.
  • Loading branch information
JusticeRage committed Nov 24, 2018
1 parent b606b30 commit 98fd4e8
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 17 deletions.
2 changes: 1 addition & 1 deletion bin/yara_rules/suspicious_strings.yara
Original file line number Diff line number Diff line change
Expand Up @@ -1578,7 +1578,7 @@ rule Misc_Suspicious_Strings
rule BITS_CLSID
{
meta:
description = "References the BITS service."
description = "References the BITS service"
author = "Ivan Kwiatkowski (@JusticeRage)"
// The BITS service seems to be used heavily by EquationGroup.
strings:
Expand Down
12 changes: 12 additions & 0 deletions include/manape/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,16 @@ pptime DECLSPEC dosdate_to_btime(boost::uint32_t dosdate);
*/
pString DECLSPEC dosdate_to_string(boost::uint32_t dosdate);

// ----------------------------------------------------------------------------
/**
* @brief This helper function compares a dosdate and the PE timestamp to
* check whether the dosdate is actually a posix timestamp.
*
* @param dosdate The dosdate to test.
* @param pe_timestamp The reference posix time (typically, the PE compilation
* date from the PE header.
* @param threshold How close the two timestamps should be to determine that
* the dosdate is actually a posix timestamp (default is 0.1%).
*/
bool DECLSPEC is_actually_posix(boost::uint32_t dosdate, boost::uint32_t pe_timestamp, float threshold = 0.001);
}
33 changes: 26 additions & 7 deletions manape/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,18 @@ pString timestamp_to_string(boost::uint64_t epoch_timestamp)
static std::locale loc(std::cout.getloc(), new btime::time_facet("%Y-%b-%d %H:%M:%S%F %z"));
std::stringstream ss;
ss.imbue(loc);
ss << boost::posix_time::from_time_t(epoch_timestamp);
ss << btime::from_time_t(epoch_timestamp);
return boost::make_shared<std::string>(ss.str());
}

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

pptime dosdate_to_btime(boost::uint32_t dosdate)
{
if (dosdate == 0) {
return boost::make_shared<btime::ptime>(btime::ptime(boost::gregorian::date(1980, 1, 1)));
}

boost::uint16_t date = dosdate >> 16;
boost::uint16_t time = dosdate & 0xFFFF;
boost::uint16_t year = ((date & 0xFE00) >> 9) + 1980;
Expand All @@ -183,22 +187,37 @@ pptime dosdate_to_btime(boost::uint32_t dosdate)
second = 59;
}

if (dosdate == 0) {
return boost::make_shared<btime::ptime>(btime::ptime(boost::gregorian::date(1980, 1, 1)));
}

try {
return boost::make_shared<btime::ptime>(btime::ptime(boost::gregorian::date(year, month, day), btime::hours(hour) + btime::minutes(minute) + btime::seconds(second)));
}
catch (std::exception&)
{
PRINT_WARNING << "Tried to convert an invalid DosDate: " << dosdate << "." << DEBUG_INFO << std::endl;
return pptime();
PRINT_WARNING << "Tried to convert an invalid DosDate: " << dosdate << ". Falling back to posix timestamp." << DEBUG_INFO << std::endl;
// Some samples seem to be using a standard epoch timestamp (i.e. be7dc7c927caa47740c369daf35fc5e5). Try falling back to that.
return boost::make_shared<btime::ptime>(btime::from_time_t(dosdate));
}
}

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

bool is_actually_posix(boost::uint32_t dosdate, boost::uint32_t pe_timestamp, float threshold)
{
if (dosdate == 0) {
return false;
}
float variation;
if (dosdate > pe_timestamp) {
variation = static_cast<float>(dosdate - pe_timestamp) / static_cast<float>(dosdate);
}
else {
variation = static_cast<float>(pe_timestamp - dosdate) / static_cast<float>(dosdate);
}

return abs(variation) <= threshold;
}

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

pString dosdate_to_string(boost::uint32_t dosdate)
{
static std::locale loc(std::cout.getloc(), new btime::time_facet("%Y-%b-%d %H:%M:%S%F %z"));
Expand Down
15 changes: 11 additions & 4 deletions plugins/plugin_resources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,19 @@ class ResourcesPlugin : public IPlugin
continue;
}

// Create a set of timestamps which differ from the one reported in the PE header.
const auto res_timestamp = utils::dosdate_to_btime(it->get_timestamp());
utils::pptime res_timestamp;
// Some compilers seem to use posix times as timestamps. Determine which situation we are in.
if (utils::is_actually_posix(it->get_timestamp(), pe.get_pe_header()->TimeDateStamp)) {
res_timestamp = boost::make_shared<btime::ptime>(boost::posix_time::from_time_t(it->get_timestamp()));
}
else {
res_timestamp = utils::dosdate_to_btime(it->get_timestamp());
}
if (!res_timestamp) { // Ignore un-convertable timestamps.
continue;
}

// Create a set of timestamps which differ from the one reported in the PE header.
auto delta = *res_timestamp - pe_timestamp;
// There might be a slight delta between the PE timestamp and the one found in the resources.
// Assume nobody will tamper them to fake the compilation date by less than 12 hours.
Expand Down Expand Up @@ -100,7 +107,7 @@ class ResourcesPlugin : public IPlugin
{
if (abs(delta.minutes()) == 59 || abs(delta.minutes()) <= 1) {
std::stringstream ss;
ss << "The binary may have been compiled on a machine on the UTC" << std::showpos << hours << " timezone.";
ss << "The binary may have been compiled on a machine in the UTC" << std::showpos << hours << " timezone.";
res->add_information(ss.str());
timezones.insert(hours);
}
Expand Down Expand Up @@ -197,7 +204,7 @@ class ResourcesPlugin : public IPlugin
res->set_summary("The PE is possibly a dropper.");
}
else if (res->get_information()->size() > 0 && !res->get_summary()) {
res->set_summary("The PE contains encrypted or compressed resources.");
res->set_summary("The PE's resources present abnormal characteristics.");
}

return res;
Expand Down
12 changes: 9 additions & 3 deletions src/dump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ void dump_exports(const mana::PE& pe, io::OutputFormatter& formatter)
void dump_resources(const mana::PE& pe, io::OutputFormatter& formatter, bool compute_hashes /* = false */)
{
shared_resources resources = pe.get_resources();
if (resources->size() == 0) {
if (resources->empty()) {
return;
}

Expand All @@ -268,11 +268,17 @@ void dump_resources(const mana::PE& pe, io::OutputFormatter& formatter, bool com
res->append(boost::make_shared<io::OutputTreeNode>("Language", *(*it)->get_language()));
res->append(boost::make_shared<io::OutputTreeNode>("Codepage", *nt::translate_to_flag((*it)->get_codepage(), nt::CODEPAGES)));
res->append(boost::make_shared<io::OutputTreeNode>("Size", (*it)->get_size(), io::OutputTreeNode::DEC));
res->append(boost::make_shared<io::OutputTreeNode>("TimeDateStamp", *utils::dosdate_to_string((*it)->get_timestamp())));
if (utils::is_actually_posix((*it)->get_timestamp(), pe.get_pe_header()->TimeDateStamp)) {
res->append(boost::make_shared<io::OutputTreeNode>("TimeDateStamp", *utils::timestamp_to_string((*it)->get_timestamp())));
}
else {
res->append(boost::make_shared<io::OutputTreeNode>("TimeDateStamp", *utils::dosdate_to_string((*it)->get_timestamp())));
}

res->append(boost::make_shared<io::OutputTreeNode>("Entropy", (*it)->get_entropy()));

yara::const_matches m = detect_filetype(*it);
if (m && m->size() > 0)
if (m && !m->empty())
{
for (auto it2 = m->begin() ; it2 != m->end() ; ++it2) {
res->append(boost::make_shared<io::OutputTreeNode>("Detected Filetype", (*it2)->operator[]("description")));
Expand Down
2 changes: 1 addition & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 2.6)
project (manalyze-tests)
include_directories(${PROJECT_SOURCE_DIR}/include)

add_executable(manalyze-tests fixtures.cpp hash-library.cpp pe.cpp imports.cpp resources.cpp section.cpp escape.cpp encoding.cpp base64.cpp
add_executable(manalyze-tests fixtures.cpp hash-library.cpp pe.cpp imports.cpp resources.cpp section.cpp escape.cpp encoding.cpp base64.cpp utils.cpp
../src/import_hash.cpp)

target_link_libraries(
Expand Down
2 changes: 1 addition & 1 deletion test/section.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
This file is part of Manalyze.
Manalyze is free software: you can redistribute it and/or modify
Expand Down
53 changes: 53 additions & 0 deletions test/utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
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 <boost/test/unit_test.hpp>
#include "manape/utils.h"

BOOST_AUTO_TEST_CASE(test_dosdate_to_string)
{
const auto date_1 = utils::dosdate_to_string(0);
const auto date_2 = utils::dosdate_to_string(0x40B349E2);
// Test fallback to timestamp:
const auto date_3 = utils::dosdate_to_string(1168460802);

BOOST_ASSERT(date_1);
BOOST_ASSERT(date_2);
BOOST_ASSERT(date_3);
BOOST_CHECK_EQUAL(*date_1, "1980-Jan-01 00:00:00");
BOOST_CHECK_EQUAL(*date_2, "2012-May-19 09:15:04");
BOOST_CHECK_EQUAL(*date_3, "2007-Jan-10 20:26:42");
}

BOOST_AUTO_TEST_CASE(test_timestamp_to_string)
{
const auto date_1 = utils::timestamp_to_string(0);
const auto date_2 = utils::timestamp_to_string(0x40B349E2);
BOOST_ASSERT(date_1);
BOOST_ASSERT(date_2);
BOOST_CHECK_EQUAL(*date_1, "1970-Jan-01 00:00:00");
BOOST_CHECK_EQUAL(*date_2, "2004-May-25 13:28:02");
}

BOOST_AUTO_TEST_CASE(test_is_actually_posix)
{
BOOST_CHECK(!utils::is_actually_posix(0, 0x530b3da0));
BOOST_CHECK(utils::is_actually_posix(0x530b3da0, 0x530b3da0));
BOOST_CHECK(utils::is_actually_posix(0x530b3da3, 0x530b3da0));
BOOST_CHECK(utils::is_actually_posix(0x530b3d90, 0x530b3da0));
BOOST_CHECK(!utils::is_actually_posix(0x40b349e2, 0x4fb6e609));
}

0 comments on commit 98fd4e8

Please sign in to comment.