Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > надежный способ идентифицировать программу.


Автор: boostcoder 15.7.2011, 06:50
всем привет.

ситуация такая:
написал античит для онлайн игрушки. античит периодично с сервера запрашивает инфу из БД известных читов(имя_файла + CRC_сумму + текст_заголовка_окна), периодично сканит систему на предмет поиска известных читов, и, если находит - прибивает. это все отлично работает.
но попался очень хитрый чит. а хитер он тем, что он доступен в исходниках, т.е. тупая перекомпиляция разными версиями компиляторов тупо произведет exe`шники с разной CRC-суммой. в добавок, в опциях можно самому настраивать/указывать текст заголовка.

вопрос: как идентифицировать такой чит?

всем спасибо.

Автор: ZVano 15.7.2011, 09:42
Цитата(boostcoder @  15.7.2011,  06:50 Найти цитируемый пост)
как идентифицировать такой чит?

Предположение: контрольная сумма названий экспортируемых функций в верхнем регистре и отсортированых по алфавиту.

Автор: boostcoder 15.7.2011, 09:44
Цитата(ZVano @  15.7.2011,  09:42 Найти цитируемый пост)
контрольная сумма названий экспортируемых функций в верхнем регистре и отсортированых по алфавиту.

вариант smile 
а не подскажите случайно, как имена можно извлечь без использования "инструмента" ?

Добавлено через 9 минут и 27 секунд
хотя... если чит доступен в исходниках, значит набор/имена экспортируемых функций могут часто меняться..

Автор: ZVano 15.7.2011, 09:58
Цитата(boostcoder @  15.7.2011,  09:44 Найти цитируемый пост)
ак имена можно извлечь без использования "инструмента" ?

Могу дать только направление.

Нужно прочитать таблицу импорта из заголовка PE -файла.
http://forum.sources.ru/index.php?showtopic=148144  исходник.
http://www.google.ru/search?hl=ru&newwindow=1&biw=1886&bih=924&q=%D0%BF%D1%80%D0%BE%D1%87%D0%B8%D1%82%D0%B0%D1%82%D1%8C+%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%83+%D0%B8%D0%BC%D0%BF%D0%BE%D1%80%D1%82%D0%B0+PE&oq=%D0%BF%D1%80%D0%BE%D1%87%D0%B8%D1%82%D0%B0%D1%82%D1%8C+%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%83+%D0%B8%D0%BC%D0%BF%D0%BE%D1%80%D1%82%D0%B0+PE&aq=f&aqi=&aql=&gs_sm=e&gs_upl=164410l170115l0l171499l11l11l0l0l0l1l268l1539l0.8.1l9 поисковый запрос гугла.

Автор: boostcoder 15.7.2011, 10:00
спасибо. ушел читать.

Добавлено через 28 секунд
может кто-то подкинет еще какую-то идею...

Автор: ASMatic 15.7.2011, 23:03
количество\относительное расположение окон(дочерных)...
С импортом - не особо хорошо считать контрольную сумму, я бы проверял на наличие некоторых ключевых импортируемых фунок + окна(например) - если все сошлось 99% что можно бить)
Но это все ничего не даст - до сраки, поправили код и уже нету фунок в импорте = поможет вам только ядро и неплохая логика, дабы читу не дать открыть процесс и т.п.
в2. - инжект во все создаваемые процессы и там уже перехват NtOpenProcess(), NtWriteVirtualMemory, etc = опять таки можно на ваши перехваты забить)

Вот сорс на си, почти что вам нужно...(по поводу импорта)

Код

PVOID
peGetProcAddrByModule(
    IN      PBYTE       Base,
    IN      DWORD       ProcHash
    )
{
    PIMAGE_NT_HEADERS32         ImageNtHeader;
    PIMAGE_EXPORT_DIRECTORY     ied;
    WCHAR       LibraryName[MAX_PATH];
    CHAR        buff[MAX_PATH];
    PULONG_PTR  Names,
                Functions;
    PUSHORT     NameOrdinals;
    PCHAR       Name;
    PVOID       Addr;
    ULONG       i;

    Addr = NULL;
    if (Base) {

        ImageNtHeader = (PIMAGE_NT_HEADERS32)( ((PIMAGE_DOS_HEADER)Base)->e_lfanew + (ULONG_PTR)Base );
        ied = (PIMAGE_EXPORT_DIRECTORY)ImageNtHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].VirtualAddress;
        if (ied) {

            ied = (PIMAGE_EXPORT_DIRECTORY)( (ULONG_PTR)ied + (ULONG_PTR)Base );
            Names = (PULONG_PTR)( (ULONG_PTR)Base + ied->AddressOfNames );
            Functions = (PULONG_PTR)( (ULONG_PTR)Base + ied->AddressOfFunctions );
            NameOrdinals = (PUSHORT)( (ULONG_PTR)Base + ied->AddressOfNameOrdinals );

            for (i = 0; i < ied->NumberOfNames; i++) {
                Name = (PCHAR)( (ULONG_PTR)Base + Names[i] );
                if (ProcHash == HashStr( Name )) {
                    Addr = (PVOID)( Functions[ NameOrdinals[i] ] + (ULONG_PTR)Base);

                    if ( ((ULONG_PTR)Addr >= (ULONG_PTR)ied)
                        && ((ULONG_PTR)Addr <= (ULONG_PTR)ied + ImageNtHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].Size)) {

                        //This is forwarded proc, begin load library and get proc addr
                        i = 0;
                        while (((PCHAR)Addr)[i] != '\0'){
                            buff[i] = ((PCHAR)Addr)[i];
                            if (((PCHAR)Addr)[i] == '.')
                                break;
                            i++;
                        }
                        *(PDWORD)&buff[++i] = '\0lld';

                        AtoW( LibraryName,
                              buff );

                        WcsToLower( LibraryName );
                        ProcHash = HashStr( &((PCHAR)Addr)[i] );
                        Addr = peGetProcAddr( LibraryName,
                                              ProcHash );
                    }
                    break;
                }
            }
        }
    }

    return Addr;
}

Автор: boostcoder 15.7.2011, 23:05
Цитата(ASMatic @  15.7.2011,  23:03 Найти цитируемый пост)
инжект во все создаваемые процессы и там уже перехват NtOpenProcess(), NtWriteVirtualMemory, etc = опять таки можно на ваши перехваты забить)

слишком сложно для меня. не силен я в системном программировании для виндоус :(

Автор: ASMatic 15.7.2011, 23:08
Цитата(boostcoder @  15.7.2011,  21:05 Найти цитируемый пост)
 не силен я в системном программировании для виндоус

трудновато тогды будет бороться с читами.)

Автор: Dik0n 16.7.2011, 00:03
Цитата(ASMatic @  15.7.2011,  23:03 Найти цитируемый пост)
в2. - инжект во все создаваемые процессы и там уже перехват NtOpenProcess(), NtWriteVirtualMemory, etc = опять таки можно на ваши перехваты забить) 

такая защита только от пионеров, даже если написать драйвер хукающий эти и другие функции, что мешает заинжектить свою DLL в игру и работать изнутри процесса, в общем способов море, обхода всяческих защит. Защита Frost еще как-то справляется, да и то ее переодически нагибают, хотя ее разрабатывают действительно профессионалы своего дела.
Цитата

трудновато тогды будет бороться с читами.

Увы, что правда то правда.
Цитата

вопрос: как идентифицировать такой чит?

Наверное так-же, как это делают антивирусные программы smile по сигнатурам, определенных участков файла (кода) только кто эти сигнатуры будет создавать ? Да и запаковать можно чит. Ох, да и хлопотное это дело вести базу читов, их же великое множество попробуй уследи за всеми.

Автор: volatile 16.7.2011, 00:05
boostcoder, если есть исходники чита, нужно копать их.
Там к чему нибудь можно наверное прикрепиться.
Самое простое - возможно есть какие-то текстовые строки.

Автор: boostcoder 16.7.2011, 00:16
volatile, уже работаю в этом направлении. еще беда(для меня) в том, что чит написан на дельфе smile 

Автор: ASMatic 16.7.2011, 00:17
Dik0n, все зависит что и как хучить. Если в ядре строить защиту = можно создать чот вполне приемлимое, но с читами трудно боротся, т.к. юзер сам заинтересован в "трояне")
понятно что от лома нет приёма, тут никто не спорит - но задачка опенсорс чит деактивировать..

Автор: volatile 16.7.2011, 01:06
Цитата(boostcoder @  16.7.2011,  00:16 Найти цитируемый пост)
еще беда(для меня) в том, что чит написан на дельфе  

В данном конкретном случае это даже лучше.
Если бы было на С++, то код очень сильно отличался бы в зависимости от компилятора.
А Дельфи - один. И код будет примерно одинаковым.

Можно попробовать выделить какую-нибудь сигнатуру ... 

Автор: boostcoder 16.7.2011, 01:10
Цитата(volatile @  16.7.2011,  01:06 Найти цитируемый пост)
Если бы было на С++, то код очень сильно отличался бы в зависимости от компилятора.
А Дельфи - один. И код будет примерно одинаковым.

кстати я не подумал об этом smile

Цитата(volatile @  16.7.2011,  01:06 Найти цитируемый пост)
Можно попробовать выделить какую-нибудь сигнатуру ... 

значит нужно нагуглить как можно больше бинарей этого чита, и каким-то бинарным diff`ом выделить одинаковые участки. и их уже можно анализировать на предмет поиска связи с исходниками. smile 

Автор: volatile 16.7.2011, 01:21
Цитата(boostcoder @  16.7.2011,  01:10 Найти цитируемый пост)
значит нужно нагуглить как можно больше бинарей этого чита, и каким-то бинарным diff`ом выделить одинаковые участки. и их уже можно анализировать на предмет поиска связи с исходниками. 

Ну, да. Как 1 из вариантов можно попробовать  smile 

Для сигнатуры в принципе достаточно не очень длинной последовательности. байт 16...32
Антивирусы примерно так работают.
Но байты сигнатуры могут идти не подряд.
например:
C2 BE F3 ?? ?? ?? ?? 01 2A ?? ?? ?? 3E
Ну вы меня поняли.

Попробуйте..

Автор: boostcoder 16.7.2011, 01:28
угу. нужно пробовать.

Автор: Dik0n 16.7.2011, 01:38
Цитата(ASMatic @  16.7.2011,  00:17 Найти цитируемый пост)
но задачка опенсорс чит деактивировать

Ну дак я написал 
Цитата

Наверное так-же, как это делают антивирусные программы по сигнатурам, определенных участков файла (кода)

Так-как в коде присутствуют определенные участки кода, которые не меняются при перекомпиляции, например ключевая функция чита, без которой он не будет работать, главное найти ее и составить сигнатуру.

Вывод: берем OpenSource антивирус и ковыряем как там и что...

Вот например моя прога с исходниками, можно посмотреть (правда на Delphi), определяет по сигнатурам на чем написана программа или чем упакована. http://my-soft.ucoz.ru/pe_sniffer.html

Автор: ASMatic 16.7.2011, 02:35
запаковал чемнить безоплатным, выложил на форум и опять ищи сигнатуру = тупая аверская "работа" от которой толку на пол дня.
Код

BOOL
WndCtrlCompareWndPos(
    IN      HWND        hWnd1,
    IN      HWND        hWnd2,
    IN      DWORD       Method
    )
{
    RECT    Rect1,
            Rect2;
    BOOL    result;

    result = FALSE;
    if (GetWindowRect( hWnd1, &Rect1 ) && GetWindowRect( hWnd2, &Rect2 )) {
        result = TRUE;

        if (FlagOn( Method, WND_CTRL_COMPARE_WND1_HAVE_LEFT)) {
            if (Rect1.left >= Rect2.left)
                result = FALSE;
        }

        if (FlagOn( Method, WND_CTRL_COMPARE_WND1_HAVE_DOWN)) {
            if (Rect1.top <= Rect2.top)
                result = FALSE;
        }

        if (FlagOn( Method, WND_CTRL_COMPARE_WND1_HAVE_UP)) {
            if (Rect1.top >= Rect2.top)
                result = FALSE;
        }

        if (FlagOn( Method, WND_CTRL_COMPARE_XPOS_EQUAL)) {
            if (Rect1.left != Rect2.left)
                result = FALSE;
        }

        if (FlagOn( Method, WND_CTRL_COMPARE_YPOS_EQUAL)) {
            if (Rect1.top != Rect2.top)
                result = FALSE;
        }
    }

    return result;
}

добавляем WND_CTRL_COMPARE_WND1_LONGER и т.п. (количество окон по типам и т.п. и с какими стилями) + вашу тулзу по определению компалера == делфик?отдыхай_радимый:и пусть те дельфекомпиляторщики поймут что нужно добавить окно на форму дабы детект снять.)

Автор: Dik0n 16.7.2011, 02:59
Цитата

пусть те дельфекомпиляторщики поймут что нужно добавить окно на форму дабы детект снять.)

ASMatic, Они как раз и поймут так и сделают, потому как любят добавлять всякие кнопки и надписи типа (этот чит сделал мега крутой ламер вася пупкин) да и стили могут подправить графики накидать что-бы круче смотрелось) и это только для отдельного случая а читов то вагон что для каждого свой способ придумывать, тут надо что-то универсальное!!!

Автор: volatile 16.7.2011, 03:08
Цитата(ASMatic @  16.7.2011,  02:35 Найти цитируемый пост)
запаковал чемнить безоплатным, выложил на форум и опять ищи сигнатуру = тупая аверская "работа"

Сигнатуру нужно искать в памяти процесса, а не на диске. Там он не запакован.

Тупая - не тупая
Острая - не острая.
 

Автор: ASMatic 16.7.2011, 03:34
Цитата(volatile @  16.7.2011,  01:08 Найти цитируемый пост)
Там он не запакован.

в случае с бесплатными, да (скорей всего).

Автор: bass 16.7.2011, 19:00
А как чит к твоей игрушке обращаеться???? Мне кажеться туда надо копать..... Например если через окошко то менять его название переодически..... Если в код длл инжектит то проверять на список модулей , можно конечно длл внедрить чтоб в списке модулей не торчала(тут пока не чего в голову не приходит...)..... Если создает сокет поковыряться в списке открытых хендолов программы......

Автор: ZVano 18.7.2011, 10:35
Цитата(bass @  16.7.2011,  19:00 Найти цитируемый пост)
А как чит к твоей игрушке обращаеться???

Действительно, дельный вопрос.
Если чит представляется модулем игры (плагином или аддоном), то достаточно встроить систему верификации.
Мне видится такой ход событий:
1. Игра грузит плагин-чит
2. Игра инициализирует плагин.
3. Игра идентифицирует плагин
3.1. Игра: Плагин, ты хто?
3.2. Плагин: Я плагин X системы такой то, версии такой то, мой приватный ключ имеет ID Y.
3.3. Игра: Подтверди свой статус. Вот тебе рандомная строка для проверки.
3.4. Плагин: Подтверждение. Вот тебе твоя строка, зашифрованая моим приватным ключем.
3.5. Игра: Такс, посмотрим что нам тут прислали... Берем публичный ключ для плагина X, расшифровываем сообщение, сверяем с первичным...
3.6.1. Игра: проверка успешна: Плагин, проходи.
3.6.2. Игра: проверка неудачна: Таксс... где то у меня были мои любимые бозар и гаусовка...

PS: публичные ключи нужно поставлять вкомпиленые в exe. 
В новых версиях exe следует добавлять невалидные ключи, которые злоумышленники сумели отыскать в бинарнике и использовать в своих читах.
PPS: зачем геморится с кючами? 
Потому что злоумышленнику сложно будет взламывать такую защиту. Попробуйте найти в памяти кусок данных, который соответствует ключику smile

Автор: shara 5.8.2011, 15:37
boostcoder

Цитата(bass @  16.7.2011,  18:00 Найти цитируемый пост)
А как чит к твоей игрушке обращаеться?


Поддерживаю, сия инфа будет весьма не лишней в процессе выработки коллективного решения проблемы. Еще неплохо было бы услышать в чем именно заключается "читерство"... Игрушка ведь как я понял он-лайн -> значитцо метод аля ArtMoney некатит

Цитата(ZVano @  18.7.2011,  09:35 Найти цитируемый пост)
Попробуйте найти в памяти кусок данных, который соответствует ключику 

А я вот нисагласен  smile  
Всевозможные отладчики с реверсинжинирингом никто не отменял. Но соглашусь что многим кулцхакерам такая защита заметно остудит пыл.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)