Шалом, это мой первый тред на борде.
И решил немного доработать старый исходник, который проходится по функциям IAT (Import Address Table), проверяя их на опкоды джампа, и брейкпоинтов x32dbg (int3, int 0x3, ud2), Ordinal, кстати, он не проверяет, но можете допилить за меня
Такой приём будет очень полезен против новичков в вашем лоадере, которые любят ставить бряки куда попало без предварительного динамического анализа защиты :)
Для незнающих скажу, что если ваша функция накрыта виртуализацией кода, которая за собой как бонус будет вызывать функцию по своему функцию и и на этот адрес нет никаких других внешних вызовов в вашей программе, то этот чек пользователь спокойно пройдет, поскольку данной функции не будет в IAT.
Вот и сам исходник:
Пример его использования:
Результат использования функции:
Также забыл сказать, что функция _initialize_narrow_environment будет находится в списке, поскольку первый её байт - это 0xE9. Для неё можно спокойно сделать проверку.
Полный исходный код находится на гитхабе: https://github.com/colby57/IAT-Scanner
Всем пока!
И решил немного доработать старый исходник, который проходится по функциям IAT (Import Address Table), проверяя их на опкоды джампа, и брейкпоинтов x32dbg (int3, int 0x3, ud2), Ordinal, кстати, он не проверяет, но можете допилить за меня
Такой приём будет очень полезен против новичков в вашем лоадере, которые любят ставить бряки куда попало без предварительного динамического анализа защиты :)
Для незнающих скажу, что если ваша функция накрыта виртуализацией кода, которая за собой как бонус будет вызывать функцию по своему функцию и и на этот адрес нет никаких других внешних вызовов в вашей программе, то этот чек пользователь спокойно пройдет, поскольку данной функции не будет в IAT.
Вот и сам исходник:
IATScan.hpp:
#pragma once
#include <Windows.h>
#include <string>
#include <vector>
#include <iostream>
namespace Engine
{
struct S_CorruptedFunction
{
std::string m_cModuleName;
std::string m_cFunctionName;
std::uintptr_t m_pAddress;
S_CorruptedFunction( std::string cModule, std::string cFunc, std::uintptr_t pAddress ): m_cModuleName( std::move( cModule ) ), m_cFunctionName( std::move( cFunc ) ), m_pAddress( std::move( pAddress ) )
{
}
};
inline BYTE bInt3Breakpoint = 0xCC;
inline BYTE bJumpOpcode = 0xE9;
inline WORD wUd2Breakpoint = 0x0B0F;
inline WORD wInt3Breakpoint = 0x03CD;
inline std::vector<S_CorruptedFunction> m_cCorruptedFunctions {};
void OutputCorruptedFunctions();
void AddFunction( const S_CorruptedFunction& cCorruptedFunctions );
bool IATScan();
}
IATScan.cpp:
#include "IATScan.hpp"
void Engine::OutputCorruptedFunctions()
{
if ( m_cCorruptedFunctions.empty() )
{
printf( "[~] No corrupted functions found!\n" );
return;
}
for ( const auto& Iterator : m_cCorruptedFunctions )
printf( "[!] Module: %s\tFunction: %s\tAddress: 0x%p\n", Iterator.m_cModuleName.c_str(), Iterator.m_cFunctionName.c_str(), Iterator.m_pAddress );
}
void Engine::AddFunction( const S_CorruptedFunction& cCorruptedFunctions )
{
m_cCorruptedFunctions.push_back( cCorruptedFunctions );
}
bool Engine::IATScan()
{
LPVOID lpBaseAddress = (LPVOID) GetModuleHandle( NULL );
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS pNtHeader;
IMAGE_OPTIONAL_HEADER pOptionalHeader;
IMAGE_DATA_DIRECTORY pImportDirectory;
DWORD dwStartRVA;
PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
pDosHeader = (PIMAGE_DOS_HEADER) lpBaseAddress;
if ( pDosHeader->e_magic != IMAGE_DOS_SIGNATURE )
return false;
pNtHeader = (PIMAGE_NT_HEADERS) ( (DWORD_PTR) lpBaseAddress + pDosHeader->e_lfanew );
if ( pNtHeader->Signature != IMAGE_NT_SIGNATURE )
return false;
pOptionalHeader = pNtHeader->OptionalHeader;
pImportDirectory = pOptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ];
dwStartRVA = pImportDirectory.VirtualAddress;
pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR) ( (DWORD_PTR) lpBaseAddress + dwStartRVA );
if ( pImportDescriptor == NULL )
return false;
DWORD dwIndex = -1;
while ( pImportDescriptor[ ++dwIndex ].Characteristics != 0 )
{
PIMAGE_THUNK_DATA pOriginalFirstThunk;
PIMAGE_THUNK_DATA pFirstThunk;
char* pDllName = (char*) ( (DWORD_PTR) lpBaseAddress + pImportDescriptor[ dwIndex ].Name );
HMODULE hModule = GetModuleHandleA( pDllName );
pOriginalFirstThunk = (PIMAGE_THUNK_DATA) ( (DWORD_PTR) lpBaseAddress + pImportDescriptor[ dwIndex ].OriginalFirstThunk );
pFirstThunk = (PIMAGE_THUNK_DATA) ( (DWORD_PTR) lpBaseAddress + pImportDescriptor[ dwIndex ].FirstThunk );
if ( pOriginalFirstThunk == nullptr || pFirstThunk == nullptr )
return false;
while ( pOriginalFirstThunk->u1.AddressOfData )
{
if ( !( pOriginalFirstThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG ) )
{
PIMAGE_IMPORT_BY_NAME pImageImport = (PIMAGE_IMPORT_BY_NAME) ( (LPBYTE) lpBaseAddress + pOriginalFirstThunk->u1.AddressOfData );
auto pFn = GetProcAddress( hModule, (LPCSTR) pImageImport->Name );
if ( ( *(BYTE*) pFn == bInt3Breakpoint || *(BYTE*) pFn == bJumpOpcode )
|| ( *(WORD*) pFn == wUd2Breakpoint || *(WORD*) pFn == wInt3Breakpoint ) )
{
Engine::AddFunction( S_CorruptedFunction( std::string( pDllName ), std::string( pImageImport->Name ), (std::uintptr_t) pFn ) );
}
}
pFirstThunk++;
pOriginalFirstThunk++;
}
}
return true;
}
Пример его использования:
Entry.cpp:
#include "IATScan.hpp"
int main()
{
printf( "t.me/colby5engineering\n\n" );
if ( Engine::IATScan() == true )
Engine::OutputCorruptedFunctions();
else
printf( "[-] Failed to scan iat :(\n" );
std::cin.get();
return 0;
}
Результат использования функции:
Также забыл сказать, что функция _initialize_narrow_environment будет находится в списке, поскольку первый её байт - это 0xE9. Для неё можно спокойно сделать проверку.
Полный исходный код находится на гитхабе: https://github.com/colby57/IAT-Scanner
Всем пока!