Skip to content

Commit

Permalink
Merge branch 'pattern_tree'
Browse files Browse the repository at this point in the history
  • Loading branch information
hasherezade committed Feb 7, 2024
2 parents cd91deb + c81303c commit 7d8e686
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 62 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ set (utils_hdrs
utils/workingset_enum.h
utils/modules_enum.h
utils/artefacts_util.h
utils/pattern_tree.h
utils/process_reflection.h
utils/console_color.h
utils/strings_util.h
Expand Down
4 changes: 2 additions & 2 deletions pe_sieve_ver_short.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
#define PESIEVE_MAJOR_VERSION 0
#define PESIEVE_MINOR_VERSION 3
#define PESIEVE_MICRO_VERSION 8
#define PESIEVE_PATCH_VERSION 0
#define PESIEVE_PATCH_VERSION 2

#define PESIEVE_VERSION_STR "0.3.8"
#define PESIEVE_VERSION_STR "0.3.8.2"
2 changes: 1 addition & 1 deletion postprocessors/imp_rec/imp_reconstructor.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ namespace pesieve {
this->is64bit = peconv::is64bit(peBuffer.vBuf);
}
else {
const DWORD found_pattern = pesieve::util::is_64bit_code(peBuffer.vBuf, peBuffer.vBufSize);
const size_t found_pattern = pesieve::util::is_64bit_code(peBuffer.vBuf, peBuffer.vBufSize);
this->is64bit = (found_pattern != CODE_PATTERN_NOT_FOUND);
}
collectMainIatData();
Expand Down
130 changes: 75 additions & 55 deletions utils/artefacts_util.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
#include "artefacts_util.h"
#include <peconv.h>
#include "pattern_tree.h"

#ifdef _DEBUG
#include <iostream>
#endif

BYTE* pesieve::util::find_pattern(BYTE *buffer, size_t buf_size, BYTE* pattern_buf, size_t pattern_size, size_t max_iter)
using namespace pattern_tree;

BYTE* pesieve::util::find_pattern(BYTE* buffer, size_t buf_size, BYTE* pattern_buf, size_t pattern_size, size_t max_iter)
{
for (size_t i = 0; (i + pattern_size) < buf_size; i++) {
if (max_iter != 0 && i > max_iter) break;
Expand All @@ -16,15 +19,10 @@ BYTE* pesieve::util::find_pattern(BYTE *buffer, size_t buf_size, BYTE* pattern_b
return nullptr;
}

namespace pesieve {
typedef struct {
BYTE *ptr;
size_t size;
} t_pattern;
};

DWORD pesieve::util::is_32bit_code(BYTE *loadedData, size_t loadedSize)
bool init_32_patterns(Node* rootN)
{
if (!rootN) return false;

BYTE prolog32_pattern[] = {
0x55, // PUSH EBP
0x8b, 0xEC // MOV EBP, ESP
Expand All @@ -40,24 +38,16 @@ DWORD pesieve::util::is_32bit_code(BYTE *loadedData, size_t loadedSize)
0x89, 0xE5 // MOV EBP, ESP
};

t_pattern patterns[] = {
{ prolog32_pattern, sizeof(prolog32_pattern) },
{ prolog32_2_pattern, sizeof(prolog32_2_pattern) },
{ prolog32_3_pattern, sizeof(prolog32_3_pattern) }
};

DWORD pattern_found = CODE_PATTERN_NOT_FOUND;
for (DWORD i = 0; i < _countof(patterns); i++) {
if (find_pattern(loadedData, loadedSize, patterns[i].ptr, patterns[i].size)) {
pattern_found = i;
break;
}
}
return pattern_found;
Node::addPattern(rootN, "prolog32_1", prolog32_pattern, sizeof(prolog32_pattern));
Node::addPattern(rootN, "prolog32_2", prolog32_2_pattern, sizeof(prolog32_2_pattern));
Node::addPattern(rootN, "prolog32_3", prolog32_3_pattern, sizeof(prolog32_3_pattern));
return true;
}

DWORD pesieve::util::is_64bit_code(BYTE* loadedData, size_t loadedSize)
bool init_64_patterns(Node* rootN64)
{
if (!rootN64) return false;

BYTE prolog64_pattern[] = {
0x40, 0x53, // PUSH RBX
0x48, 0x83, 0xEC // SUB RSP, <BYTE>
Expand Down Expand Up @@ -96,50 +86,80 @@ DWORD pesieve::util::is_64bit_code(BYTE* loadedData, size_t loadedSize)
0x41, 0x57 // PUSH R15
};

t_pattern patterns[] = {
{ prolog64_pattern, sizeof(prolog64_pattern) },
{ prolog64_2_pattern, sizeof(prolog64_2_pattern) },
{ prolog64_3_pattern, sizeof(prolog64_3_pattern) },
{ prolog64_4_pattern, sizeof(prolog64_4_pattern) },
{ prolog64_5_pattern, sizeof(prolog64_5_pattern) },
{ prolog64_6_pattern, sizeof(prolog64_6_pattern) },
{ prolog64_7_pattern, sizeof(prolog64_7_pattern) }
};
Node::addPattern(rootN64, "prolog64_1", prolog64_pattern, sizeof(prolog64_pattern));
Node::addPattern(rootN64, "prolog64_2", prolog64_2_pattern, sizeof(prolog64_2_pattern));
Node::addPattern(rootN64, "prolog64_3", prolog64_3_pattern, sizeof(prolog64_3_pattern));
Node::addPattern(rootN64, "prolog64_4", prolog64_4_pattern, sizeof(prolog64_4_pattern));
Node::addPattern(rootN64, "prolog64_5", prolog64_5_pattern, sizeof(prolog64_5_pattern));
Node::addPattern(rootN64, "prolog64_6", prolog64_6_pattern, sizeof(prolog64_6_pattern));
Node::addPattern(rootN64, "prolog64_7", prolog64_7_pattern, sizeof(prolog64_7_pattern));
return true;
}

size_t search_till_pattern(Node* rootN, const BYTE* loadedData, size_t loadedSize)
{
if (rootN && loadedData) {
for (size_t i = 0; i < loadedSize; i++) {
Match m = rootN->getMatching(loadedData + i, loadedSize - i);
if (m.sign) return (m.offset + i);
}
}
return CODE_PATTERN_NOT_FOUND;
}

DWORD pattern_found = CODE_PATTERN_NOT_FOUND;
for (DWORD i = 0; i < _countof(patterns); i++) {
if (find_pattern(loadedData, loadedSize, patterns[i].ptr, patterns[i].size)) {
pattern_found = i;
break;
size_t search_all_matches(Node* rootN, const BYTE* loadedData, size_t loadedSize, std::vector<Match>& matches)
{
size_t found = 0;
if (rootN && loadedData) {
for (size_t i = 0; i < loadedSize; i++) {
Match m = rootN->getMatching(loadedData + i, loadedSize - i);
if (m.sign) {
matches.push_back(m);
found++;
}
}
}
return pattern_found;
return found;
}


size_t pesieve::util::is_32bit_code(BYTE *loadedData, size_t loadedSize)
{
static Node *rootN32 = nullptr;
if (!rootN32) {
rootN32 = new Node();
init_32_patterns(rootN32);
}
return search_till_pattern(rootN32, loadedData, loadedSize);
}

size_t pesieve::util::is_64bit_code(BYTE* loadedData, size_t loadedSize)
{
static Node* rootN64 = nullptr;
if (!rootN64) {
rootN64 = new Node();
init_64_patterns(rootN64);
}
return search_till_pattern(rootN64, loadedData, loadedSize);
}

bool pesieve::util::is_code(BYTE* loadedData, size_t loadedSize)
{
if (peconv::is_padding(loadedData, loadedSize, 0)) {
return false;
}
DWORD pattern_found = CODE_PATTERN_NOT_FOUND;
bool is64 = false;

bool found = false;
if ((pattern_found = is_32bit_code(loadedData, loadedSize)) != CODE_PATTERN_NOT_FOUND) {
found = true;
static Node* rootN = nullptr;
if (!rootN) {
rootN = new Node();
init_32_patterns(rootN);
init_64_patterns(rootN);
}
if (!found) {
if ((pattern_found = is_64bit_code(loadedData, loadedSize)) != CODE_PATTERN_NOT_FOUND) {
found = true;
is64 = true;
}
}
#ifdef _DEBUG
if (found) {
std::cout << "Is64: " << is64 << " Pattern ID: " << pattern_found << "\n";

if ((search_till_pattern(rootN, loadedData, loadedSize)) != CODE_PATTERN_NOT_FOUND) {
return true;
}
#endif
return found;
return false;
}

bool pesieve::util::is_executable(DWORD mapping_type, DWORD protection)
Expand Down
8 changes: 4 additions & 4 deletions utils/artefacts_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ namespace pesieve {
BYTE* find_pattern(BYTE *buffer, size_t buf_size, BYTE* pattern_buf, size_t pattern_size, size_t max_iter = 0);

/*
Scans the buffer searching for the hardcoded 32-bit code patterns. If found, returns the patten ID, otherwise returns CODE_PATTERN_NOT_FOUND
Scans the buffer searching for the hardcoded 32-bit code patterns. If found, returns the match offset, otherwise returns CODE_PATTERN_NOT_FOUND
*/
DWORD is_32bit_code(BYTE *loadedData, size_t loadedSize);
size_t is_32bit_code(BYTE *loadedData, size_t loadedSize);

/*
Scans the buffer searching for the hardcoded 64-bit code patterns. If found, returns the patten ID, otherwise returns CODE_PATTERN_NOT_FOUND
Scans the buffer searching for the hardcoded 64-bit code patterns. If found, returns the match offset, otherwise returns CODE_PATTERN_NOT_FOUND
*/
DWORD is_64bit_code(BYTE *loadedData, size_t loadedSize);
size_t is_64bit_code(BYTE *loadedData, size_t loadedSize);

/*
Scans the buffer searching for any hardcoded code patterns (both 32 and 64 bit).
Expand Down
148 changes: 148 additions & 0 deletions utils/pattern_tree.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#pragma once

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

namespace pattern_tree {

class Signature
{
public:
Signature(std::string _name, const BYTE* _pattern, size_t _pattern_size)
: name(_name), pattern(nullptr), pattern_size(0)
{
this->pattern = (BYTE*)::calloc(_pattern_size, 1);
if (!this->pattern) return;

::memcpy(this->pattern, _pattern, _pattern_size);
this->pattern_size = _pattern_size;
}

size_t pattern_size;
BYTE* pattern;
std::string name;
};

class Match
{
public:
Match()
: offset(-1), sign(nullptr)
{
}

Match(size_t _offset, Signature* _sign)
: offset(_offset), sign(_sign)
{
}

Match(const Match& _match) // copy constructor
{
offset = _match.offset;
sign = _match.sign;
}

size_t offset;
Signature* sign;
};

class Node
{
public:

static bool addPattern(Node* rootN, const char* _name, const BYTE* pattern, size_t pattern_size)
{
if (!rootN || !pattern) return false;

Node* next = rootN;
for (size_t i = 0; i < pattern_size; i++) {
next = next->addNext(pattern[i]);
if (!next) return false;
}
next->sign = new Signature(_name, pattern, pattern_size);
return true;
}

//---

Node()
: level(0), val(0),
sign(nullptr)
{
}

Node(BYTE _val, size_t _level)
: val(_val), level(_level),
sign(nullptr)
{
}

Node* getNode(BYTE _val)
{
auto found = immediates.find(_val);
if (found != immediates.end()) {
return found->second;
}
return nullptr;
}

Node* addNext(BYTE _val)
{
Node* nextN = getNode(_val);
if (!nextN) {
nextN = new Node(_val, this->level + 1);
immediates[_val] = nextN;
}
return nextN;
}

void print()
{
std::cout << std::hex << (unsigned int)val << " [" << level << "]" << " [" << immediates.size() << "]";
if (!immediates.size()) {
printf("\n");
return;
}
for (auto itr = immediates.begin(); itr != immediates.end(); ++itr) {
itr->second->print();
}
}

Match getMatching(const BYTE* data, size_t data_size)
{
Match empty;
Node* curr = this;
for (size_t i = 0; i < data_size; i++)
{
if (curr->iSign()) {
const size_t match_start = i - curr->sign->pattern_size;
return Match(match_start, curr->sign);
}
if (curr->isEnd()) return empty;
BYTE val = data[i];
curr = curr->getNode(val);
if (!curr) return empty;
}
return empty;
}

bool isEnd()
{
return this->immediates.size() ? false : true;
}

bool iSign()
{
return sign ? true : false;
}

protected:
Signature* sign;
BYTE val;
size_t level;
std::map<BYTE, Node*> immediates;
};

}; //namespace pattern_tree

0 comments on commit 7d8e686

Please sign in to comment.