Как реализовать защиту проги на такой привязке? Есно, чтоб "никто" не сломал Ну и плюс к этому - как узнать начинку компа не прибегая к реестру т.е. напрямую?
Для этого твоя прога должна целиком исполняться на этой железке, а на компе выполняться только оболочка к ней. Все другие привязки можно сломать при наличии железки или даже без нее. Пиши драйвер. Только тебе надо защитить сам драйвер от дизассемблирования/отладки и защитить его взаимодействие с железкой и приложением. Главное реши к чему будешь привязываться.
Сейчас я начну расказывать о защите, очень похожей на ту, что реализованна в pinch2.5*. Что ее нельзя сломать, мягко говоря, не верно - сегодня мой друган "поломал" ее простым ArtMoney ). Однако в целом она достаточно сложная. В добавок я укажу на ошибки автора. Привязка к железу осуществляется с помощью функции GetVolumeInformation. Ее параметры: 1) Указатель на строку с именем диска. например "c:\",0 2) Указатель на буфер, куда поместится имя диска 3) Размер буфера 4) Указатель на двойное слово. Сюда поместится номер этого диска. Кажется, он задается производителем железа, но в любом случае для каждого харда он индивидуален. 5,6) Еще двойные слова. Нас не интересуют 7) Указатель на буфер, куда поместится имя файловой системы диска 8) Размер буфера Нас больше всего интересует параметр 4. Именно он (прохэшенный, на что-нибудь проксоренный и тп) является "Hardware Fingerprint". Теперь мы пишем прогрмму, бесплатная версия которой урезанна по функциональности. Функции, доступные в оплаченной версии зашифрованны каким-нибудь rc4( а до этого упакованны) или blowfish. Чтобы получить ключ дешифрования нужно сложить имя пользователя с Fingerprint, прохешировать и проксорить на серийник. Затем код расшифровывается, функции доступны. Прошу заметить, что функцию хеширования можно выбрать простую: ;eax, ebx, esi, edi - хешируемые данные xor ecx, ecx not cx hash_loop: push ecx xor ecx, eax xor ecx, ebx xor ecx, esi xor ecx, edi add eax, ecx rol eax, 1 xchg eax, ebx xchg ebx, esi xchg esi, edi pop ecx loop hash_loop ;eax, ebx, esi, edi - хэш чтобы сломать такую защиту нужно как минимум иметь валидные фигерпринтс, юзернаме и сериал. в случае пинча это требование всегда выполнимо, тк 1) программа относительно дешовая 2) ее пользователи - злые хакерюги Продавая прогу обязательно нужно задумываться о ее потребителях. Что дешевле - купить лиценцию, или поломать прогу? Выгодно ли честному американцу, заплотившему за прорамму 1000$, делиться с русскими крякерами серийником и тп. Как это ломают? Берут регистрационные данные, вбивают их в поля ввода, а фингерпринтс меняют лоадером. вот и все. Я вижу 5 способов противостоять взлому: 1) Задумываться о соотношении уровень защиты/цена 2) Думать, кто пользуется твоей программой 3) Хранить Fingerprint, разбросанным по всей программе (4 буффера по 1 байту каждый) - тогда их будет почти тяжело найти )) 4) Не принебрегать навесной защитой 5) не писать shareware
Смех просто а не защита. Такое любой новичек сломает за пару минут. Если идет расшифровка функций, то надо валидные ID апаратуры еще знать. А нафиг мне их искать? Я могу либо пропатчить проверку, либо если программа запускается на моем железе, то просто заменю вызываемые для идентификации железа функции своим кодом который всегда будет возвращать одно и тоже. Такой прием просто увеличит время взлома с 5 минут до 10. Прибавим еще от 5 минут до нескольких недель в зависимости от сложности протекта. Неплохая защита получиться при полной интеграции протектора в программу и проверки апаратуры на уровне портов. При этом нельзя забывать защитить КАЖДЫЙ элемент этой проверки, так как тут есть куча потенциально слабых мест. Неплохих результатов в этом направлении удалось достигнуть StarForce, но все равно ломают (хоть и очень нечасто). Короче, от взлома пока еще ничего не придумали. Как вариант - держать программу на сервере, а у клиента будет только оболочка. Но такой вариант в большинстве случаев неприемлим.
Как реализовать защиту проги на такой привязке? Есно, чтоб "никто" не сломал Ну и плюс к этому - как узнать начинку компа не прибегая к реестру т.е. напрямую? Обычная привязка к железу бессмысленна, одного единственного экземпляра приложения достаточно, чтобы навсегда "отвязать" прогу от защиты. Лучше попробуй привязываться к электронным ключам, гораздо надежнее…
И еще касательно защит. У меня появилась мысль, использовать набор символов ключа или серийника, как байт-коды для виртуального процессора, строящего граф вызова функций программы. Такой серийник нельзя пропатчить или отключить, т.к. он неотемлимая часть алгоритма программы. Пример такой защиты в аттаче, при неправильном серийнике, вызов функций происходит криво. Ключ 12345678, ради прикола введите 12347658 и программа будет глючить... 1529753218__SuperClock.zip
Те же яйца - вид сбоку. Достаточно одной рабочей копии программы с ключем для того чтобы отвязать ее от ключа. А иногда это можно сделать даже без ключа. Почему нельзя? Имея валидный серийник я просто прошью его в программу а все запросы на ввод серийника уберу. Да и зачем геморроиться, можно просто распостранять серийник с программой. Да и смысла в этом нет, так как такая защита будет работать только до покупки первого серийника. Того же эффекта можно добиться пошифровав программу серийником, тогда без него при всем желании сломать будет невозможно. Или как вариант - положи программу в запароленый рар архив, получиться тот же эффект что и с твоими ключами. Значит надо делать какую-то проверку на валидность. Если пользователь ошибется при вводе серийника и программа от этого будет глючить, то он ее просто выбросит.
По моему субъектиному мнению еще не придумано надежных систем защиты, кроме 1) исполнения части кода внутри аппаратного модуля, 2) исполнения части кода на сервере в Интернете. Причем эта часть кода должна нести ключевую функциональность программного продукта и быть невосстановимой конкурентами (к сожалению, она еще должна быть небольшой, чтобы не нагружать чип или сервер, и иметь небольшой объем входных и выходных параметров, чтобы не создавать huge-трафик)
Возьми готовый протектор и не парься. Большинство из новоявленных кулхацкеров об него зубы сломают, узнав разве только что название защиты. А абсолютной защиты нет, тебе правильно заметили. Нужно просто чтоб цена программы не превышала стоимость работы крякера, который сможет взломать протектор.
Ms Rem ты прекрасно рассуждаешь, но 1) Я не говорил что эта защита надежна. Если ты заметил, я сразу оговорился что она взломана обычным генератором трейнеров. 2) Понятное дело что по одиночки такие защиты ничего не стоят. 3) По поводу "человеческого фактора" ты почему-то ничего не сказал, посему буду считать что в этом направлении я мыслю верно. Исходя из пункта (3) мы предпологаем, что взломщику тяжело получить серийник, username и fingerprints. посему он не расшифрует часть кода, доступную только в платной версии. Я считаю что защищенность программы в первую очередь зависит от расчета количества потенциальных покупателей, цены программы и необходимого уровня защиты. От самого уровня защиты - во вторую очередь. Станут ли ломать программу стоимостью 5$, если ею пользуются 10 "платежеспасобных" человек, а программа защищена морфером, виртуальной машиной и описанным мною способом?
drmist Да, тут ты прав. Если программа очень узконаправленая и пользуется ей только платежеспособная аудитория, то ломать ее не будут, даже если это будет очень просто. А если программой пользуется весь мир, то ее обязательно сломают не зависимо от того, какая там была защита.
Контроллер на пике или атмеге, к которому время от времени обращается функция f(a), контроллер отдаёт пару байт, кот. затем исполняет прога. +некоторые не очень замороченные (арифметические и пр.) функции отдать самому контроллеру. Сломать будет оч. сложно.
riban С этих микроконтроллеров срипают любую микропрограмму. Так что это просто перенос поля боя на слабый аппаратный уровень.
alpet Если будет произведён постоянный обмен данными с большим кол-вом команд - то долго слизывать будут. Не хватает одного контроллера - поставь два в связке. А если и этого мало то есть ещё ПЛИС. Аппаратный уровень слабым я бы не стал называть.
Я тут вот подумал, что надо можно сделать вот так, только вопрос а нужно? Метод (смешно конечно, но интересно мнение спецов): На основе железа формируем каждый раз из конкретной железки выдираем инфу о ней и формируем ключ. Допустим имеется n пользовательских функций. Получаем адреса вызовов всех функций. Так же необходимо определить размер кода функции. Это можно сделать: - Вставив пустые функции для определения конца предыдущей функции - Конец определить по следующей функции Рассмотрим процесс для одной функции. Выделяем буфер под размер функции. Открываем процесс. Читаем память с адреса начала функции в буфер до конца функции. Пишем в файл код функции, не забывая записать адрес начала функции. Затем в файле .exe заменяем код функции на что угодно, хоть на нули. Аналогично для каждой функции. !Замечание1: Запись кода функций должна быть в один проход, без перекомпиляции, иначе могут поменяться адреса вызовов. В итоге имеем файл, который можно представить в виде таблицы, содержащую два поля: - Адрес вызова - Код функции Теперь необходимо произвести след. действия с файлом: - Шифруем код функции на ключе "адрес" - Заменяем адрес в таблице хэш значением адреса !Замечание2: Все хэши должны быть различны. В итоге получили файл-таблицу след. содержания: - Хэш адреса вызова - Зашифрованный код функции на ключе "адрес" Теперь относительно самих вызовов в программе. До вызова любой зашифрованной функции идет вызов функции "заполнения", которая обеспечивает расшифровку кода функции. После вызова любой зашифрованной функции идет вызов "Очистки", которая опять удаляет код функции. Так же загружаем файл-таблицу "кода". От всяких бряков на чтение можно в паре "нужных" потоков периодически считывать его. А где-н в нужном месте проверять существование потока и в случае чего "ругаться". Допустим был вызов функции "заполнения". - В качестве параметров ей передается адрес необходимой функции - Далее получаем хэш адреса - Ищем в файл-таблице по хэшу необходимый код и загружаем его по переданному адресу Вызов функции "Очистка". - В качестве параметров ей передается адрес необходимой функции - Далее получаем хэш адреса - Ищем в файл-таблице по хэшу необходимый код и чистим по переданному адресу в процессе
Значит главное найти эти функции. А бряки на них не обязательно ставить с помощью CC, если там крутые проверки кода имеються, то можно и bpr воспользоваться. Хотя проще будет занопить проверки в дополнительных потоках. Подобные ыещи давео уже применяются в протекторах и как показала практика хватает их ненадолго. Либо можно даже не вникать в алгоритмы подобных извратов, а просто найти и проэмулировать ключ. Так что эффективность защиты зависит от ее самого слабого звена.
Короче, как я понял, я ничего не знаю Поэтому вопрос: Где можно почитать о защите, не считая статьи wasm.ru
cracklab.ru (для начинающих) xtin.km.ru (есть весьма неплохие статьи) и зайди в раздел "ссылки" на краклабе, может еще что-нить интересное попадется. А после прочтения статей желательно самому что-нить покопать. И чем больше узнаешь про защиту, тем больше разубеждаешся в возможности создать что-то неломаемое.
Ms Rem а все-таки как защитить подобной привязкой к железу на скорую руку. (время поджимает ). Я уверен, что данная программа не попадет в руки суперкрякеров, максимум - это будут любители.
Вот код получающий серийный номер первого HDD: Код (Text): function GetIdeDiskSerialNumber: string; type TSrbIoControl = packed record HeaderLength: ULONG; Signature: array[0..7] of Char; Timeout: ULONG; ControlCode: ULONG; ReturnCode: ULONG; Length: ULONG; end; SRB_IO_CONTROL = TSrbIoControl; PSrbIoControl = ^TSrbIoControl; TIDERegs = packed record bFeaturesReg: Byte; bSectorCountReg: Byte; bSectorNumberReg: Byte; bCylLowReg: Byte; bCylHighReg: Byte; bDriveHeadReg: Byte; bCommandReg: Byte; bReserved: Byte; end; IDEREGS = TIDERegs; PIDERegs = ^TIDERegs; TSendCmdInParams = packed record cBufferSize: DWORD; irDriveRegs: TIDERegs; bDriveNumber: Byte; bReserved: array[0..2] of Byte; dwReserved: array[0..3] of DWORD; bBuffer: array[0..0] of Byte; end; SENDCMDINPARAMS = TSendCmdInParams; PSendCmdInParams = ^TSendCmdInParams; TIdSector = packed record wGenConfig: Word; wNumCyls: Word; wReserved: Word; wNumHeads: Word; wBytesPerTrack: Word; wBytesPerSector: Word; wSectorsPerTrack: Word; wVendorUnique: array[0..2] of Word; sSerialNumber: array[0..19] of Char; wBufferType: Word; wBufferSize: Word; wECCSize: Word; sFirmwareRev: array[0..7] of Char; sModelNumber: array[0..39] of Char; wMoreVendorUnique: Word; wDoubleWordIO: Word; wCapabilities: Word; wReserved1: Word; wPIOTiming: Word; wDMATiming: Word; wBS: Word; wNumCurrentCyls: Word; wNumCurrentHeads: Word; wNumCurrentSectorsPerTrack: Word; ulCurrentSectorCapacity: ULONG; wMultSectorStuff: Word; ulTotalAddressableSectors: ULONG; wSingleWordDMA: Word; wMultiWordDMA: Word; bReserved: array[0..127] of Byte; end; PIdSector = ^TIdSector; const IDE_ID_FUNCTION = $EC; IDENTIFY_BUFFER_SIZE = 512; DFP_RECEIVE_DRIVE_DATA = $0007C088; IOCTL_SCSI_MINIPORT = $0004D008; IOCTL_SCSI_MINIPORT_IDENTIFY = $001B0501; DataSize = sizeof(TSendCmdInParams) - 1 + IDENTIFY_BUFFER_SIZE; BufferSize = SizeOf(SRB_IO_CONTROL) + DataSize; W9xBufferSize = IDENTIFY_BUFFER_SIZE + 16; var hDevice: THandle; cbBytesReturned: DWORD; pInData: PSendCmdInParams; pOutData: Pointer; Buffer: array[0..BufferSize - 1] of Byte; srbControl: TSrbIoControl absolute Buffer; procedure ChangeByteOrder(var Data; Size: Integer); var ptr: PChar; i: Integer; c: Char; begin ptr := @Data; for i := 0 to (Size shr 1) - 1 do begin c := ptr^; ptr^ := (ptr + 1)^; (ptr + 1)^ := c; Inc(ptr, 2); end; end; begin Result := ''; FillChar(Buffer, BufferSize, #0); begin hDevice := CreateFile('\\.\Scsi0:', GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0); if hDevice = INVALID_HANDLE_VALUE then Exit; try srbControl.HeaderLength := SizeOf(SRB_IO_CONTROL); System.Move('SCSIDISK', srbControl.Signature, 8); srbControl.Timeout := 2; srbControl.Length := DataSize; srbControl.ControlCode := IOCTL_SCSI_MINIPORT_IDENTIFY; pInData := PSendCmdInParams(PChar(@Buffer) + SizeOf(SRB_IO_CONTROL)); pOutData := pInData; with pInData^ do begin cBufferSize := IDENTIFY_BUFFER_SIZE; bDriveNumber := 0; with irDriveRegs do begin bFeaturesReg := 0; bSectorCountReg := 1; bSectorNumberReg := 1; bCylLowReg := 0; bCylHighReg := 0; bDriveHeadReg := $A0; bCommandReg := IDE_ID_FUNCTION; end; end; if not DeviceIoControl(hDevice, IOCTL_SCSI_MINIPORT, @Buffer, BufferSize, @Buffer, BufferSize, cbBytesReturned, nil) then Exit; finally CloseHandle(hDevice); end; if not DeviceIoControl(hDevice, DFP_RECEIVE_DRIVE_DATA, pInData, SizeOf(TSendCmdInParams) - 1, pOutData, W9xBufferSize, cbBytesReturned, nil) then Exit; end; with PIdSector(PChar(pOutData) + 16)^ do begin ChangeByteOrder(sSerialNumber, SizeOf(sSerialNumber)); SetString(Result, sSerialNumber, SizeOf(sSerialNumber)); end; end; Но поможет это только от начинающих. Если надо спастсь от любителей, то получай номера железа в ринг0. А от профессионалов можешь даже не пытаться защититься, так как это невозможно.