C++ Optimized pattern search

Модератор форума
Модератор
Статус
Оффлайн
Регистрация
19 Май 2018
Сообщения
954
Реакции[?]
1,067
Поинты[?]
20K
Всем привет и сегодня мы будем оптимизировать поиск паттернов в вашем чите
Оптимизация заключается в том, что мы будем сканировать только секции с кодом вместо всего модуля

Некоторые структуры:
C++:
struct CModule
{
    DWORD64 m_dwBase = 0;
    DWORD m_dwSize = 0;

    void Initialize( const wchar_t* m_wszModuleName );
};   

template< typename T, int m_iSize = 512 >
struct CVector
{
    private:
    int m_nSize;
    T m_aData[ m_iSize ];

    public:
    CVector< T, m_iSize >( )
    {
        m_nSize = 0;
        ZeroMemory( m_aData, sizeof( T ) * m_iSize );
    }

    void Push( T m_iValue )
    {
        m_aData[ m_nSize++ ] = m_iValue;
    }

    T* Data( )
    {
        return m_aData;
    }

    int Size( )
    {
        return m_nSize;
    }

    T& operator[]( int m_iIndex )
    {
        return m_aData[ m_iIndex ];
    }
};
Приколы для структуры CModule
C++:
DWORD64 Utils::GetModuleBase( const wchar_t* m_wszModuleName )
{
    for ( PLIST_ENTRY m_pListEntry = reinterpret_cast< PLIST_ENTRY64 >( m_pPEB->Ldr->InLoadOrderModuleList.Flink );
        m_pListEntry != &m_pPEB->Ldr->InLoadOrderModuleList;
        m_pListEntry = reinterpret_cast< PLIST_ENTRY >( m_pListEntry->Flink ) )
    {
        PLDR_DATA_TABLE_ENTRY m_pEntry = CONTAINING_RECORD( m_pListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks );

        if ( m_pEntry->BaseDllName.Buffer && !Utils::wcsicmp( m_wszModuleName, m_pEntry->BaseDllName.Buffer ) )
            return m_pEntry->DllBase;
    }

    return 0;
}

DWORD Utils::GetModuleSize( DWORD64 m_dwImageBase )
{
    if ( !m_dwImageBase )
        return 0;

    PIMAGE_DOS_HEADER m_pDos = reinterpret_cast< PIMAGE_DOS_HEADER >( m_dwImageBase );
    return reinterpret_cast< PIMAGE_NT_HEADERS >( m_dwImageBase + m_pDos->e_lfanew )->OptionalHeader.SizeOfImage;
}

void Utils::CModule::Initialize( const wchar_t* m_wszModuleName )
{
    m_dwBase = Utils::GetModuleBase( m_wszModuleName );
    m_dwSize = Utils::GetModuleSize( m_dwBase );

    Log( "[+] %ws info: 0x%llX \t0x%lX\n", m_wszModuleName, m_dwBase, m_dwSize );
}
Сам поиск паттерна:
C++:
void PatternToBytes( Utils::CVector< int >& m_aBytes, const char* m_szPattern )
{
    char* m_pStart = const_cast< char* >( m_szPattern );
    char* m_pEnd = const_cast< char* >( m_szPattern ) + Utils::strlen( m_szPattern );

    for ( char* m_pCurrent = m_pStart; m_pCurrent < m_pEnd; ++m_pCurrent )
    {
        if ( *m_pCurrent == '?' )
        {
            ++m_pCurrent;
            if ( *m_pCurrent == '?' )
                ++m_pCurrent;
            m_aBytes.Push( -1 );
        }
        else
        {
            m_aBytes.Push( Utils::strtoul( m_pCurrent, &m_pCurrent, 16 ) );
        }
    }
}

DWORD64 Utils::FindPattern( CModule m_Module, const char* m_szPattern )
{
    DWORD m_dwSizeOfImage = m_Module.m_dwSize;
    CVector< int > m_aBytes = CVector< int >( );
    PatternToBytes( m_aBytes, m_szPattern );

    int m_dwSize = m_aBytes.Size( );

    IMAGE_NT_HEADERS* m_pNt = reinterpret_cast< IMAGE_NT_HEADERS* >( m_Module.m_dwBase +
        reinterpret_cast< IMAGE_DOS_HEADER* >( m_Module.m_dwBase )->e_lfanew );

    CVector< IMAGE_SECTION_HEADER*, 10 > m_aSections; // ну 10 и 10 че бухтеть то
    IMAGE_SECTION_HEADER* m_pCurrentSection = IMAGE_FIRST_SECTION( m_pNt );
    for ( int i = 0; i != m_pNt->FileHeader.NumberOfSections; ++i, ++m_pCurrentSection )
        if ( m_pCurrentSection->Characteristics & IMAGE_SCN_CNT_CODE ||
            m_pCurrentSection->Characteristics & IMAGE_SCN_MEM_EXECUTE )
            m_aSections.Push( m_pCurrentSection );

    for ( int c = 0; c < m_aSections.Size( ); ++c )
    {
        BYTE* m_pScanBytes = reinterpret_cast< BYTE* >( m_Module.m_dwBase + m_aSections[ c ]->VirtualAddress );

        for ( DWORD i = 0; i < m_aSections[ c ]->SizeOfRawData - m_dwSize; ++i )
        {
            bool m_bFound = true;

            for ( int j = 0; j < m_dwSize; ++j )
            {
                if ( m_pScanBytes[ i + j ] != m_aBytes[ j ] && m_aBytes[ j ] != -1 )
                {
                    m_bFound = false;
                    break;
                }
            }

            if ( m_bFound )
                return ( DWORD64 )( &m_pScanBytes[ i ] );
        }
    }   

    return 0;
}
Ради универсальности под разные игры секции выбираются по их характеристикам, а не названию

Плюсы или минусы хуй знает мне похуй:
  • отсутствие CRT
  • нет вызовов WinAPI

Кто не знает как получать PEB:
m_pPEB __readfsdword( 0x30 ); // x86
m_pPEB = __readgsqword( 0x60 ); // x64

Изначально делалось для x64, но на x86 проблем быть не должно
 
bruh
Участник
Статус
Оффлайн
Регистрация
15 Апр 2017
Сообщения
1,299
Реакции[?]
365
Поинты[?]
0
PatternToBytes кстати может быть превращен в constexpr функцию, чтобы экономить количество строчек в дллке и не тратить лишние наносекунды в рантайме
 
Keine panik!
Эксперт
Статус
Оффлайн
Регистрация
29 Апр 2020
Сообщения
812
Реакции[?]
417
Поинты[?]
49K
- Зачем заполнять m_aSections в вектор если можно сразу для каждой секции проводить поиск без этого контейнера вообще.
- Очень медленное сравнение, эти рассчеты смещений гораздо проще заменить на прямые указатели, кроме того для скорости нужно сперва сравнивать первый байт и только затем входить в цикл.
- Никаких комментариев, для именования ЛЮБЫХ переменных используется префикс m_, когда это придумали делать чтобы показать лишь что это переменная класса.
 
Сверху Снизу