diff --git a/README.md b/README.md index 576568a..c81156c 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,14 @@ Available methods and associated options: - `off` - Offset at which to apply data - Cannot be a range - `data` - Hex data + - `replace` - Replace bytes within matching sequence + - `off` - Offset into sequence to start replacing + - Cannot be a range + - Can be negative + - `seq` - Hex sequence to search for + - `data` - Replacement data + - `multi` - Whether to allow multiple replacements + - NOTE: Previously matching sequence is currently skipped Global options: - `chance` - How likely the tamper method is to be used on a given packet diff --git a/inc/PacketHandler.hpp b/inc/PacketHandler.hpp index 7d727af..ea81d79 100644 --- a/inc/PacketHandler.hpp +++ b/inc/PacketHandler.hpp @@ -14,8 +14,8 @@ class PacketHandler { private: std::vector meths; /*!< List of configured tamper methods, in order they are to be applied */ - std::random_device rand_rd; - std::default_random_engine rand_engine; + std::random_device rand_rd; + std::mt19937 rand_engine; int handleTCPPacket(struct iphdr *_ip_head); int handleUDPPacket(struct iphdr *_ip_head); diff --git a/inc/TamperMethod.hpp b/inc/TamperMethod.hpp index 453edc7..3c6e1dd 100644 --- a/inc/TamperMethod.hpp +++ b/inc/TamperMethod.hpp @@ -77,6 +77,22 @@ class TamperMethod { */ static int parseBool(std::string &_str, bool &_val); + /** + * @brief Parse hex string from configuration + * + * The format is byte-ordered groups of two hexadecimal characters: + * [[[...]]] + * + * Where each of is a sequence of two characters, e.g.: + * C0FFEE1234 + * + * @param _str Configuration value string + * @param _val Output byte vector + * + * @return 0 on success, < 0 on failure + */ + static int parseHex(std::string &_str, std::vector &_val); + /** * @brief Generates a random non-negative number in the given range * diff --git a/inc/TamperMethods/TamperMethodRand.hpp b/inc/TamperMethods/TamperMethodRand.hpp index 0968492..bcf2f6d 100644 --- a/inc/TamperMethods/TamperMethodRand.hpp +++ b/inc/TamperMethods/TamperMethodRand.hpp @@ -18,8 +18,8 @@ class TamperMethodRand : public TamperMethod { int size_min = 1; /*!< Minimum size of modified region */ int size_max = 1; /*!< Maximum size of modified region */ - std::random_device rand_rd; - std::default_random_engine rand_engine; + std::random_device rand_rd; + std::mt19937 rand_engine; public: TamperMethodRand(std::map &_opts); diff --git a/inc/TamperMethods/TamperMethodReplace.hpp b/inc/TamperMethods/TamperMethodReplace.hpp new file mode 100644 index 0000000..6cbf51c --- /dev/null +++ b/inc/TamperMethods/TamperMethodReplace.hpp @@ -0,0 +1,24 @@ +#ifndef _TAMPER_METHOD_REPLACE_HPP_ +#define _TAMPER_METHOD_REPLACE_HPP_ + +#include + +#include "TamperMethod.hpp" + +/** + * @brief Method used to tamper with packet data using search-and-replace with offset + */ +class TamperMethodReplace : public TamperMethod { + private: + int offset = 0; /*!< Offset into sequence in which to insert data */ + bool multi = false; /*!< Whether to allow multiple replacements per packet */ + std::vector data; /*!< Data to insert */ + std::vector sequence; /*!< Sequence to search for */ + + public: + TamperMethodReplace(std::map &_opts); + + int tamper(size_t len, uint8_t *data); +}; + +#endif /* _TAMPER_METHOD_REPLACE_HPP_ */ diff --git a/src/PacketHandler.cpp b/src/PacketHandler.cpp index 7aea504..005ec41 100644 --- a/src/PacketHandler.cpp +++ b/src/PacketHandler.cpp @@ -120,7 +120,7 @@ int PacketHandler::doTamper(size_t len, uint8_t *data) { return 0; } - std::uniform_real_distribution rand_dist(0, 1); + std::uniform_real_distribution rand_dist(0, 0.999); for(TamperMethod *meth : this->meths) { if(meth->getProbability() >= rand_dist(this->rand_engine)) { diff --git a/src/TamperMethod.cpp b/src/TamperMethod.cpp index 5227669..977ab30 100644 --- a/src/TamperMethod.cpp +++ b/src/TamperMethod.cpp @@ -5,6 +5,7 @@ #include "TamperMethod.hpp" #include "TamperMethods/TamperMethodRand.hpp" #include "TamperMethods/TamperMethodFixed.hpp" +#include "TamperMethods/TamperMethodReplace.hpp" TamperMethod::TamperMethod(std::map &_opts) { if(_opts.count("chance")) { @@ -43,6 +44,8 @@ TamperMethod *TamperMethod::create(std::string &_str) { return new TamperMethodRand(opts); } else if(meth == "fixed") { return new TamperMethodFixed(opts); + } else if(meth == "replace") { + return new TamperMethodReplace(opts); } else { throw std::runtime_error("Unhandled tamper method: " + meth); } @@ -85,7 +88,24 @@ int TamperMethod::parseBool(std::string &_str, bool &_val) { return 0; } +int TamperMethod::parseHex(std::string &_str, std::vector &_val) { + if((_str.size() == 0) || + (_str.size() & 1)) { + return -1; + } + for(size_t i = 0; i < _str.size(); i+=2) { + uint8_t byte = std::stoi(_str.substr(i,2), NULL, 16); + _val.push_back(byte); + } + + return 0; +} + + size_t TamperMethod::randRange(int _min, int _max, size_t _sz) { + static std::random_device rand_rd; + static std::mt19937 rand_engine(rand_rd()); + if(_max == -1 || (size_t)_max > _sz) { _max = _sz; } @@ -98,9 +118,7 @@ size_t TamperMethod::randRange(int _min, int _max, size_t _sz) { } /* TODO: This is likely highly ineffecient */ - std::random_device rand_rd; - std::default_random_engine rand_engine(rand_rd()); std::uniform_int_distribution rand_dist(_min, _max); return rand_dist(rand_engine); -} \ No newline at end of file +} diff --git a/src/TamperMethods/TamperMethodFixed.cpp b/src/TamperMethods/TamperMethodFixed.cpp index 79919c8..004489b 100644 --- a/src/TamperMethods/TamperMethodFixed.cpp +++ b/src/TamperMethods/TamperMethodFixed.cpp @@ -17,12 +17,8 @@ TamperMethod(_opts) { throw std::runtime_error("offset<0 for TamperMethodFixed!"); } - if(_opts["data"].size() & 1) { - throw std::runtime_error("Data length must be an even number of characters for TamperMethodFixed!"); - } - for(size_t i = 0; i < _opts["data"].size(); i+=2) { - uint8_t byte = std::stoi(_opts["data"].substr(i,2), NULL, 16); - this->data.push_back(byte); + if(TamperMethod::parseHex(_opts["data"], this->data)) { + throw std::runtime_error("Error while parsing data for TamperMethodFixed!"); } } diff --git a/src/TamperMethods/TamperMethodReplace.cpp b/src/TamperMethods/TamperMethodReplace.cpp new file mode 100644 index 0000000..abb03d9 --- /dev/null +++ b/src/TamperMethods/TamperMethodReplace.cpp @@ -0,0 +1,85 @@ +#include +#include + +#include "TamperMethods/TamperMethodReplace.hpp" + +TamperMethodReplace::TamperMethodReplace(std::map &_opts) : +TamperMethod(_opts) { + if(!_opts.count("off")) { + throw std::runtime_error("Missing offset for TamperMethodReplace!"); + } + if(!_opts.count("data")) { + throw std::runtime_error("Missing data for TamperMethodReplace!"); + } + if(!_opts.count("seq")) { + throw std::runtime_error("Missing sequence for TamperMethodReplace!"); + } + + this->offset = std::stoi(_opts["off"]); + + if(TamperMethod::parseHex(_opts["data"], this->data)) { + throw std::runtime_error("Error while parsing data for TamperMethodReplace!"); + } + if(TamperMethod::parseHex(_opts["seq"], this->sequence)) { + throw std::runtime_error("Error while parsing sequence for TamperMethodReplace!"); + } + + if(_opts.count("multi") && + TamperMethod::parseBool(_opts["multi"], this->multi)) { + throw std::runtime_error("Error while parsing multi for TamperMethodReplace!"); + } +} + +int TamperMethodReplace::tamper(size_t len, uint8_t *data) { + size_t search_sz = this->sequence.size(); + + for(size_t i = 0; i < (len - search_sz); i++) { + size_t j = 0; + for(; j < search_sz; j++) { + if(data[i+j] != this->sequence[j]) { + break; + } + } + if(j == search_sz) { + /* Sequence match */ + size_t sz = this->data.size(); + size_t off = i; + size_t idx = 0; + + if(((ssize_t)off + this->offset) < 0) { + idx = (size_t)-((ssize_t)off + this->offset); + if(idx >= sz) { + /* Entire insertion is before the payload */ + continue; + } + sz -= idx; + off = 0; + } else { + off += this->offset; + } + + if(off >= len) { + break; + } else if((off + sz) > len) { + /* Insertion continues beyond end of payload */ + sz = len - off; + } + + std::cerr << " + TamperMethodReplace(off: " << off << ", sz: " << sz << "): "; + std::cerr << std::setw(2) << std::setfill('0') << std::hex; + for(size_t k = 0; k < sz; k++) { + data[off + k] = this->data[idx + k]; + std::cerr << unsigned(data[off + k]); + } + std::cerr << std::endl << std::setw(0) << std::setfill(' ') << std::dec; + + if(!this->multi) { + break; + } + + i += (search_sz - 1); + } + } + + return 0; +} diff --git a/src/main.cpp b/src/main.cpp index c8acd70..a1d9db0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,6 +34,7 @@ int main(int argc, char **argv) { NFQueue nfqueue(_options.queueNumber, pHandler); nfqueue.open(); + std::cerr << "Starting NFQueue listener..." << std::endl; nfqueue.run(); /* TODO: Run in a different thread, close on keyboard interrupt. */ nfqueue.close();