Skip to content

Commit

Permalink
Merge branch 'iat2' (Issues: #98, #92, #77)
Browse files Browse the repository at this point in the history
  • Loading branch information
hasherezade committed Jan 18, 2022
2 parents d009c6e + b010ebe commit 74100b5
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 12 deletions.
157 changes: 148 additions & 9 deletions scanners/iat_scanner.cpp
Expand Up @@ -3,6 +3,7 @@
#include <peconv.h>

#include <fstream>
#include <iostream>

using namespace pesieve;

Expand Down Expand Up @@ -196,6 +197,129 @@ bool pesieve::IATScanner::hasImportTable(RemoteModuleData &remoteModData)
return true;
}


template <typename FIELD_T>
FIELD_T get_thunk_at_rva(BYTE *mod_buf, size_t mod_size, DWORD rva)
{
if (!mod_buf || !mod_size) {
return 0;
}
if (!peconv::validate_ptr(mod_buf, mod_size, (BYTE*)((ULONG_PTR)mod_buf + rva), sizeof(FIELD_T))) {
return 0;
}

FIELD_T* field_ptr = (FIELD_T*)((ULONG_PTR)mod_buf + rva);
return (*field_ptr);
}


bool pesieve::IATScanner::isValidFuncFilled(const peconv::ExportedFunc &possibleFunc, const peconv::ExportedFunc &definedFunc)
{
const std::string possible_short = peconv::remove_extension(peconv::get_file_name(possibleFunc.libName));
const std::string defined_short = peconv::remove_extension(peconv::get_file_name(definedFunc.libName));

if (!peconv::ExportedFunc::isTheSameFuncName(possibleFunc, definedFunc)) {
return false;
}

if (defined_short.compare(possible_short) == 0) {
return true;
}
std::string fullName = exportsMap.get_dll_path(possibleFunc.libName);
if (isInSystemDir(fullName)) {
//std::cout << "^ Common redir, full: " << fullName << " dFunc: " << definedFunc.toString() << "\n";
//common redirection
return true;
}

//std::cout << "!! Names mismatch: [" << defined_short << "] vs [" << possible_short << "] , full: " << fullName << "\n";
return false;
}

bool pesieve::IATScanner::scanByOriginalTable(peconv::ImpsNotCovered &not_covered)
{
if (!remoteModData.isInitialized()) {
std::cerr << "[-] Failed to initialize remote module header" << std::endl;
return false;
}

if (!moduleData.isInitialized() && !moduleData.loadOriginal()) {
std::cerr << "[-] Failed to initialize module data: " << moduleData.szModName << std::endl;
return false;
}
if (moduleData.is64bit() != remoteModData.is64bit()) {
std::cerr << "[-] Mismatching ModuleData given: " << moduleData.szModName << std::endl;
return false;
}
// get addresses of the thunks from the original module (file)
peconv::ImportsCollection collection;
if (!moduleData.loadImportsList(collection)) {
return false;
}

std::map<DWORD, peconv::ExportedFunc*>::iterator itr;
// get filled thunks from the mapped module (remote):

for (itr = collection.thunkToFunc.begin(); itr != collection.thunkToFunc.end(); ++itr) {
DWORD thunk_rva = itr->first;

//std::cout << "Thunk: " << std::hex << *itr << "\n";
ULONGLONG filled_val = 0;
if (moduleData.is64bit()) {
filled_val = get_thunk_at_rva<ULONGLONG>(remoteModData.imgBuffer, remoteModData.imgBufferSize, thunk_rva);
}
else {
filled_val = get_thunk_at_rva<DWORD>(remoteModData.imgBuffer, remoteModData.imgBufferSize, thunk_rva);
}
peconv::ExportedFunc* func = itr->second;
if (!func) {
// cannot retrieve the origial import
continue;
}

const std::set<peconv::ExportedFunc>* possibleExports = exportsMap.find_exports_by_va(filled_val);
// no export at this thunk:
if (!possibleExports || possibleExports->size() == 0) {

//filter out .NET: mscoree._CorExeMain
const std::string dShortName = peconv::get_dll_shortname(func->libName);
if ( dShortName.compare("mscoree") == 0 && (func->funcName.compare("_CorExeMain") || func->funcName.compare("_CorDllMain")) ) {
continue; //this is normal, skip it
}

not_covered.insert(thunk_rva, filled_val);
#ifdef _DEBUG
std::cout << "Function not covered: " << std::hex << thunk_rva << " [" << dShortName << "] func: [" << func->funcName << "] val: " << std::hex << filled_val << "\n";
#endif
continue;
}

// check if the defined import matches the possible ones:
bool is_covered = false;
std::set<peconv::ExportedFunc>::const_iterator cItr;
for (cItr = possibleExports->begin(); cItr != possibleExports->end(); ++cItr) {
const peconv::ExportedFunc possibleFunc = *cItr;
if (isValidFuncFilled(possibleFunc, *func)){
is_covered = true;
break;
}
}

if (!is_covered) {
not_covered.insert(thunk_rva, filled_val);
#ifdef _DEBUG
std::cout << "Mismatch at RVA: " << std::hex << thunk_rva << " " << func->libName<< " func: " << func->toString() << "\n";

for (cItr = possibleExports->begin(); cItr != possibleExports->end(); ++cItr) {
const peconv::ExportedFunc possibleFunc = *cItr;
std::cout << "\t proposed: " << possibleFunc.libName << " : " << possibleFunc.toString() << "\n";
}
#endif
}
}
return true;
}

IATScanReport* pesieve::IATScanner::scanRemote()
{
if (!remoteModData.isInitialized()) {
Expand All @@ -219,13 +343,22 @@ IATScanReport* pesieve::IATScanner::scanRemote()
return nullptr;
}
peconv::ImpsNotCovered not_covered;
peconv::fix_imports(vBuf, vBufSize, exportsMap, &not_covered);

t_scan_status status = SCAN_NOT_SUSPICIOUS;

// first try to find by the Import Table in the original file:
if (!scanByOriginalTable(not_covered)) {
// IAT scan failed:
status = SCAN_ERROR;
}

if (not_covered.count() > 0) {
#ifdef _DEBUG
std::cout << "[*] IAT: " << moduleData.szModName << " hooked: " << not_covered.count() << "\n";
#endif
status = SCAN_SUSPICIOUS;
}

IATScanReport *report = new(std::nothrow) IATScanReport(processHandle, remoteModData.modBaseAddr, remoteModData.getModuleSize(), moduleData.szModName);
if (!report) {
return nullptr;
Expand Down Expand Up @@ -261,6 +394,17 @@ void pesieve::IATScanner::initExcludedPaths()
std::transform(m_system32Path_str.begin(), m_system32Path_str.end(), m_system32Path_str.begin(), tolower);
}

bool pesieve::IATScanner::isInSystemDir(const std::string &moduleName)
{
std::string dirName = peconv::get_directory_name(moduleName);
std::transform(dirName.begin(), dirName.end(), dirName.begin(), tolower);

if (dirName == m_system32Path_str || dirName == m_sysWow64Path_str) {
return true;
}
return false;
}

bool pesieve::IATScanner::filterResults(peconv::ImpsNotCovered &notCovered, IATScanReport &report)
{
std::map<ULONGLONG, ULONGLONG>::iterator itr;
Expand All @@ -286,14 +430,9 @@ bool pesieve::IATScanner::filterResults(peconv::ImpsNotCovered &notCovered, IATS
// filter out hooks leading to system DLLs
char moduleName[MAX_PATH] = { 0 };
if (GetModuleFileNameExA(this->processHandle, (HMODULE)module_start, moduleName, sizeof(moduleName))) {
std::string dirName = peconv::get_directory_name(moduleName);
std::transform(dirName.begin(), dirName.end(), dirName.begin(), tolower);
#ifdef _DEBUG
std::cout << "Module dir name: " << dirName << "\n";
#endif
if (dirName == m_system32Path_str || dirName == m_sysWow64Path_str) {
if (isInSystemDir(moduleName)) {
#ifdef _DEBUG
std::cout << "Skipped: " << dirName << "\n";
std::cout << "Skipped: " << moduleName << "\n";
#endif
continue;
}
Expand Down
4 changes: 4 additions & 0 deletions scanners/iat_scanner.h
Expand Up @@ -77,7 +77,11 @@ namespace pesieve {
virtual IATScanReport* scanRemote();

private:
bool scanByOriginalTable(peconv::ImpsNotCovered &not_covered);
bool isValidFuncFilled(const peconv::ExportedFunc &possibleFunc, const peconv::ExportedFunc &definedFunc);

void initExcludedPaths();
bool isInSystemDir(const std::string &moduleName);

bool hasImportTable(RemoteModuleData &remoteModData);
bool filterResults(peconv::ImpsNotCovered &not_covered, IATScanReport &report);
Expand Down
53 changes: 52 additions & 1 deletion scanners/module_data.cpp
Expand Up @@ -21,6 +21,9 @@ bool pesieve::ModuleData::loadModuleName()
return false;
}
memcpy(this->szModName, my_name.c_str(), my_name.length());

// autoswitch the path to Wow64 mode if needed:
autoswichIfWow64Mapping();
return true;
}

Expand Down Expand Up @@ -126,6 +129,25 @@ bool pesieve::ModuleData::loadImportThunks(std::set<DWORD>& thunk_rvas)
return false;
}

bool pesieve::ModuleData::loadImportsList(peconv::ImportsCollection &collection)
{
if (!original_module || !original_size) {
return false;
}
if (!peconv::has_valid_import_table(original_module, original_size)) {
// No import table
return false;
}
if (!peconv::collect_imports(original_module, original_size, collection)) {
// Could not collect imports
return false;
}
if (collection.size()) {
return true;
}
return false;
}

bool pesieve::ModuleData::relocateToBase(ULONGLONG new_base)
{
if (!original_module) return false;
Expand All @@ -146,6 +168,28 @@ bool pesieve::ModuleData::relocateToBase(ULONGLONG new_base)
return true;
}


bool pesieve::ModuleData::autoswichIfWow64Mapping()
{
std::string mapped_name = pesieve::RemoteModuleData::getMappedName(processHandle, this->moduleHandle);
std::string module_name = this->szModName;
bool is_same = (to_lowercase(mapped_name) == to_lowercase(module_name));

size_t mod_name_len = module_name.length();
if (!is_same && mod_name_len > 0) {
//check Wow64
char path_copy[MAX_PATH] = { 0 };
memcpy(path_copy, this->szModName, mod_name_len);
convert_to_wow64_path(path_copy);
is_same = (to_lowercase(mapped_name) == to_lowercase(path_copy));
if (is_same) {
this->switchToWow64Path();
return true;
}
}
return false;
}

bool pesieve::ModuleData::switchToWow64Path()
{
BOOL isWow64 = FALSE;
Expand All @@ -165,10 +209,17 @@ bool pesieve::ModuleData::reloadWow64()

//reload it and check again...
peconv::free_pe_buffer(original_module, original_size);
original_module = peconv::load_pe_module(szModName, original_size, false, false);
if (this->useCache) {
original_module = cache.loadCached(szModName, original_size);
}
else {
original_module = peconv::load_pe_module(szModName, original_size, false, false);
}
if (!original_module) {
std::cout << "[-] Failed to reload: " << szModName << "\n";
return false;
}
std::cout << "[+] Reloaded: " << szModName << "\n";
return true;
}

Expand Down
8 changes: 8 additions & 0 deletions scanners/module_data.h
Expand Up @@ -95,6 +95,7 @@ namespace pesieve {
bool relocateToBase(ULONGLONG new_base);
bool loadRelocatedFields(std::set<DWORD>& fields_rvas);
bool loadImportThunks(std::set<DWORD>& fields_rvas);
bool loadImportsList(peconv::ImportsCollection &collection);

HANDLE processHandle;
HMODULE moduleHandle;
Expand All @@ -107,6 +108,7 @@ namespace pesieve {
protected:
bool _loadOriginal(bool disableFSredir);
bool loadModuleName();
bool autoswichIfWow64Mapping();
bool isDotNetManagedCode();

bool is_dot_net;
Expand Down Expand Up @@ -148,6 +150,12 @@ namespace pesieve {
return true;
}

bool is64bit()
{
if (!isHdrReady) return 0;
return peconv::is64bit(headerBuffer);
}

size_t getHdrImageSize()
{
if (!isHdrReady) return 0;
Expand Down
9 changes: 8 additions & 1 deletion scanners/scanner.cpp
Expand Up @@ -353,7 +353,10 @@ size_t pesieve::ProcessScanner::scanModulesIATs(ProcessScanReport &pReport) //th
if (modules_count == 0) {
return 0;
}

if (!args.quiet) {
std::cout << "Scanning for IAT hooks: " << modules_count << " modules." << std::endl;
}
ULONGLONG start_tick = GetTickCount64();
size_t counter = 0;
for (counter = 0; counter < modules_count; counter++) {
if (processHandle == nullptr) break;
Expand All @@ -380,5 +383,9 @@ size_t pesieve::ProcessScanner::scanModulesIATs(ProcessScanReport &pReport) //th
continue;
}
}
if (!args.quiet) {
ULONGLONG total_time = GetTickCount64() - start_tick;
std::cout << "[*] IATs scanned in " << std::dec << total_time << " ms" << std::endl;
}
return counter;
}

0 comments on commit 74100b5

Please sign in to comment.