Skip to content

Commit

Permalink
Unprintable characters in section names are now escaped (fixes #3).
Browse files Browse the repository at this point in the history
The PE's size is now cached and won't be computed everytime get_filesize
is called. This prevents useless I/O (#4).
  • Loading branch information
JusticeRage committed Feb 6, 2016
1 parent 0b9e792 commit 52ea5b1
Show file tree
Hide file tree
Showing 11 changed files with 350 additions and 90 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ include_directories(

add_library(manape SHARED manape/pe.cpp manape/nt_values.cpp manape/utils.cpp manape/imports.cpp manape/resources.cpp manape/section.cpp)

add_library(manacommons SHARED manacommons/color.cpp manacommons/output_tree_node.cpp manacommons/plugin_framework/result.cpp)
add_library(manacommons SHARED manacommons/color.cpp manacommons/output_tree_node.cpp manacommons/escape.cpp manacommons/plugin_framework/result.cpp)

add_executable(manalyze src/main.cpp src/config_parser.cpp src/output_formatter.cpp src/dump.cpp
src/plugin_framework/dynamic_library.cpp src/plugin_framework/plugin_manager.cpp # Plugin system
Expand Down
167 changes: 167 additions & 0 deletions include/manacommons/escape.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
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/>.
*/

#pragma once

#include <string>
#include <boost/static_assert.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

#include "manacommons/color.h"

namespace io
{

// ----------------------------------------------------------------------------
// Grammars used to escape stings
// ----------------------------------------------------------------------------

namespace karma = boost::spirit::karma;
typedef std::back_insert_iterator<std::string> sink_type;
typedef boost::shared_ptr<std::string> pString;

/**
* @brief This grammar is used to escape strings printed to the console.
*
* Printable characters are returned as-is, while the others are displayed using the C
* notation.
*/
template <typename OutputIterator>
struct escaped_string_raw
: karma::grammar<OutputIterator, std::string()>
{
escaped_string_raw()
: escaped_string_raw::base_type(esc_str)
{
esc_str = *(boost::spirit::karma::iso8859_1::print | "\\x" << karma::right_align(2, 0)[karma::hex]);
}

karma::rule<OutputIterator, std::string()> esc_str;
karma::symbols<char, char const*> esc_char;
};

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

/**
* @brief This grammar is used to escape strings printed by the JSON formatter.
*
* Paths contained in debug information insert unescaped backslashes which cause
* the resulting JSON to be invalid.
*
* WARNING: Single quotes are NOT escaped.
*/
// Source: http://svn.boost.org/svn/boost/trunk/libs/spirit/example/karma/escaped_string.cpp
template <typename OutputIterator>
struct escaped_string_json
: karma::grammar<OutputIterator, std::string()>
{
escaped_string_json()
: escaped_string_json::base_type(esc_str)
{
// We allow "'" because it will be used in messages (i.e. [... don't ...]).
// We don't care if those are not escaped because they will be printed between double quotes
// in JSON strings.
esc_char.add('\a', "\\a")('\b', "\\b")('\f', "\\f")('\n', "\\n")
('\r', "\\r")('\t', "\\t")('\v', "\\v")('\\', "\\\\")
('\"', "\\\"");

// JSON fails miserably on non-printable characters, but
// at the same time doesn't support the \x notation.
esc_str = *(esc_char | boost::spirit::karma::iso8859_1::print | "\\u00" << karma::right_align(2, 0)[karma::hex]);
}

karma::rule<OutputIterator, std::string()> esc_str;
karma::symbols<char, char const*> esc_char;
};

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

/*
* Forward-declare the OutputFormatter class, so it can be used in a static
* assert in template<typename T> std::string escape(const std::string&).
*/
class OutputFormatter;

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

/**
* @brief Performs the actual string escaping based on the grammar given as
* template parameter.
*
* @param const std::string& s The string to escape.
*
* @return A pointer to the escaped string, or a null pointer if an error occurred.
*/
template<typename Grammar>
pString _do_escape(const std::string& s)
{
BOOST_STATIC_ASSERT(boost::is_base_of<karma::grammar<sink_type, std::string()>, Grammar>::value);
typedef std::back_insert_iterator<std::string> sink_type;

std::string generated;
sink_type sink(generated);

Grammar g;
if (!karma::generate(sink, g, s))
{
PRINT_WARNING << "Could not escape \"" << s << "!" << std::endl;
return nullptr;
}
else {
return boost::make_shared<std::string>(generated);
}
}

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

/**
* @brief Escapes problematic characters from a string.
*
* The template parameter is the type of the formatter calling the function.
* Each output formatter has an escape_grammar type which describes how the
* string should be escaped.
*
* @param const std::string& s The string to escape.
*
* @returns The escaped string, or a null pointer if an error was encountered.
*/
template<typename T>
pString escape(const std::string& s)
{
BOOST_STATIC_ASSERT(boost::is_base_of<OutputFormatter, T>::value);
return _do_escape<T::escape_grammar>(s);
}

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

/*
* @brief Escapes problematic characters from a string.
*
* Non printable characters found in the input string will be escaped using
* the C notation (i.e. \x0D).
*
* @param const std::string& s The string to escape.
*
* @returns The escaped string, or a null pointer if an error was encountered.
*/
DECLSPEC_MANACOMMONS pString escape(const std::string& s);

}
3 changes: 2 additions & 1 deletion include/manape/pe.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class PE
DECLSPEC virtual ~PE() {}
DECLSPEC static boost::shared_ptr<PE> create(const std::string& path);

DECLSPEC size_t get_filesize() const;
DECLSPEC boost::uint64_t get_filesize() const;

DECLSPEC pString get_path() const {
return boost::make_shared<std::string>(_path);
Expand Down Expand Up @@ -380,6 +380,7 @@ class PE

std::string _path;
bool _initialized;
boost::uint64_t _file_size;

/*
-----------------------------------
Expand Down
1 change: 1 addition & 0 deletions include/manape/section.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "manape/pe_structs.h"
#include "manape/utils.h"
#include "manacommons/color.h"
#include "manacommons/escape.h"

#if defined BOOST_WINDOWS_API
#ifdef MANAPE_EXPORT
Expand Down
29 changes: 11 additions & 18 deletions include/output_formatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,19 @@ along with Manalyze. If not, see <http://www.gnu.org/licenses/>.
#include <boost/cstdint.hpp>
#include <boost/date_time.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/spirit/include/karma.hpp>

#include "manacommons/output_tree_node.h"
#include "manacommons/escape.h" // String escaping functions
#include "manacommons/color.h"
#include "plugin_framework/result.h" // Necessary to hold a threat level in a node.

namespace io
{

// ----------------------------------------------------------------------------
// Base class of all the formatters
// ----------------------------------------------------------------------------

/**
* @brief Abstract class describing objects whose role is to display the output of the program
* in a specific format (raw, json, ...).
Expand Down Expand Up @@ -139,6 +143,8 @@ class OutputFormatter
boost::shared_ptr<OutputTreeNode> _root; // The analysis data is contained in this field
};

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

/**
* @brief The default formatter. Displays the data as a human readable text.
*/
Expand All @@ -147,6 +153,7 @@ class RawFormatter : public OutputFormatter

public:
virtual void format(std::ostream& sink, bool end_stream = true);
typedef escaped_string_raw<sink_type> escape_grammar;

private:
/**
Expand Down Expand Up @@ -184,13 +191,16 @@ class RawFormatter : public OutputFormatter

};

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

/**
* @brief Formatter that prints the analysis result in JSON.
*/
class JsonFormatter : public OutputFormatter
{
public:
virtual void format(std::ostream& sink, bool end_stream = true);
typedef escaped_string_json<sink_type> escape_grammar;

private:
/**
Expand Down Expand Up @@ -230,21 +240,4 @@ std::string uint64_to_version_number(boost::uint32_t msbytes, boost::uint32_t ls
*/
std::string timestamp_to_string(boost::uint64_t epoch_timestamp);

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

/**
* @brief Escapes problematic characters from a string.
*
* This is especially useful for JSON output, because paths contained in debug
* information insert unescaped backslashes which cause the resulting JSON to
* be invalid.
*
* WARNING: Single quotes are NOT escaped.
*
* @param const std::string& s The string to escape.
*
* @returns The escaped string.
*/
std::string escape(const std::string& s);

} // !namespace io
27 changes: 27 additions & 0 deletions manacommons/escape.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
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 "manacommons/escape.h"

namespace io
{

pString escape(const std::string& s) {
return _do_escape<escaped_string_raw<sink_type> >(s);
}

}
21 changes: 10 additions & 11 deletions manape/pe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ PE::PE(const std::string& path)
PRINT_ERROR << "Could not open " << _path << "." << std::endl;
goto END;
}

// Get the file size
fseek(f, 0, SEEK_END);
_file_size = ftell(f);
fseek(f, 0, SEEK_SET);

if (!_parse_dos_header(f)) {
goto END;
}
Expand Down Expand Up @@ -80,17 +86,8 @@ void PE::operator delete(void* p) {

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

size_t PE::get_filesize() const
{
FILE* f = fopen(_path.c_str(), "rb");
size_t res = 0;
if (f == nullptr) {
return res;
}
fseek(f, 0, SEEK_END);
res = ftell(f);
fclose(f);
return res;
boost::uint64_t PE::get_filesize() const {
return _file_size;
}

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -887,6 +884,8 @@ void delete_manape_module_data(manape_data* data)
}
}

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

boost::shared_ptr<manape_data> PE::create_manape_module_data() const
{
boost::shared_ptr<manape_data> res(new manape_data, delete_manape_module_data);
Expand Down
4 changes: 4 additions & 0 deletions manape/section.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ Section::Section(const image_section_header& header,
_path(path)
{
_name = std::string((char*) header.Name);
pString escaped = io::escape(_name);
if (escaped != nullptr) {
_name = *escaped;
}

if (_name.size() > 0 && _name[0] == '/')
{
Expand Down
2 changes: 1 addition & 1 deletion plugins/plugin_resources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class ResourcesPlugin : public IPlugin
}
}

float ratio = (float) size / (float) pe.get_filesize();
double ratio = static_cast<double>(size) / static_cast<double>(pe.get_filesize());
if (ratio > .75)
{
std::stringstream ss;
Expand Down

0 comments on commit 52ea5b1

Please sign in to comment.