Skip to content

Commit

Permalink
Moved the RICH header checks to the packer detection plugin.
Browse files Browse the repository at this point in the history
Added a new detection method based on the number of imports in the RICH
header.
  • Loading branch information
JusticeRage committed Mar 13, 2018
1 parent e736bcc commit 9e6ca31
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 36 deletions.
4 changes: 4 additions & 0 deletions include/manape/nt_values.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ extern const DECLSPEC flag_dict GLOBAL_FLAGS;
extern const DECLSPEC flag_dict HEAP_FLAGS;
extern const DECLSPEC flag_dict GUARD_FLAGS;

// RICH header tables
extern const DECLSPEC std::map<int, std::string> COMP_ID_TYPE;
extern const DECLSPEC flag_dict COMP_ID_PRODID;

/**
* @brief Breaks down an integer given as input as a combination of flags.
*
Expand Down
111 changes: 111 additions & 0 deletions manape/nt_values.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,117 @@ const flag_dict GUARD_FLAGS =

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

// Source: https://github.com/dishather/richprint/blob/master/comp_id.txt
// The strings cannot be used as keys here are there are multiple identical values.
const std::map<int, std::string> COMP_ID_TYPE =
boost::assign::map_list_of (0x000, "Unmarked objects")
(0x001, "Total imports")
(0x002, "Imports")
(0x004, "Linker")
(0x006, "Resource objects")
(0x00A, "C objects")
(0x00B, "C++ objects")
(0x00F, "ASM objects")
(0x015, "C objects")
(0x016, "C++ objects")
(0x019, "Imports")
(0x01C, "C objects")
(0x01D, "C++ objects")
(0x03D, "Linker")
(0x03F, "Exports")
(0x040, "ASM objects")
(0x045, "Resource objects")
(0x05A, "Linker")
(0x05C, "Exports")
(0x05D, "Imports")
(0x05F, "C objects")
(0x060, "C++ objects")
(0x06D, "C objects")
(0x06E, "C++ objects")
(0x078, "Linker")
(0x07C, "Resource objects")
(0x07A, "Exports")
(0x07B, "Imports")
(0x07D, "ASM objects")
(0x083, "C objects")
(0x084, "C++ objects")
(0x091, "Resource objects")
(0x092, "Exports")
(0x093, "Imports")
(0x094, "Linker")
(0x095, "ASM objects")
(0x09A, "Resource objects")
(0x09B, "Exports")
(0x09C, "Imports")
(0x09D, "Linker")
(0x09E, "ASM objects")
(0x0AA, "C objects")
(0x0AB, "C++ objects")
(0x0C9, "Resource objects")
(0x0CA, "Exports")
(0x0CB, "Imports")
(0x0CC, "Linker")
(0x0CD, "ASM objects")
(0x0CE, "C objects")
(0x0CF, "C++ objects")
(0x0DB, "Resource objects")
(0x0DC, "Exports")
(0x0ED, "Imports")
(0x0DE, "Linker")
(0x0DF, "ASM objects")
(0x0E0, "C objects")
(0x0E1, "C++ objects")
(0x0FF, "Resource objects")
(0x100, "Exports")
(0x101, "Imports")
(0x102, "Linker")
(0x103, "ASM objects")
(0x104, "C objects")
(0x105, "C++ objects");

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

const flag_dict COMP_ID_PRODID =
boost::assign::map_list_of ("VS97 SP3 link 5.10.7303", 0x1c87)
("VS97 SP3 cvtres 5.00.1668", 0x0684)
("VS98 cvtres build 1720", 0x06b8)
("VS98 build 8168", 0x1fe8)
("VS98 SP6 cvtres build 1736", 0x06c7)
("VC++ 6.0 SP5 imp/exp build 8447", 0x20ff)
("VC++ 6.0 SP5 build 8804", 0x2306)
("VS98 SP6 build 8804", 0x2636)
("VS2002 (.NET) build 9466", 0x24fa)
("VS2003 (.NET) build 3052", 0x0bec)
("VS2003 (.NET) build 3077", 0x0c05)
("VS2003 (.NET) build 4035", 0x0fc3)
("VS2003 (.NET) SP1 build 6030", 0x178e)
("VS2008 build 21022", 0x521e)
("VS2008 SP1 build 30729", 0x7809)
("VS2010 build 30319", 0x766f)
("VS2010 SP1 build 40219", 0x9d1b)
("VS2012 build 50727 / VS2005 build 50727", 0xc627)
("VS2012 UPD1 build 51106", 0xc7a2)
("VS2012 UPD2 build 60315", 0xeb9b)
("VS2012 UPD3 build 60610", 0xecc2)
("VS2012 UPD4 build 61030", 0xee66)
("VS2013 build 21005", 0x520d)
("VS2013 UPD2 build 30501", 0x7725)
("VS2013 UPD3 build 30723", 0x7803)
("VS2013 UPD4 build 31101", 0x797d)
("VS2013 UPD5 build 40629", 0x9eb5)
("VS2015 build 23026", 0x59f2)
("VS2015 UPD1 build 23506", 0x5bd2)
("VS2015 UPD2 build 23918", 0x5d6e)
("VS2015 UPD3 build 24123", 0x5e3b)
("VS2015 UPD3 build 24210", 0x5e92)
("VS2015 UPD3 build 24213", 0x5e95)
("VS2015 UPD3.1 build 24215", 0x5e97)
("VS2017 v15.5.4 build 25834", 0x64ea)

;

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

const_shared_strings translate_to_flags(int value, const flag_dict& dict)
{
auto res = boost::make_shared<std::vector<std::string> >();
Expand Down
58 changes: 58 additions & 0 deletions plugins/plugin_packer_detection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,61 @@ class PackerDetectionPlugin : public IPlugin
return boost::make_shared<std::string>("Tries to structurally detect packer presence.");
}

void rich_header_tests(const mana::PE& pe, pResult res) const
{
auto rich = pe.get_rich_header();
if (!rich) {
return;
}

// Validate the checksum in the RICH header
boost::uint32_t checksum = rich->file_offset;
auto bytes = pe.get_raw_bytes(rich->file_offset);
// Checksum of the DOS header
for (unsigned int i = 0; i < rich->file_offset; ++i)
{
// Ignore e_lfanew?
if (0x3c <= i && i < 0x40) {
continue;
}
checksum += utils::rol32(bytes->at(i), i);
}
// Checksum of the @comp.ids.
for (unsigned int i = 0; i < rich->values.size(); ++i)
{
auto v = rich->values.at(i);
checksum += utils::rol32((std::get<0>(v) << 16) | std::get<1>(v), std::get<2>(v));
}

if (checksum != rich->xor_key)
{
res->raise_level(MALICIOUS);
if (res->get_summary() == nullptr) {
res->set_summary("The file headers were tampered with.");
}
res->add_information("The RICH header checksum is invalid.");
}

// Verify that the number of imports is consistent with the one reported in the header
for (auto it = rich->values.begin() ; it != rich->values.end() ; ++it)
{
// Look for the "Total imports" @comp.id.
if (std::get<0>(*it) == 1)
{
auto imports = pe.find_imports(".*");
if (std::get<2>(*it) != imports->size())
{
if (res->get_summary() == nullptr) {
res->set_summary("The PE is packed or was manually edited.");
}
res->add_information("The number of imports reported in the RICH header is inconsistent.");
res->raise_level(SUSPICIOUS);
}
}
}

}

pResult analyze(const mana::PE& pe) override
{
pResult res = create_result();
Expand Down Expand Up @@ -207,6 +262,9 @@ class PackerDetectionPlugin : public IPlugin
res->add_information("The PE's resources are bigger than it is.");
}

// Perform tests on the RICH header.
rich_header_tests(pe, res);

// Put a default summary if none was set.
if (res->get_level() != NO_OPINION && res->get_summary() == nullptr) {
res->set_summary("The PE is possibly packed.");
Expand Down
37 changes: 2 additions & 35 deletions plugins/plugins_yara.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,41 +242,8 @@ class CompilerDetectionPlugin : public YaraPlugin
public:
CompilerDetectionPlugin() : YaraPlugin("yara_rules/compilers.yara") {}

pResult analyze(const mana::PE& pe) override
{
auto res = scan(pe, "Matching compiler(s):", NO_OPINION, "description");

// Check the checksum in the RICH header
auto rich = pe.get_rich_header();
if (rich)
{
boost::uint32_t checksum = rich->file_offset;
auto bytes = pe.get_raw_bytes(rich->file_offset);
// Checksum of the DOS header
for (unsigned int i = 0 ; i < rich->file_offset ; ++i)
{
// Ignore e_lfanew?
if (0x3c <= i && i < 0x40) {
continue;
}
checksum += utils::rol32(bytes->at(i), i);
}
// Checksum of the @comp.ids.
for (unsigned int i = 0 ; i < rich->values.size() ; ++i)
{
auto v = rich->values.at(i);
checksum += utils::rol32((std::get<0>(v) << 16) | std::get<1>(v), std::get<2>(v));
}

if (checksum != rich->xor_key)
{
res->raise_level(MALICIOUS);
res->set_summary("The file headers were almost certainly tampered with.");
res->add_information("The RICH header checksum is invalid.");
}
}

return res;
pResult analyze(const mana::PE& pe) override {
return scan(pe, "Matching compiler(s):", NO_OPINION, "description");
}

pString get_id() const override {
Expand Down
18 changes: 17 additions & 1 deletion src/dump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,23 @@ void dump_rich_header(const mana::PE& pe, io::OutputFormatter& formatter)
for (auto it = rich->values.begin() ; it != rich->values.end() ; ++it)
{
std::stringstream ss;
ss << "id " << std::get<0>(*it) << "=" << std::get<1>(*it);
if (nt::COMP_ID_TYPE.find(std::get<0>(*it)) != nt::COMP_ID_TYPE.end()) {
ss << nt::COMP_ID_TYPE.at(std::get<0>(*it));
}
else {
ss << std::get<0>(*it);
}

if (std::get<1>(*it) != 0)
{
auto s = *nt::translate_to_flag(std::get<1>(*it), nt::COMP_ID_PRODID);
if (s.find("UNKNOWN") != 0) {
ss << " (" << s << ")";
}
else {
ss << " (" << std::get<1>(*it) << ")";
}
}
rich_node->append(boost::make_shared<io::OutputTreeNode>(ss.str(), std::get<2>(*it)));
}
formatter.add_data(rich_node, *pe.get_path());
Expand Down

0 comments on commit 9e6ca31

Please sign in to comment.