2022-04-10 19:59:34 +02:00
//===========================================================================//
//
// Purpose: Implementation of the CModule class.
//
//===========================================================================//
# include "core/stdafx.h"
2022-08-09 17:18:07 +02:00
# include "public/utility/utility.h"
# include "public/utility/memaddr.h"
2022-04-10 19:59:34 +02:00
//-----------------------------------------------------------------------------
// Purpose: constructor
// Input : *svModuleName
//-----------------------------------------------------------------------------
CModule : : CModule ( const string & svModuleName ) : m_svModuleName ( svModuleName )
{
const MODULEINFO mInfo = GetModuleInfo ( svModuleName . c_str ( ) ) ;
m_nModuleSize = static_cast < size_t > ( mInfo . SizeOfImage ) ;
m_pModuleBase = reinterpret_cast < uintptr_t > ( mInfo . lpBaseOfDll ) ;
m_pDOSHeader = reinterpret_cast < IMAGE_DOS_HEADER * > ( m_pModuleBase ) ;
m_pNTHeaders = reinterpret_cast < IMAGE_NT_HEADERS64 * > ( m_pModuleBase + m_pDOSHeader - > e_lfanew ) ;
const IMAGE_SECTION_HEADER * hSection = IMAGE_FIRST_SECTION ( m_pNTHeaders ) ; // Get first image section.
for ( WORD i = 0 ; i < m_pNTHeaders - > FileHeader . NumberOfSections ; i + + ) // Loop through the sections.
{
const IMAGE_SECTION_HEADER & hCurrentSection = hSection [ i ] ; // Get current section.
m_vModuleSections . push_back ( ModuleSections_t ( string ( reinterpret_cast < const char * > ( hCurrentSection . Name ) ) ,
static_cast < uintptr_t > ( m_pModuleBase + hCurrentSection . VirtualAddress ) , hCurrentSection . SizeOfRawData ) ) ; // Push back a struct with the section data.
}
2022-06-13 23:34:06 +02:00
m_ExecutableCode = GetSectionByName ( " .text " ) ;
m_ExceptionTable = GetSectionByName ( " .pdata " ) ;
m_RunTimeData = GetSectionByName ( " .data " ) ;
m_ReadOnlyData = GetSectionByName ( " .rdata " ) ;
2022-04-10 19:59:34 +02:00
}
2022-12-03 00:08:48 +01:00
# ifndef PLUGINSDK
2022-04-10 19:59:34 +02:00
//-----------------------------------------------------------------------------
// Purpose: find array of bytes in process memory using SIMD instructions
// Input : *szPattern -
// *szMask -
// Output : CMemory
//-----------------------------------------------------------------------------
2022-12-20 23:38:45 +01:00
CMemory CModule : : FindPatternSIMD ( const uint8_t * szPattern , const char * szMask , const ModuleSections_t * moduleSection , const uint32_t nOccurrence ) const
2022-04-10 19:59:34 +02:00
{
2022-06-13 23:34:06 +02:00
if ( ! m_ExecutableCode . IsSectionValid ( ) )
2022-04-10 19:59:34 +02:00
return CMemory ( ) ;
2022-12-20 23:38:45 +01:00
const bool bSectionValid = moduleSection ? moduleSection - > IsSectionValid ( ) : false ;
2022-07-20 18:03:35 +02:00
2022-12-20 23:38:45 +01:00
const uintptr_t nBase = bSectionValid ? moduleSection - > m_pSectionBase : m_ExecutableCode . m_pSectionBase ;
const uintptr_t nSize = bSectionValid ? moduleSection - > m_nSectionSize : m_ExecutableCode . m_nSectionSize ;
2022-04-10 19:59:34 +02:00
2022-12-01 22:47:39 +01:00
const size_t nMaskLen = strlen ( szMask ) ;
2022-04-10 19:59:34 +02:00
const uint8_t * pData = reinterpret_cast < uint8_t * > ( nBase ) ;
2022-12-05 00:57:48 +01:00
const uint8_t * pEnd = pData + nSize - nMaskLen ;
2022-04-10 19:59:34 +02:00
2022-09-09 19:47:31 +02:00
int nOccurrenceCount = 0 ;
2022-04-10 19:59:34 +02:00
int nMasks [ 64 ] ; // 64*16 = enough masks for 1024 bytes.
2022-12-01 22:47:39 +01:00
const int iNumMasks = static_cast < int > ( ceil ( static_cast < float > ( nMaskLen ) / 16.f ) ) ;
2022-04-10 19:59:34 +02:00
memset ( nMasks , ' \0 ' , iNumMasks * sizeof ( int ) ) ;
for ( intptr_t i = 0 ; i < iNumMasks ; + + i )
{
for ( intptr_t j = strnlen ( szMask + i * 16 , 16 ) - 1 ; j > = 0 ; - - j )
{
if ( szMask [ i * 16 + j ] = = ' x ' )
{
2022-11-24 15:41:52 +01:00
_bittestandset ( reinterpret_cast < LONG * > ( & nMasks [ i ] ) , static_cast < LONG > ( j ) ) ;
2022-04-10 19:59:34 +02:00
}
}
}
2022-07-15 19:43:29 +02:00
const __m128i xmm1 = _mm_loadu_si128 ( reinterpret_cast < const __m128i * > ( szPattern ) ) ;
2022-04-10 19:59:34 +02:00
__m128i xmm2 , xmm3 , msks ;
for ( ; pData ! = pEnd ; _mm_prefetch ( reinterpret_cast < const char * > ( + + pData + 64 ) , _MM_HINT_NTA ) )
{
if ( szPattern [ 0 ] = = pData [ 0 ] )
{
xmm2 = _mm_loadu_si128 ( reinterpret_cast < const __m128i * > ( pData ) ) ;
msks = _mm_cmpeq_epi8 ( xmm1 , xmm2 ) ;
if ( ( _mm_movemask_epi8 ( msks ) & nMasks [ 0 ] ) = = nMasks [ 0 ] )
{
for ( uintptr_t i = 1 ; i < static_cast < uintptr_t > ( iNumMasks ) ; + + i )
{
xmm2 = _mm_loadu_si128 ( reinterpret_cast < const __m128i * > ( ( pData + i * 16 ) ) ) ;
xmm3 = _mm_loadu_si128 ( reinterpret_cast < const __m128i * > ( ( szPattern + i * 16 ) ) ) ;
msks = _mm_cmpeq_epi8 ( xmm2 , xmm3 ) ;
if ( ( _mm_movemask_epi8 ( msks ) & nMasks [ i ] ) = = nMasks [ i ] )
{
if ( ( i + 1 ) = = iNumMasks )
{
2022-09-09 19:47:31 +02:00
if ( nOccurrenceCount = = nOccurrence )
2022-08-15 21:05:42 +02:00
{
return static_cast < CMemory > ( const_cast < uint8_t * > ( pData ) ) ;
}
2022-09-09 19:47:31 +02:00
nOccurrenceCount + + ;
2022-04-10 19:59:34 +02:00
}
}
else
{
goto cont ;
}
}
2022-09-09 19:47:31 +02:00
if ( nOccurrenceCount = = nOccurrence )
2022-08-15 21:05:42 +02:00
{
return static_cast < CMemory > ( ( & * ( const_cast < uint8_t * > ( pData ) ) ) ) ;
}
2022-09-09 19:47:31 +02:00
nOccurrenceCount + + ;
2022-04-10 19:59:34 +02:00
}
} cont : ;
}
return CMemory ( ) ;
}
2022-06-25 00:08:02 +02:00
//-----------------------------------------------------------------------------
2022-12-02 10:37:41 +01:00
// Purpose: find a string pattern in process memory using SIMD instructions
// Input : &svPattern
// &moduleSection
2022-06-25 00:08:02 +02:00
// Output : CMemory
//-----------------------------------------------------------------------------
2022-12-20 23:38:45 +01:00
CMemory CModule : : FindPatternSIMD ( const string & svPattern , const ModuleSections_t * moduleSection ) const
2022-06-25 00:08:02 +02:00
{
2022-12-02 10:37:41 +01:00
uint64_t nRVA ;
if ( g_SigCache . FindEntry ( svPattern , nRVA ) )
2022-12-02 00:30:49 +01:00
{
2022-12-02 10:37:41 +01:00
return CMemory ( nRVA + GetModuleBase ( ) ) ;
2022-12-02 00:30:49 +01:00
}
2022-06-26 17:45:54 +02:00
const pair patternInfo = PatternToMaskedBytes ( svPattern ) ;
2022-12-05 00:57:48 +01:00
const CMemory memory = FindPatternSIMD ( patternInfo . first . data ( ) , patternInfo . second . c_str ( ) , moduleSection ) ;
2022-12-02 00:30:49 +01:00
g_SigCache . AddEntry ( svPattern , GetRVA ( memory . GetPtr ( ) ) ) ;
return memory ;
2022-06-25 00:08:02 +02:00
}
2022-04-10 19:59:34 +02:00
//-----------------------------------------------------------------------------
// Purpose: find address of input string constant in read only memory
// Input : *svString -
// bNullTerminator -
// Output : CMemory
//-----------------------------------------------------------------------------
CMemory CModule : : FindStringReadOnly ( const string & svString , bool bNullTerminator ) const
{
2022-06-13 23:34:06 +02:00
if ( ! m_ReadOnlyData . IsSectionValid ( ) )
2022-04-10 19:59:34 +02:00
return CMemory ( ) ;
2022-12-02 11:10:53 +01:00
uint64_t nRVA ;
if ( g_SigCache . FindEntry ( svString , nRVA ) )
{
return CMemory ( nRVA + GetModuleBase ( ) ) ;
}
2022-07-15 19:43:29 +02:00
const vector < int > vBytes = StringToBytes ( svString , bNullTerminator ) ; // Convert our string to a byte array.
2022-04-10 19:59:34 +02:00
const pair bytesInfo = std : : make_pair ( vBytes . size ( ) , vBytes . data ( ) ) ; // Get the size and data of our bytes.
2022-12-05 00:57:48 +01:00
const uint8_t * pBase = reinterpret_cast < uint8_t * > ( m_ReadOnlyData . m_pSectionBase ) ; // Get start of .rdata section.
2022-04-10 19:59:34 +02:00
2022-06-13 23:34:06 +02:00
for ( size_t i = 0ull ; i < m_ReadOnlyData . m_nSectionSize - bytesInfo . first ; i + + )
2022-04-10 19:59:34 +02:00
{
bool bFound = true ;
// If either the current byte equals to the byte in our pattern or our current byte in the pattern is a wildcard
// our if clause will be false.
for ( size_t j = 0ull ; j < bytesInfo . first ; j + + )
{
if ( pBase [ i + j ] ! = bytesInfo . second [ j ] & & bytesInfo . second [ j ] ! = - 1 )
{
bFound = false ;
break ;
}
}
if ( bFound )
{
2022-12-02 11:10:53 +01:00
CMemory result = CMemory ( & pBase [ i ] ) ;
g_SigCache . AddEntry ( svString , GetRVA ( result . GetPtr ( ) ) ) ;
return result ;
2022-04-10 19:59:34 +02:00
}
}
return CMemory ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: find address of reference to string constant in executable memory
// Input : *svString -
// bNullTerminator -
// Output : CMemory
//-----------------------------------------------------------------------------
2022-09-09 19:47:31 +02:00
CMemory CModule : : FindString ( const string & svString , const ptrdiff_t nOccurrence , bool bNullTerminator ) const
2022-04-10 19:59:34 +02:00
{
2022-06-13 23:34:06 +02:00
if ( ! m_ExecutableCode . IsSectionValid ( ) )
2022-04-10 19:59:34 +02:00
return CMemory ( ) ;
2022-12-02 10:37:41 +01:00
uint64_t nRVA ;
string svPackedString = svString + std : : to_string ( nOccurrence ) ;
if ( g_SigCache . FindEntry ( svPackedString , nRVA ) )
{
return CMemory ( nRVA + GetModuleBase ( ) ) ;
}
2022-07-15 19:43:29 +02:00
const CMemory stringAddress = FindStringReadOnly ( svString , bNullTerminator ) ; // Get Address for the string in the .rdata section.
2022-08-15 21:05:42 +02:00
2022-04-10 19:59:34 +02:00
if ( ! stringAddress )
return CMemory ( ) ;
2022-09-09 19:47:31 +02:00
uint8_t * pLatestOccurrence = nullptr ;
2022-06-13 23:34:06 +02:00
uint8_t * pTextStart = reinterpret_cast < uint8_t * > ( m_ExecutableCode . m_pSectionBase ) ; // Get the start of the .text section.
2022-09-09 19:47:31 +02:00
ptrdiff_t dOccurrencesFound = 0 ;
2022-12-02 10:37:41 +01:00
CMemory resultAddress ;
2022-04-10 19:59:34 +02:00
2022-06-13 23:34:06 +02:00
for ( size_t i = 0ull ; i < m_ExecutableCode . m_nSectionSize - 0x5 ; i + + )
2022-04-10 19:59:34 +02:00
{
byte byte = pTextStart [ i ] ;
if ( byte = = LEA )
{
2022-07-15 19:43:29 +02:00
const CMemory skipOpCode = CMemory ( reinterpret_cast < uintptr_t > ( & pTextStart [ i ] ) ) . OffsetSelf ( 0x2 ) ; // Skip next 2 opcodes, those being the instruction and the register.
const int32_t relativeAddress = skipOpCode . GetValue < int32_t > ( ) ; // Get 4-byte long string relative Address
const uintptr_t nextInstruction = skipOpCode . Offset ( 0x4 ) . GetPtr ( ) ; // Get location of next instruction.
const CMemory potentialLocation = CMemory ( nextInstruction + relativeAddress ) ; // Get potential string location.
2022-04-10 19:59:34 +02:00
if ( potentialLocation = = stringAddress )
{
2022-09-09 19:47:31 +02:00
dOccurrencesFound + + ;
if ( nOccurrence = = dOccurrencesFound )
2022-12-02 10:37:41 +01:00
{
resultAddress = CMemory ( & pTextStart [ i ] ) ;
g_SigCache . AddEntry ( svPackedString , GetRVA ( resultAddress . GetPtr ( ) ) ) ;
return resultAddress ;
}
2022-04-10 19:59:34 +02:00
2022-09-09 19:47:31 +02:00
pLatestOccurrence = & pTextStart [ i ] ; // Stash latest occurrence.
2022-04-10 19:59:34 +02:00
}
}
}
2022-12-02 10:37:41 +01:00
resultAddress = CMemory ( pLatestOccurrence ) ;
g_SigCache . AddEntry ( svPackedString , GetRVA ( resultAddress . GetPtr ( ) ) ) ;
2022-12-02 11:10:53 +01:00
2022-12-02 10:37:41 +01:00
return resultAddress ;
2022-04-10 19:59:34 +02:00
}
2022-12-03 00:08:48 +01:00
//-----------------------------------------------------------------------------
// Purpose: get address of a virtual method table by rtti type descriptor name.
// Input : *svTableName -
// nRefIndex -
// Output : CMemory
//-----------------------------------------------------------------------------
CMemory CModule : : GetVirtualMethodTable ( const string & svTableName , const uint32_t nRefIndex )
{
uint64_t nRVA ; // Packed together as we can have multiple VFTable searches, but with different ref indexes.
string svPackedTableName = svTableName + std : : to_string ( nRefIndex ) ;
if ( g_SigCache . FindEntry ( svPackedTableName , nRVA ) )
{
return CMemory ( nRVA + GetModuleBase ( ) ) ;
}
2022-12-20 23:38:45 +01:00
ModuleSections_t moduleSection = { " .data " , m_RunTimeData . m_pSectionBase , m_RunTimeData . m_nSectionSize } ;
2022-12-03 00:08:48 +01:00
const auto tableNameInfo = StringToMaskedBytes ( svTableName , false ) ;
2022-12-20 23:38:45 +01:00
CMemory rttiTypeDescriptor = FindPatternSIMD ( tableNameInfo . first . data ( ) , tableNameInfo . second . c_str ( ) , & moduleSection ) . OffsetSelf ( - 0x10 ) ;
2022-12-03 00:08:48 +01:00
if ( ! rttiTypeDescriptor )
return CMemory ( ) ;
uintptr_t scanStart = m_ReadOnlyData . m_pSectionBase ; // Get the start address of our scan.
const uintptr_t scanEnd = ( m_ReadOnlyData . m_pSectionBase + m_ReadOnlyData . m_nSectionSize ) - 0x4 ; // Calculate the end of our scan.
const uintptr_t rttiTDRva = rttiTypeDescriptor . GetPtr ( ) - m_pModuleBase ; // The RTTI gets referenced by a 4-Byte RVA address. We need to scan for that address.
while ( scanStart < scanEnd )
{
2022-12-20 23:38:45 +01:00
moduleSection = { " .rdata " , scanStart , m_ReadOnlyData . m_nSectionSize } ;
CMemory reference = FindPatternSIMD ( reinterpret_cast < rsig_t > ( & rttiTDRva ) , " xxxx " , & moduleSection , nRefIndex ) ;
2022-12-03 00:08:48 +01:00
if ( ! reference )
break ;
CMemory referenceOffset = reference . Offset ( - 0xC ) ;
if ( referenceOffset . GetValue < int32_t > ( ) ! = 1 ) // Check if we got a RTTI Object Locator for this reference by checking if -0xC is 1, which is the 'signature' field which is always 1 on x64.
{
scanStart = reference . Offset ( 0x4 ) . GetPtr ( ) ; // Set location to current reference + 0x4 so we avoid pushing it back again into the vector.
continue ;
}
2022-12-20 23:38:45 +01:00
moduleSection = { " .rdata " , m_ReadOnlyData . m_pSectionBase , m_ReadOnlyData . m_nSectionSize } ;
CMemory vfTable = FindPatternSIMD ( reinterpret_cast < rsig_t > ( & referenceOffset ) , " xxxxxxxx " , & moduleSection ) . OffsetSelf ( 0x8 ) ;
2022-12-03 00:08:48 +01:00
g_SigCache . AddEntry ( svPackedTableName , GetRVA ( vfTable . GetPtr ( ) ) ) ;
return vfTable ;
}
return CMemory ( ) ;
2022-04-10 19:59:34 +02:00
}
2022-12-03 00:08:48 +01:00
# endif // !PLUGINSDK
2022-04-10 19:59:34 +02:00
//-----------------------------------------------------------------------------
// Purpose: get address of exported function in this module
// Input : *svFunctionName -
// bNullTerminator -
// Output : CMemory
//-----------------------------------------------------------------------------
CMemory CModule : : GetExportedFunction ( const string & svFunctionName ) const
{
if ( ! m_pDOSHeader | | m_pDOSHeader - > e_magic ! = IMAGE_DOS_SIGNATURE ) // Is dosHeader valid?
return CMemory ( ) ;
if ( ! m_pNTHeaders | | m_pNTHeaders - > Signature ! = IMAGE_NT_SIGNATURE ) // Is ntHeader valid?
return CMemory ( ) ;
2022-07-20 11:27:42 +02:00
// Get the location of IMAGE_EXPORT_DIRECTORY for this module by adding the IMAGE_DIRECTORY_ENTRY_EXPORT relative virtual address onto our module base address.
2022-07-15 19:43:29 +02:00
const IMAGE_EXPORT_DIRECTORY * pImageExportDirectory = reinterpret_cast < IMAGE_EXPORT_DIRECTORY * > ( m_pModuleBase + m_pNTHeaders - > OptionalHeader . DataDirectory [ IMAGE_DIRECTORY_ENTRY_EXPORT ] . VirtualAddress ) ;
2022-04-10 19:59:34 +02:00
if ( ! pImageExportDirectory )
return CMemory ( ) ;
// Are there any exported functions?
if ( ! pImageExportDirectory - > NumberOfFunctions )
return CMemory ( ) ;
2022-07-20 11:27:42 +02:00
// Get the location of the functions via adding the relative virtual address from the struct into our module base address.
2022-07-15 19:43:29 +02:00
const DWORD * pAddressOfFunctions = reinterpret_cast < DWORD * > ( m_pModuleBase + pImageExportDirectory - > AddressOfFunctions ) ;
2022-04-10 19:59:34 +02:00
if ( ! pAddressOfFunctions )
return CMemory ( ) ;
2022-07-20 11:27:42 +02:00
// Get the names of the functions via adding the relative virtual address from the struct into our module base Address.
2022-07-15 19:43:29 +02:00
const DWORD * pAddressOfName = reinterpret_cast < DWORD * > ( m_pModuleBase + pImageExportDirectory - > AddressOfNames ) ;
2022-04-10 19:59:34 +02:00
if ( ! pAddressOfName )
return CMemory ( ) ;
2022-07-20 11:27:42 +02:00
// Get the ordinals of the functions via adding the relative virtual Address from the struct into our module base address.
2022-04-10 19:59:34 +02:00
DWORD * pAddressOfOrdinals = reinterpret_cast < DWORD * > ( m_pModuleBase + pImageExportDirectory - > AddressOfNameOrdinals ) ;
if ( ! pAddressOfOrdinals )
return CMemory ( ) ;
for ( DWORD i = 0 ; i < pImageExportDirectory - > NumberOfFunctions ; i + + ) // Iterate through all the functions.
{
// Get virtual relative Address of the function name. Then add module base Address to get the actual location.
string ExportFunctionName = reinterpret_cast < char * > ( reinterpret_cast < DWORD * > ( m_pModuleBase + pAddressOfName [ i ] ) ) ;
if ( ExportFunctionName . compare ( svFunctionName ) = = 0 ) // Is this our wanted exported function?
{
2022-07-20 11:27:42 +02:00
// Get the function ordinal. Then grab the relative virtual address of our wanted function. Then add module base address so we get the actual location.
return CMemory ( m_pModuleBase + pAddressOfFunctions [ reinterpret_cast < WORD * > ( pAddressOfOrdinals ) [ i ] ] ) ; // Return as CMemory class.
2022-04-10 19:59:34 +02:00
}
}
return CMemory ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: get the module section by name (example: '.rdata', '.text')
// Input : *svModuleName -
// Output : ModuleSections_t
//-----------------------------------------------------------------------------
CModule : : ModuleSections_t CModule : : GetSectionByName ( const string & svSectionName ) const
{
2022-12-04 00:01:31 +01:00
for ( const ModuleSections_t & section : m_vModuleSections )
2022-04-10 19:59:34 +02:00
{
2022-12-04 00:01:31 +01:00
if ( section . m_svSectionName = = svSectionName )
return section ;
2022-04-10 19:59:34 +02:00
}
return ModuleSections_t ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: returns the module base
//-----------------------------------------------------------------------------
uintptr_t CModule : : GetModuleBase ( void ) const
{
return m_pModuleBase ;
}
2022-05-03 02:37:37 +02:00
//-----------------------------------------------------------------------------
// Purpose: returns the module size
//-----------------------------------------------------------------------------
DWORD CModule : : GetModuleSize ( void ) const
{
return m_nModuleSize ;
}
2022-04-10 19:59:34 +02:00
//-----------------------------------------------------------------------------
// Purpose: returns the module name
//-----------------------------------------------------------------------------
string CModule : : GetModuleName ( void ) const
{
return m_svModuleName ;
}
2022-12-02 00:30:49 +01:00
//-----------------------------------------------------------------------------
// Purpose: returns the RVA of given address
//-----------------------------------------------------------------------------
uintptr_t CModule : : GetRVA ( const uintptr_t nAddress ) const
{
return ( nAddress - GetModuleBase ( ) ) ;
}