Re[5]: Шифрование кода – 2023
От: Черный 😈 Властелин Австралия https://www.softperfect.com
Дата: 28.02.23 20:58
Оценка: +4 :))
ЧВ>>У меня несколько десятков ордеров в день, если переписываться с каждым у кого не работает ключ, поменялись железки или там закончились активации, когда тогда писать код?
S>С другой стороны, если у тебя несколько десятков ордеров в день, зачем писать код?

В смысле, зачем? Я так к ним и пришел, постоянно совершенствуя и разрабатывая свой софт.

Конечно можно кого-то нанять, но я не люблю людей, а вот код и компы да — потому по сути занимаюсь любимым делом с достойной оплатой.
Re[3]: Шифрование кода – 2023
От: wantus  
Дата: 27.02.23 11:26
Оценка: 21 (3) +1
Здравствуйте, Khimik, Вы писали:

K>Здравствуйте, wantus, Вы писали:


W>>Шифрование кода, в подавляющем числе случаем — это абсолютный overkill. Нормальный рабочий вариант — это внутренний проверки на модификацию кода, как статическую (патчи и кряки) так и динамическую (API hooking, DLL side loading, dynamic patching, etc.).


K>Я не очень понял, что это за проверки, поясните их принцип.

K>Полагаю, эти проверки устроены так, что в случае срабатывания отрубаются какие-то отдельные фичи программы? Так что программой в целом пользоваться по-прежнему можно, но будет мотивация всё-таки купить официальную версию. По идее, хакеры не разбираются во взламываемой программе и не будут проверять функциональность этих случайных фич.

Из простых вариантов

1. Добавить проверку подписи собственного exe (через WinVerifyTrust). По случайному таймеру, не сразу. Проверили, запомнили результат, запустили еще один таймер на несколько минут. Когда таймер сработал, покрасили background окна в красный цвет, или перевернули его кверх ногами, или еще чего-нибудь очевидно намеренное и кривое. Это уже достаточно противно патчить.

2. Добавить еще несколько копий этой проверки — полных копий всего кода, а не просто вызовов check_exe_signature() — и навешать их на разные события, 56-ой клик мышки или типа того. Опять же, все проверки и реакции на их результаты — отложенные, через таймеры, простые counters или чего там еще.

3. Могут запатчить сам WinVerifyTrust. На это добавляем вызовов WinVerifyTrust с кривыми параметрами и смотрим, что он таки возвращает ошибку.

Как бонус

4. Можно все эти проверки отключать, если похоже что программа бежит под дебагером — IsDebuggerPresent, PEB.BeingDebugged, NtQueryInformationProcess(..., 7, ...), etc. В принципе все хорошие дебагеры умеют притворяться, что их нет, но это простая в изготовлении какашка и подложить её не мешает.

5. Из той же серии в релиз билды можно нашпиговать NtSetInformationThread((HANDLE)-2, 0x11), что выключает дебаг текущего треда. Тоже обходится соответствующим add-on'ом к дебагеру, но далеко не все об этом знают.

Типа окопная партизанская война с целью измождения противника.

И как уже сказали — строго online licensing с public keys, никаких прошитых или алгоритмических ключей. Как побочный эффект, это помогает свести credit card fraud и chargebacks практически в ноль.
Re[4]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 28.02.23 10:33
Оценка: 24 (3)
Здравствуйте, wantus, Вы писали:

W>Из простых вариантов


W>1. Добавить проверку подписи собственного exe (через WinVerifyTrust). По случайному таймеру, не сразу. Проверили, запомнили результат, запустили еще один таймер на несколько минут. Когда таймер сработал, покрасили background окна в красный цвет, или перевернули его кверх ногами, или еще чего-нибудь очевидно намеренное и кривое. Это уже достаточно противно патчить.


Пропатчат место вызова WinVerifyTrust.

W>2. Добавить еще несколько копий этой проверки — полных копий всего кода, а не просто вызовов check_exe_signature() — и навешать их на разные события, 56-ой клик мышки или типа того. Опять же, все проверки и реакции на их результаты — отложенные, через таймеры, простые counters или чего там еще.


Положат рядом прокси DLL с "правильным" WinVerifyTrust

W>3. Могут запатчить сам WinVerifyTrust. На это добавляем вызовов WinVerifyTrust с кривыми параметрами и смотрим, что он таки возвращает ошибку.


В прокси DLL сначала вызовут оригинальную WinVerifyTrust, если ничего страшного не произошло — вернут "правильный" результат.

W>Как бонус


W>4. Можно все эти проверки отключать, если похоже что программа бежит под дебагером — IsDebuggerPresent, PEB.BeingDebugged, NtQueryInformationProcess(..., 7, ...), etc. В принципе все хорошие дебагеры умеют притворяться, что их нет, но это простая в изготовлении какашка и подложить её не мешает.


На момент написания прокси DLL поставят ScyllaHide и обойдут все эти проверки на отладчик.

W>5. Из той же серии в релиз билды можно нашпиговать NtSetInformationThread((HANDLE)-2, 0x11), что выключает дебаг текущего треда. Тоже обходится соответствующим add-on'ом к дебагеру, но далеко не все об этом знают.


Из той же серии про ScyllaHide.

W>Типа окопная партизанская война с целью измождения противника.


Я вижу тут измождение только со стороны разработчика. Даже написать вызов WinVerifyTrust у него займет гораздо больше времени чем у крякера занопить его в готовом бинарнике.

W>И как уже сказали — строго online licensing с public keys, никаких прошитых или алгоритмических ключей. Как побочный эффект, это помогает свести credit card fraud и chargebacks практически в ноль.


Public Keys для этого нужно так нихренова защитить, а то получится все таже шляпа, что и с WinVerifyTrust.
Отредактировано 28.02.2023 10:34 drVanо . Предыдущая версия .
Re: Шифрование кода – 2023
От: Александр Широков Россия www.alzex.com
Дата: 27.02.23 15:25
Оценка: 23 (3)
Здравствуйте, Khimik, Вы писали:

Защита не нужна. Он-лайн активация, отзыв ключей (чтобы спокойно делать рефанды) и простая проверка ключа. Все.
Я в последнем большом обновлении всю защиту выкинул. Те два с половиной бедолаги, что в 2023 году все еще шарятся по помойкам в поисках кряка, все равно ничего не купят. Даже наличие кейгена не влияет на продажи по моему опыту.
https://www.personalfinances.ru
Re[19]: Шифрование кода – 2023
От: rudzuk  
Дата: 06.03.23 10:49
Оценка: +2 :)
Здравствуйте, TailWind, Вы писали:

TW> Он, конечно, не совсем культурно себя ведёт

TW> Но он прав

Да не, он расказывает сказочки о 15-летних ксакепах умножающих труд программиста на ноль за десять минут

Сейчас даже просто использование нативной компиляции уже является достаточным препятствием для "юных дарований". Все потому, что они привыкли "ломать" софт, для которого есть декомпилятор, и даже просто использование примитивной обфускации ставит их в тупик. Я, если что, говорю о 95% интеллектуального большинства. Более того, не так давно читал интервью с антивирусным аналитиком из одной очень известной конторы, так он сокрушался, что очень хреново анализировать малвару написанную на Go т.к. она компилируется в натив, а вот малвару под дотнет ему анализировать нравится

TW> Делал такую защиту:

TW> — проверка CRC exe файла (не в памяти, а на диске)
TW> — отложенная по таймеру

TW> Ну и опытные хакеры надо мной поржали, взломав за день


Перед опытным и упорным реверсером не устоит ни одна защита (его вмпротект — тоже), это нужно понимать. Но и цели делать непробиваемую защиту нет, и это тоже нужно понимать. Мой софт с 2016 года в сети. Нет, ни кряков, ни кейгенов. Несколько слитых ключей и все. Софт дороже $300. Но это все потому, что я Неуловимый Джо (предвосхищаю, на всякий случай).
avalon/3.0.2
Re[3]: Шифрование кода – 2023
От: Sharowarsheg  
Дата: 01.03.23 13:57
Оценка: 12 (1) +1
Здравствуйте, sergey2b, Вы писали:

S>Почему его автор не стал поддерживать


Вечные лицензии, мне помнится. Деньги получаешь один раз, а поддержка навсегда. Через какое-то время проект приходится закрывать.
Re[16]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 06.03.23 03:47
Оценка: 1 (1) :)
Здравствуйте, wantus, Вы писали:

W>Да, елки-палки.

W>Разговор не о том, что несложные вещи не ломаются.
W>Разговор о том, что их зачастую достаточно для того, чтобы по сети не плавали взломанные версии.

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

Итак твои тезисы
Автор: wantus
Дата: 27.02.23
:

Добавить проверку подписи собственного exe (через WinVerifyTrust)

У начинающего крякера (этот такой 15-ти летний пацан, который прочитал пару статей Криса Касперски, пол статьи из журнала "Хакера", умеет запускать дебаггер и знает чем отличается условный от безусловного перехода) на отлом этой защиты у него уйдет минут 10. Почему этот способ по моему мнению полный шлак? Да потому, что даже минимальных знаний в реверсинге будет достаточно чтобы его обойти. А если учесть, что программист, который решил закодить этот способ убил на него час-два-три (чтение документации по WinVerifyTrust, сам кодинг, отладка и все остальные прелести), а крякер на эту "защиту" потратил всего 10 минут, то нужна ли эта защита? Думаю что нахрен не нужна, потому что программист за эти самые полчаса может написать какой-то полезный функционал, который в конечном итоге принесет ему реальные деньги, а не мнимую защиту от кряка.

Добавить еще несколько копий этой проверки — полных копий всего кода

Опустим некоторые технические моменты написания такой полноценной проверки целостности образа в памяти, на которые я намекал
Автор: drVanо
Дата: 28.02.23
. Просто скажем, что по сравнению c п.1 квалификация программиста, который самостоятельно будет реализовывать подобный функционал, должна быть гораздо выше, а вот квалификация крякера будет тойже самой (ну может быть он найдет еще какую-нить статейку про то как в отладчике работают бряки на доступ к данным) и ему будет достаточно пропатчить всего одну функцию, которая считает эту самую CRC. Итого — на имлементацию, отладку и прочие прелести у программиста ушел день/неделя/месяц, а у 15-ти летнего пацана, который ждаст фор фан запустил вашу программу в отладчике, в сумме с п.1 ушел максимум час. Почему этот способ по моему мнению полный шлак? Читай выводы п.1.

Могут запатчить сам WinVerifyTrust. На это добавляем вызовов WinVerifyTrust с кривыми параметрами и смотрим, что он таки возвращает ошибку.

Ну если 15-ти летний пацан не дурак, а он не дурак и тоже умеет читать документацию по WinAPI, то он пропатчит проверку результата так, чтобы в случае критической ошибки код этой самой ошибки вернулся в вызывающий код.

4. Можно все эти проверки отключать, если похоже что программа бежит под дебагером — IsDebuggerPresent, PEB.BeingDebugged, NtQueryInformationProcess(..., 7, ...), etc. В принципе все хорошие дебагеры умеют притворяться, что их нет, но это простая в изготовлении какашка и подложить её не мешает.

15-ти летний пацан уже решил пару крякмиксов и тоже знает про IsDebuggerPresent, PEB.BeingDebugged, NtQueryInformationProcess. Почему данный способ полный шлак? Потому что, программист еще даже не закодил все это дело в свою программу, а 15-ти летний пацан это все уже сломал, скачав ScyllaHide.

Типа окопная партизанская война с целью измождения противника.

Подведем итоги окопной войны? С одной стороны в "окопе" сидит программист, который уже потратил кучу времени на имплементацию твоего бреда, при этом не написал еще ниодной строчки полезного для его программы (своих пользователей) кода, а с другой стороны в "окопе" сидит 15-ти летний пацан, который только вчера вживую увидел программу, а сегодня ради прикола, потратив на порядок меньше времени, уже все это взломал.

Нужна ли ТС-у такая защита? Думаю что нахрен не нужна. Любой думающий своей головой шароварщик прекрасно понимает, что его время гораздо ценнее, чем время 15-ти летнего пацана, и что лучше это время потратить на написание полезный функционала, чем на "мега крутую защиту от Сифона и Бороды вантуса и рудзика".

Ну а теперь на десерт самый твой последний тезис:

Разговор о том, что их зачастую достаточно для того, чтобы по сети не плавали взломанные версии.

Кто-то мне может показать логическую связь между тем, что программист на имплементацию всего это бреда потратил неделю, а 15-ти летний пацан потратил на взлом программы 3 часа, с тем фактом, что по сети вдруг перестали плавать взломанные версии? Я вот например вообще не вижу никакой связи. Если даже 15-ти летний пацан с нулевым опытом смог справиться с "задачей", то взломанные версии будут плавать везде, да еще как! Хотя это скорее всего к тому вопросу, почему у некоторых здешних обитателей иногда напрочь отсутствует какая-либо логика (некоторые ее отсутствие прикрывают недостатком сантиметров
Автор: rudzuk
Дата: 01.03.23
). Если это действительно так, то с программированием лучше завязывать.
Re: Шифрование кода – 2023
От: Черный 😈 Властелин Австралия https://www.softperfect.com
Дата: 26.02.23 23:00
Оценка: +2
Здравствуйте, Khimik, Вы писали:

K>Я раньше пользовался The Enigma Protector, сейчас по идее надо его обновить, вопрос насколько всё это сейчас актуально? Как сейчас на Западе с хакерами — их прижали, или наоборот они распоясались? Есть ли среди хакеров такие, которые продают наши программы за крипту?

K>Я наверно не смогу шифровать код Mac версии своей программы, поэтому такая мысль – может быть, довольно важно сделать кейген для Mac другой, с другим алгоритмом, чтобы хакеры не смогли, разобрав код Mac-программы, написать кейген для Windows-версии?
K>Какой шифратор лучше выбрать — Execryptor или есть что-то новое?
K>Не решена ли как-то проблема ложных срабатываний антивирусов на шифрованный код?

C кейгенами бороться очень просто: используй RSA c двумя ключами. Приватный ключ у девелопера, публичный в программе. Ключ шифруется приватным ключом при создании, а программа расшифровывает публичным. Не имея приватного ключа, создать валидный лицензионный ключ невозможно. Остаются патчи, но они скажем так менее привлекательны — пользователи боятся тк хз что там еще они поменяют и сколько троянов довесят.

По моему личному опыту, использовал VMProtect. Насколько я знаю один из немногих протекторов поддерживащих мак и линух. Протектор отличный, но у меня было три основные проблемы:

1) Размер исполняемых файлов удваивается как минимум за счет внедрения виртуальной машины. Собственно исполнение части кода на виртуальной машине и осложняет жизнь хацкерам, тк становится очень сложно понять что там происходит.
2) В связи с этим вместо кряков появляются покупки по краденным картам и чержбеки владельцев.
3) Антивирусы тоже не особо понимают код виртуальной машины, а следовательно на всякий случай задетектим его как вирус. Короче говоря ложных срабатываний полно, особенно у говно-антивирусов второго эшелона.

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

Естественно появились патчи, но в целом продажи не изменились, и теперь фроды и борьба с антивирусниками занимают куда меньше времени. В итоге я пришел к выводу, что те кто не купят ваш софт в любом случае воспользутся или левой кредиткой или патчем. Да ну и пусть, c них все равно денег не получить. А те кто могут купить прогу, покупают ее как и раньше.

  Поделюсь модулем для FPC/DELPHI который декодирует ключи VMProtect:
unit Licensing.VMProtect;

interface

{$IFDEF FPC}
  {$MODE DELPHI}
  {$RANGECHECKS OFF}
  {$OVERFLOWCHECKS OFF}
{$ELSE}
  {$WEAKLINKRTTI ON}
  {$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
{$ENDIF}

uses
  {$IFDEF FPC}
  Base64, Classes, SysUtils, Sha1, Md5;
  {$ELSE}
  System.NetEncoding, System.Classes, System.SysUtils, System.WideStrUtils,
  System.Hash;
  {$ENDIF}

const
  BuildDate = {$I BuildDate.inc};

type
  TVMProtectDate = packed record
    wYear: Word;
    bMonth: Byte;
    bDay: Byte;
  end;

  PVMProtectSerialNumberData = ^TVMProtectSerialNumberData;
  TVMProtectSerialNumberData = packed record
    nState: Longword;
    wUserName: array [0..255] of WideChar;
    wEMail: array [0..255] of WideChar;
    dtExpire: TVMProtectDate;
    dtMaxBuild: TVMProtectDate;
    bRunningTime: Longword;
    nUserDataLength: Byte;
    bUserData: array [0..254] of Byte;
  end;

const
  SERIAL_STATE_SUCCESS                 = 0;
  SERIAL_STATE_FLAG_CORRUPTED            = $00000001;
  SERIAL_STATE_FLAG_INVALID           = $00000002;
  SERIAL_STATE_FLAG_BLACKLISTED           = $00000004;
  SERIAL_STATE_FLAG_DATE_EXPIRED       = $00000008;
  SERIAL_STATE_FLAG_RUNNING_TIME_OVER  = $00000010;
  SERIAL_STATE_FLAG_BAD_HWID           = $00000020;
  SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED  = $00000040;

type
  LbIntBuf = packed record
    dwLen: Integer;
    pBuf: PByte;
  end;

  LbInteger = packed record
    bSign: Boolean;
    dwUsed: Integer;
    IntBuf: LbIntBuf;
  end;

  { TSerialDecoder }

  TSerialDecoder = class
  private
    FSerialNum: TBytes;
    FPublicExp: TBytes;
    FPublicMod: TBytes;
    FProductCode: UInt64;
    FSerialNumberData: TVMProtectSerialNumberData;
    procedure CheckSerialBlackList;
    function GetDecodedBytes: TBytes;
    function GetBytesFromBase64(const Value: string): TBytes;
    function GetLargeIntFromBytes(const Bytes: TBytes): LbInteger;
    function GetBytesFromLargeInt(const Value: LbInteger): TBytes;
  protected
    function GetBuildDate: TDate; virtual;
  public
    procedure SetPublicExp(const Value: string);
    procedure SetPublicMod(const Value: string);
    procedure SetSerialNum(const Value: string);
    procedure SetProductCode(const Value: UInt64);
    function VMProtectSetSerialNumber(const SerialNumber: string): LongWord;
    function VMProtectGetSerialNumberData(Data: PVMProtectSerialNumberData;
      DataSize: Integer): Boolean;
  end;

implementation

const
  cLESS_THAN = ShortInt(-1);
  cEQUAL_TO = ShortInt(0);
  cGREATER_THAN = ShortInt(1);
  cPOSITIVE = True;
  cNEGATIVE = False;

const
  cBYTE_POSSIBLE_VALUES = 256;
  cDEFAULT_PRECISION = 64;
  cUSE_DEFAULT_PRECISION = 0;
  cDEFAULT_SIGN = cPOSITIVE;
  cDEFAULT_USED = 0;
  cAPPEND_ARRAY = 0;
  cPREPEND_ARRAY = 1;

const
  sBIBufferNotAssigned = 'Buffer not assigned';
  sBINoNumber = 'No number';
  sBISubtractErr = 'Subtraction error';
  sBIZeroDivide = 'Division by zero';
  sBIQuotientErr = 'Quotient process error';
  sBIZeroFactor = 'Factor is zero';

type
  PBiByteArray = ^TBiByteArray;
  TBiByteArray = array [0..Pred(MaxInt)] of Byte;

{$IFNDEF FPC}
type
  TBytesStream = class(System.Classes.TBytesStream)
  public
    function ReadByte: Byte;
    function ReadWord: Word;
    function ReadDWord: UInt32;
    function ReadQWord: UInt64;
  end;
{$ENDIF}

procedure LbBiInit(out N1: LbInteger; const Precision: Integer);
begin
  N1 := Default(LbInteger);
  if Precision > 0 then
    N1.IntBuf.dwLen := Precision
  else
    N1.IntBuf.dwLen := cDEFAULT_PRECISION;

  N1.bSign := cDEFAULT_SIGN;
  N1.dwUsed := cDEFAULT_USED;

  N1.IntBuf.pBuf := PByte(AllocMem(N1.IntBuf.dwLen));
end;

procedure LbBiTrimSigZeros(var N1: LbInteger);
begin
  if (not Assigned(N1.IntBuf.pBuf)) then
    raise Exception.Create(sBIBufferNotAssigned);

  while (PBiByteArray(N1.IntBuf.pBuf)[Pred(N1.dwUsed)] = 0) do
  begin
    Dec(N1.dwUsed);
    if (N1.dwUsed <= 0) then
    begin
      N1.dwUsed := 1;
      Exit;
    end;
  end;
end;

procedure LbBiRealloc(var N1: LbInteger; const Len: Integer);
var
  TmpPtr: PByte;
begin
  if (N1.dwUsed > Len) then
    Exit;
  TmpPtr := AllocMem(Len);
  move(N1.IntBuf.pBuf^, TmpPtr^, N1.dwUsed);
  FreeMem(N1.IntBuf.pBuf);
  N1.IntBuf.dwLen := Len;
  N1.IntBuf.pBuf := TmpPtr;
end;

procedure LbBiClear(var N1: LbInteger);
begin
  N1.bSign := cDEFAULT_SIGN;
  N1.dwUsed := cDEFAULT_USED;
  FillChar(N1.IntBuf.pBuf^, N1.IntBuf.dwLen, $00);
end;

function LbBiIsZero(N1: LbInteger): Boolean;
begin
  LbBiTrimSigZeros(N1);
  Result := False;
  if (N1.dwUsed = 1) and (PBiByteArray(N1.IntBuf.pBuf)[0] = 0) then
    Result := True
end;

procedure LbBiAddByte(var N1: LbInteger; const Place: Integer;
  const ByteValue: Byte);
begin
  if (Place = cAPPEND_ARRAY) then
  begin
    if (Succ(N1.dwUsed) > N1.IntBuf.dwLen) then
      LbBiRealloc(N1, Succ(N1.dwUsed));
    PBiByteArray(N1.IntBuf.pBuf)[N1.dwUsed] := ByteValue;
    Inc(N1.dwUsed);
  end
  else
  begin
    if (Place > N1.IntBuf.dwLen) then
      LbBiRealloc(N1, Place);
    PBiByteArray(N1.IntBuf.pBuf)[Pred(Place)] := ByteValue;
    if (N1.dwUsed < Place) then
      N1.dwUsed := Place;
  end;
end;

function LbBiGetByteValue(const N1: LbInteger; const Place: Integer): Byte;
begin
  if (N1.dwUsed < Place) then
  begin
    Result := 0;
    Exit;
  end;
  Result := PBiByteArray(N1.IntBuf.pBuf)[Pred(Place)];
end;

function LbBiReverseBits(ByteValue: Byte): Byte;
var
  I: Byte;
  RBit: Byte;
begin
  Result := 0;
  RBit := $80;
  for I := 1 to 8 do
  begin
    if ((ByteValue and $01) <> 0) then
      Result := Result or RBit;
    RBit := RBit shr 1;
    ByteValue := ByteValue shr 1;
  end;
end;

function LbBiIsOne(N1: LbInteger): Boolean;
begin
  LbBiTrimSigZeros(N1);
  Result := False;
  if (N1.dwUsed = 1) and (PBiByteArray(N1.IntBuf.pBuf)[0] = 1) then
    Result := True
end;

procedure LbBiCopy(var Dest: LbInteger; const Src: LbInteger;
  const Len: Integer);
var
  Ptr: PByte;
  Size: Integer;
begin
  FillChar(Dest.IntBuf.pBuf^, Dest.IntBuf.dwLen, $00);

  Size := Integer(Len);

  if Size > Dest.IntBuf.dwLen then
    LbBiRealloc(Dest, Size);

  Ptr := Dest.IntBuf.pBuf;
  move(Src.IntBuf.pBuf^, Ptr^, Len);

  if (Dest.dwUsed < Size) then
    Dest.dwUsed := Size;
end;

function MultSpecialCase(const N1: LbInteger; const N2: LbInteger;
  var Product: LbInteger): Boolean;
begin
  Result := False;

  if (LbBiIsZero(N1) or LbBiIsZero(N2)) then
  begin
    LbBiAddByte(Product, cPREPEND_ARRAY, $00);
    Result := True;
    Exit;
  end;

  if (LbBiIsOne(N1)) then
  begin
    Product.dwUsed := N2.dwUsed;

    if (Product.IntBuf.dwLen < N2.IntBuf.dwLen) then
      LbBiRealloc(Product, N2.IntBuf.dwLen);

    LbBiCopy(Product, N2, N2.dwUsed);
    Result := True;
    Exit;
  end;

  if (LbBiIsOne(N2)) then
  begin
    Product.dwUsed := N1.dwUsed;

    if (Product.IntBuf.dwLen < N1.IntBuf.dwLen) then
      LbBiRealloc(Product, N1.IntBuf.dwLen);

    LbBiCopy(Product, N1, N1.dwUsed);
    Result := True;
  end;
end;

procedure LbBiMultBase(N1: LbInteger; const N2: LbInteger;
  var Product: LbInteger);
var
  InxX: Integer;
  InxY: Integer;
  MaxX: Integer;
  Carry: Integer;
  Prd: Integer;
  Plc: Integer;
  TmpByte: Byte;
  TmpInt: Integer;
begin
  if (MultSpecialCase(N1, N2, Product)) then
    Exit;
  MaxX := Pred(N1.dwUsed);
  TmpInt := Pred(N2.dwUsed);
  for InxY := 0 to TmpInt do
  begin
    if PBiByteArray(N2.IntBuf.pBuf)[InxY] <> 0 then
    begin
      Carry := 0;
      for InxX := 0 to MaxX do
      begin
        Plc := InxX + InxY;
        Prd := PBiByteArray(N1.IntBuf.pBuf)[InxX];
        Prd := Prd * PBiByteArray(N2.IntBuf.pBuf)[InxY];
        if (Product.dwUsed < Plc) then
          Prd := Prd + Carry
        else
          Prd := Prd + PBiByteArray(Product.IntBuf.pBuf)[Plc] + Carry;

        TmpByte := Prd and $00FF;
        Carry := Prd shr 8;

        if (Succ(Plc) > Product.IntBuf.dwLen) then
          LbBiRealloc(Product, Plc + 100);
        PBiByteArray(Product.IntBuf.pBuf)[Plc] := TmpByte;
        if (Product.dwUsed < Succ(Plc)) then
          N1.dwUsed := Succ(Plc);
      end;
      LbBiAddByte(Product, (MaxX + InxY + 2), Carry);
    end;
  end;
  LbBiTrimSigZeros(Product);
end;

procedure LbBiMult(const N1, N2: LbInteger; var Product: LbInteger);
begin
  LbBiMultBase(N1, N2, Product);
  if (N1.bSign = N2.bSign) then
    Product.bSign := cPOSITIVE
  else
    Product.bSign := cNEGATIVE;
end;

procedure LbBiFree(var N1: LbInteger);
begin
  if (Assigned(N1.IntBuf.pBuf)) then
    FreeMem(N1.IntBuf.pBuf);
  FillChar(N1, SizeOf(LbInteger), $00);
end;

procedure LbBiMultInPlace(var N1: LbInteger; const N2: LbInteger);
var
  Product: LbInteger;
  Precis: Integer;
begin
  Precis := (N1.dwUsed + N2.dwUsed) * 2;
  LbBiInit(Product, Precis);
  LbBiMult(N1, N2, Product);
  LbBiClear(N1);
  N1.dwUsed := Product.dwUsed;
  N1.bSign := Product.bSign;
  if (N1.IntBuf.dwLen < Product.IntBuf.dwLen) then
    LbBiRealloc(N1, Product.IntBuf.dwLen);
  LbBiCopy(N1, Product, Product.dwUsed);
  LbBiFree(Product);
end;

function LbBiFindFactor(B1: Byte): Byte;
begin
  Result := 1;
  while (B1 < $80) do
  begin
    B1 := (B1 shl 1);
    Result := Result * 2;
  end;
end;

procedure LbBiMulByDigitBase(N1: LbInteger; const N2: Byte;
  var Product: LbInteger);
var
  Count: Integer;
  Carry: Byte;
  Prd: WORD;
  TmpByte: Byte;
  TmpInt: Integer;
begin
  if (N2 = 1) then
  begin
    if (Product.IntBuf.dwLen < N1.IntBuf.dwLen) then
      LbBiRealloc(Product, N1.IntBuf.dwLen);
    Product.dwUsed := N1.dwUsed;
    Product.bSign := N1.bSign;
    LbBiCopy(Product, N1, N1.dwUsed);
  end;

  if (N2 = 0) then
  begin
    Product.dwUsed := 1;
    LbBiAddByte(Product, cPREPEND_ARRAY, 0);
  end;

  if LbBiIsOne(N1) then
  begin
    Product.dwUsed := 1;
    LbBiAddByte(Product, cPREPEND_ARRAY, N2);
  end;
  if (N1.dwUsed = 1) and (PBiByteArray(N1.IntBuf.pBuf)[0] = 0) then
  begin
    Product.dwUsed := 1;
    LbBiAddByte(Product, cPREPEND_ARRAY, 0);
  end;

  Carry := 0;
  TmpInt := Pred(N1.dwUsed);
  for Count := 0 to TmpInt do
  begin
    Prd := (PBiByteArray(N1.IntBuf.pBuf)[Count] * N2) + Carry;
    TmpByte := Prd and $00FF;
    Carry := Prd shr 8;
    PBiByteArray(Product.IntBuf.pBuf)[Count] := TmpByte;
    if (Product.dwUsed < Succ(Count)) then
      N1.dwUsed := Succ(Count);
  end;
  LbBiAddByte(Product, Succ(N1.dwUsed), Carry);
  LbBiTrimSigZeros(Product);
end;

procedure LbBiMulByDigit(const N1: LbInteger; const N2: Byte;
  var Product: LbInteger);
begin
  LbBiMulByDigitBase(N1, N2, Product);
  Product.bSign := N1.bSign;
end;

procedure LbBiMulByDigitInPlace(var N1: LbInteger; const N2: Byte);
var
  Product: LbInteger;
  Precis: Integer;
begin
  Precis := (N1.dwUsed + 1) * 2;
  LbBiInit(Product, Precis);
  try
    LbBiMulByDigit(N1, N2, Product);
    if (N1.IntBuf.dwLen < Product.IntBuf.dwLen) then
      LbBiRealloc(N1, Product.IntBuf.dwLen);
    N1.bSign := Product.bSign;
    N1.dwUsed := Product.dwUsed;
    LbBiCopy(N1, Product, Product.dwUsed);
  finally
    LbBiFree(Product);
  end;
end;

procedure LbBiMove(var Fest: LbInteger; const Src: LbInteger;
  const Place, Len: Integer);
var
  Ptr: PByte;
  Size: Integer;
begin
  if (not Assigned(Fest.IntBuf.pBuf)) then
    raise Exception.Create(sBIBufferNotAssigned);

  if (Place = cAPPEND_ARRAY) then
  begin
    if ((Integer(Len) + Fest.dwUsed) > Fest.IntBuf.dwLen) then
      LbBiRealloc(Fest, (Integer(Len) + Fest.dwUsed));

    Ptr := Fest.IntBuf.pBuf;
    Inc(Ptr, Fest.dwUsed);
    move(Src.IntBuf.pBuf^, Ptr^, Len);
    Inc(Fest.dwUsed, Len);
  end
  else
  begin
    Size := Pred(Place) + Integer(Len);
    if Size > Fest.IntBuf.dwLen then
      LbBiRealloc(Fest, Size);
    Ptr := Fest.IntBuf.pBuf;
    Inc(Ptr, Pred(Place));
    move(Src.IntBuf.pBuf^, Ptr^, Len);
    if (Fest.dwUsed < Size) then
      Fest.dwUsed := Size;
  end;
end;

function LbBiCompare(N1, N2: LbInteger): ShortInt;
var
  Count: Integer;
begin
  LbBiTrimSigZeros(N1);
  LbBiTrimSigZeros(N2);
  if (N1.bSign <> N2.bSign) then
  begin
    if (N1.bSign = cPOSITIVE) then
      Result := cGREATER_THAN
    else
      Result := cLESS_THAN;
    Exit;
  end;

  if (N1.dwUsed <> N2.dwUsed) then
  begin
    if (N1.dwUsed > N2.dwUsed) then
    begin
      Result := cGREATER_THAN;
      Exit;
    end
    else
    begin
      Result := cLESS_THAN;
      Exit;
    end;
  end;

  Count := N1.dwUsed;
  while PBiByteArray(N1.IntBuf.pBuf)[Pred(Count)] = PBiByteArray(N2.IntBuf.pBuf)
    [Pred(Count)] do
  begin
    Dec(Count);
    if (Count = 0) then
    begin
      Result := cEQUAL_TO;
      Exit;
    end;
  end;

  if PBiByteArray(N1.IntBuf.pBuf)[Pred(Count)] > PBiByteArray(N2.IntBuf.pBuf)
    [Pred(Count)] then
    Result := cGREATER_THAN
  else
    Result := cLESS_THAN;
end;

procedure LbBiFindLargestUsed(const N1, N2: LbInteger; out Count: Integer);
begin
  if (N1.dwUsed >= N2.dwUsed) then
    Count := N1.dwUsed
  else
    Count := N2.dwUsed;
end;

procedure LbBiAddBase(const N1, N2: LbInteger; var Sum: LbInteger);
var
  Carry: Byte;
  I: Integer;
  Count: Integer;
  TempWord: WORD;
  TempByte: Byte;
begin
  LbBiFindLargestUsed(N1, N2, Count);

  if (LbBiIsZero(N1)) then
  begin
    LbBiCopy(Sum, N2, N2.dwUsed);
    Exit;
  end;

  if (LbBiIsZero(N2)) then
  begin
    LbBiCopy(Sum, N1, N1.dwUsed);
    Exit;
  end;

  Carry := 0;
  if (Succ(Count) > Sum.dwUsed) then
    LbBiRealloc(Sum, Succ(Count));
  for I := 1 to Count do
  begin
    TempWord := LbBiGetByteValue(N1, I) + LbBiGetByteValue(N2, I) + Carry;
    TempByte := TempWord and $00FF;
    Carry := TempWord shr 8;
    PBiByteArray(Sum.IntBuf.pBuf)[Sum.dwUsed] := TempByte;
    Inc(Sum.dwUsed);
  end;
  LbBiAddByte(Sum, cAPPEND_ARRAY, Carry);
  LbBiTrimSigZeros(Sum);
end;

function LbBiAbs(N1, N2: LbInteger): ShortInt;
var
  Count: Integer;
begin
  LbBiTrimSigZeros(N1);
  LbBiTrimSigZeros(N2);

  if (N1.dwUsed <> N2.dwUsed) then
  begin
    if (N1.dwUsed > N2.dwUsed) then
    begin
      Result := cGREATER_THAN;
      Exit;
    end
    else
    begin
      Result := cLESS_THAN;
      Exit;
    end;
  end;

  Count := N1.dwUsed;
  while PBiByteArray(N1.IntBuf.pBuf)[Pred(Count)] = PBiByteArray(N2.IntBuf.pBuf)
    [Pred(Count)] do
  begin
    Dec(Count);
    if (Count = 0) then
    begin
      Result := cEQUAL_TO;
      Exit;
    end;
  end;

  if PBiByteArray(N1.IntBuf.pBuf)[Pred(Count)] > PBiByteArray(N2.IntBuf.pBuf)
    [Pred(Count)] then
    Result := cGREATER_THAN
  else
    Result := cLESS_THAN;
end;

procedure LbBiSubBase(const N1, N2: LbInteger; var Diff: LbInteger);
var
  TempInt: Integer;
  Borrow: WORD;
  Count: Integer;
  I: Integer;
begin
  if (LbBiIsZero(N1)) then
  begin
    LbBiCopy(Diff, N2, N2.dwUsed);
    Exit;
  end;

  if (LbBiIsZero(N2)) then
  begin
    LbBiCopy(Diff, N1, N1.dwUsed);
    Exit;
  end;

  Borrow := 0;
  I := Pred(N1.dwUsed);
  for Count := 0 to I do
  begin
    TempInt := PBiByteArray(N1.IntBuf.pBuf)[Count];
    if (N2.dwUsed < Succ(Count)) then
      TempInt := TempInt - Borrow
    else
      TempInt := TempInt - (PBiByteArray(N2.IntBuf.pBuf)[Count] + Borrow);

    if (TempInt < 0) then
    begin
      Inc(TempInt, cBYTE_POSSIBLE_VALUES);
      Borrow := 1;
    end
    else
      Borrow := 0;

    if (Succ(Diff.dwUsed) > Diff.IntBuf.dwLen) then
      LbBiRealloc(Diff, Succ(Diff.dwUsed));
    PBiByteArray(Diff.IntBuf.pBuf)[Diff.dwUsed] := TempInt;
    Inc(Diff.dwUsed);
  end;
  if (Borrow <> 0) then
    raise Exception.Create(sBISubtractErr);
  LbBiTrimSigZeros(Diff);
end;

procedure LbBiAdd(const N1, N2: LbInteger; var Sum: LbInteger);
var
  Value: ShortInt;
begin
  if (N1.bSign = N2.bSign) then
  begin
    Sum.bSign := N1.bSign;
    LbBiAddBase(N1, N2, Sum);
  end
  else
  begin
    Value := LbBiAbs(N1, N2);
    if (Value = cEQUAL_TO) then
    begin
      LbBiAddByte(Sum, cPREPEND_ARRAY, $00);
      Exit;
    end
    else if (Value = cGREATER_THAN) then
    begin
      Sum.bSign := N1.bSign;
      LbBiSubBase(N1, N2, Sum);
    end
    else
    begin
      Sum.bSign := N2.bSign;
      LbBiSubBase(N2, N1, Sum);
    end;
  end;
end;

procedure LbBiSub(const N1: LbInteger; N2: LbInteger; var Diff: LbInteger);
begin
  N2.bSign := not N2.bSign;
  LbBiAdd(N1, N2, Diff);
end;

procedure LbBiSubInPlace(var N1: LbInteger; const N2: LbInteger);
var
  Difference: LbInteger;
  Precision: Integer;
begin
  if (N1.dwUsed > N2.dwUsed) then
    Precision := Succ(N1.dwUsed)
  else
    Precision := Succ(N2.dwUsed);

  LbBiInit(Difference, Precision);
  try
    LbBiSub(N1, N2, Difference);
    LbBiClear(N1);
    N1.dwUsed := Difference.dwUsed;
    N1.bSign := Difference.bSign;
    if (N1.IntBuf.dwLen < Difference.IntBuf.dwLen) then
      LbBiRealloc(N1, Difference.IntBuf.dwLen);

    LbBiCopy(N1, Difference, Difference.dwUsed);
  finally
    LbBiFree(Difference);
  end;
end;

procedure LbBiDivByDigitBase(const N1: LbInteger; const N2: Byte;
  var Quotient: LbInteger; var Remainder: Byte);
var
  Factor: Byte;
  I: Integer;
  Temp: Integer;
  SigDivd: LongInt;
  LclQT: LongInt;
  Carry: WORD;
  Plc: Integer;
  LclDVD: LbInteger;
  Divisor: Byte;
begin
  LbBiInit(LclDVD, N1.dwUsed);
  Carry := 0;
  try
    if (LbBiIsZero(N1)) then
    begin
      LbBiAddByte(Quotient, cPREPEND_ARRAY, $00);
      Exit;
    end;
    if (N2 = 1) then
    begin
      LbBiCopy(Quotient, N1, N1.dwUsed);
      Exit;
    end;
    if (N2 = 0) then
      raise Exception.Create(sBIZeroDivide);

    LbBiCopy(LclDVD, N1, N1.dwUsed);
    Divisor := N2;

    Factor := LbBiFindFactor(N2);
    if (Factor <> 1) then
    begin
      LbBiMulByDigitInPlace(LclDVD, Factor);
      Divisor := Divisor * Factor;
    end;

    if PBiByteArray(LclDVD.IntBuf.pBuf)[Pred(LclDVD.dwUsed)] >= Divisor then
    begin
      LbBiAddByte(LclDVD, cAPPEND_ARRAY, $00);
    end;

    LbBiClear(Quotient);
    Remainder := 0;

    Plc := Pred(LclDVD.dwUsed);
    if (LclDVD.dwUsed > Quotient.dwUsed) then
      LbBiRealloc(Quotient, LclDVD.dwUsed);
    Carry := 0;
    Temp := Pred(LclDVD.dwUsed);
    for I := Temp downto 0 do
    begin
      SigDivd := (Carry shl 8) or
        (Integer(PBiByteArray(LclDVD.IntBuf.pBuf)[I]));
      if (SigDivd < Divisor) then
      begin
        Carry := SigDivd;
        Dec(Plc);
        continue;
      end;

      LclQT := SigDivd div Divisor;
      if (LclQT <> 0) then
      begin
        if (LclQT >= cBYTE_POSSIBLE_VALUES) then
          LclQT := Pred(cBYTE_POSSIBLE_VALUES);

        while SigDivd < (Divisor * LclQT) do
        begin
          Dec(LclQT);
          if (LclQT = 0) then
            raise Exception.Create(sBIQuotientErr);
        end;
      end;

      if (LclQT <> 0) then
      begin
        PBiByteArray(Quotient.IntBuf.pBuf)[Plc] := LclQT;
        if (Quotient.dwUsed < Succ(Plc)) then
          Quotient.dwUsed := Succ(Plc);

        Carry := SigDivd - (Divisor * LclQT);
      end;
      Dec(Plc);
    end;
  finally
    Remainder := Carry;
    if (Quotient.dwUsed = 0) then
      LbBiAddByte(Quotient, cPREPEND_ARRAY, $00);

    LbBiFree(LclDVD);
    LbBiTrimSigZeros(Quotient);
  end;
end;

procedure LbBiDivByDigit(const N1: LbInteger; const N2: Byte;
  var Quotient: LbInteger; var Remainder: Byte);
begin
  LbBiDivByDigitBase(N1, N2, Quotient, Remainder);
  Quotient.bSign := N1.bSign;
end;

procedure LbBiDivByDigitInPlace(var N1: LbInteger; const N2: Byte;
  var Remainder: Byte);
var
  Temp: LbInteger;
  Precision: Integer;
begin
  Precision := (N1.dwUsed + 1) * 2;
  LbBiInit(Temp, Precision);
  try
    LbBiDivByDigit(N1, N2, Temp, Remainder);

    N1.dwUsed := Temp.dwUsed;
    N1.bSign := Temp.bSign;
    if (N1.IntBuf.dwLen < Temp.IntBuf.dwLen) then
      LbBiRealloc(N1, Temp.IntBuf.dwLen);
    LbBiCopy(N1, Temp, Temp.dwUsed);
  finally
    LbBiFree(Temp);
  end;
end;

procedure LbBiDivBase(const N1, N2: LbInteger;
  var Quotient, Remainder: LbInteger);
var
  Factor: Byte;
  InxQ: Integer;
  InxX: Integer;
  TmpByte: Byte;
  TempInt: Integer;
  SigDigit: Byte;
  LclQT: LongInt;
  LclDVD: LbInteger;
  LclDSR: LbInteger;
  TempDR: LbInteger;
  TempBN: LbInteger;
  SigDivd: LongInt;
begin
  LbBiInit(LclDVD, N1.dwUsed);
  LbBiInit(LclDSR, N1.dwUsed);
  LbBiInit(TempDR, N1.dwUsed);
  LbBiInit(TempBN, N1.dwUsed);
  try
    if (N1.dwUsed < 1) or (N2.dwUsed < 1) then
      raise Exception.Create(sBINoNumber);

    if LbBiIsZero(N1) then
    begin
      LbBiAddByte(Quotient, cPREPEND_ARRAY, $00);
      LbBiAddByte(Remainder, cPREPEND_ARRAY, $00);
      Exit;
    end;

    if LbBiIsOne(N2) then
    begin
      LbBiCopy(Quotient, N1, N1.dwUsed);
      LbBiAddByte(Remainder, cPREPEND_ARRAY, $00);
      Exit;
    end;
    if LbBiIsZero(N2) then
      raise Exception.Create(sBIZeroDivide);

    LbBiCopy(LclDVD, N1, N1.dwUsed);
    LbBiCopy(LclDSR, N2, N2.dwUsed);
    LbBiTrimSigZeros(LclDSR);

    TmpByte := PBiByteArray(LclDSR.IntBuf.pBuf)[Pred(LclDSR.dwUsed)];
    if (TmpByte = 0) then
      raise Exception.Create(sBIZeroFactor);

    Factor := LbBiFindFactor(TmpByte);
    if (Factor <> 1) then
    begin
      LbBiMulByDigitInPlace(LclDVD, Factor);
      LbBiMulByDigitInPlace(LclDSR, Factor);
    end;

    if PBiByteArray(LclDVD.IntBuf.pBuf)[Pred(LclDVD.dwUsed)] >=
      PBiByteArray(LclDSR.IntBuf.pBuf)[Pred(LclDSR.dwUsed)] then
    begin
      LbBiAddByte(LclDVD, cAPPEND_ARRAY, $00);
    end;

    while (LclDVD.dwUsed < LclDSR.dwUsed) do
      LbBiAddByte(LclDVD, cAPPEND_ARRAY, $00);

    InxQ := LclDVD.dwUsed - LclDSR.dwUsed + 1;
    InxX := LclDVD.dwUsed;

    LbBiClear(Quotient);
    LbBiClear(Remainder);

    SigDigit := PBiByteArray(LclDSR.IntBuf.pBuf)[Pred(LclDSR.dwUsed)];
    if (SigDigit = 0) then
    begin
      TempInt := Pred(LclDSR.dwUsed);
      while SigDigit = 0 do
      begin
        SigDigit := PBiByteArray(LclDSR.IntBuf.pBuf)[TempInt];
        Dec(TempInt);
        if TempInt < 0 then
          raise Exception.Create(sBIQuotientErr);
      end;
    end;

    while InxQ >= 1 do
    begin
      if (LclDVD.dwUsed = 1) then
        SigDivd := PBiByteArray(LclDVD.IntBuf.pBuf)[0]
      else
        SigDivd := Integer(PBiByteArray(LclDVD.IntBuf.pBuf)[InxX])
          shl 8 + PBiByteArray(LclDVD.IntBuf.pBuf)[Pred(InxX)];

      LclQT := SigDivd div SigDigit;
      if (LclQT <> 0) then
      begin
        if (LclQT >= cBYTE_POSSIBLE_VALUES) then
          LclQT := Pred(cBYTE_POSSIBLE_VALUES);

        LbBiClear(TempDR);
        LbBiMove(TempDR, LclDSR, InxQ, LclDSR.dwUsed);

        LbBiMulByDigitInPlace(TempDR, LclQT);

        while (LbBiCompare(LclDVD, TempDR) = cLESS_THAN) do
        begin
          Dec(LclQT);
          if (LclQT = 0) then
            break;

          LbBiClear(TempDR);
          LbBiMove(TempDR, LclDSR, InxQ, LclDSR.dwUsed);

          LbBiMulByDigitInPlace(TempDR, LclQT);
        end;
      end;

      if (LclQT <> 0) then
      begin

        LbBiAddByte(Quotient, InxQ, LclQT);
        LbBiSubInPlace(LclDVD, TempDR);
      end;
      Dec(InxX);
      Dec(InxQ);
    end;

    LbBiCopy(Remainder, LclDVD, LclDVD.dwUsed);

    if (Factor <> 0) then
    begin
      if (Remainder.dwUsed > 1) then
      begin
        LbBiDivByDigitInPlace(Remainder, Factor, TmpByte);
      end
      else if (Remainder.dwUsed = 1) then
      begin
        TmpByte := PBiByteArray(Remainder.IntBuf.pBuf)[0];
        TmpByte := TmpByte div Factor;
        LbBiAddByte(Remainder, cPREPEND_ARRAY, TmpByte);
      end;
    end;
  finally
    LbBiFree(LclDVD);
    LbBiFree(LclDSR);
    LbBiFree(TempDR);
    LbBiFree(TempBN);

    if (Quotient.dwUsed = 0) then
      LbBiAddByte(Quotient, cPREPEND_ARRAY, $00);

    if (Remainder.dwUsed = 0) then
    begin
      LbBiAddByte(Remainder, cPREPEND_ARRAY, $00);
    end;

    LbBiTrimSigZeros(Quotient);
    LbBiTrimSigZeros(Remainder);
  end;
end;

procedure LbBiDiv(const N1, N2: LbInteger; var Quotient, Remainder: LbInteger);
begin
  LbBiDivBase(N1, N2, Quotient, Remainder);
  if (N1.bSign = N2.bSign) then
    Quotient.bSign := cPOSITIVE
  else
    Quotient.bSign := cNEGATIVE;
end;

procedure LbBiMod(const N1, N2: LbInteger; var Remainder: LbInteger);
var
  Quotient: LbInteger;
begin
  LbBiInit(Quotient, N2.dwUsed);
  LbBiDiv(N1, N2, Quotient, Remainder);
  LbBiFree(Quotient);
end;

procedure LbBiModInPlace(var N1: LbInteger; const Modulas: LbInteger);
var
  Remainder: LbInteger;
begin
  LbBiInit(Remainder, Modulas.dwUsed);
  LbBiMod(N1, Modulas, Remainder);

  LbBiClear(N1);
  N1.dwUsed := Remainder.dwUsed;
  N1.bSign := Remainder.bSign;

  if (N1.IntBuf.dwLen < Remainder.IntBuf.dwLen) then
    LbBiRealloc(N1, Remainder.IntBuf.dwLen);

  LbBiCopy(N1, Remainder, Remainder.dwUsed);
  LbBiFree(Remainder);
end;

procedure LbBiPowerAndMod(const I1, Exponent, Modulus: LbInteger;
  var Result: LbInteger);
var
  BitCount: Integer;
  I: Integer;
  TempByte: Byte;
  Hold: LbInteger;
begin
  LbBiClear(Result);
  if (LbBiIsZero(Exponent)) then
  begin
    LbBiAddByte(Result, cPREPEND_ARRAY, $01);
    Exit;
  end;
  LbBiInit(Hold, cDEFAULT_PRECISION);
  try
    I := Exponent.dwUsed;
    LbBiAddByte(Result, cPREPEND_ARRAY, $01);
    while I > 0 do
    begin
      TempByte := LbBiGetByteValue(Exponent, I);
      Dec(I);
      BitCount := 8;
      TempByte := LbBiReverseBits(TempByte);

      while BitCount > 0 do
      begin
        LbBiMultInPlace(Result, Result);
        LbBiModInPlace(Result, Modulus);
        if Odd(TempByte) then
        begin
          LbBiMultInPlace(Result, I1);
          LbBiModInPlace(Result, Modulus);
        end;
        TempByte := TempByte shr 1;
        Dec(BitCount);
      end;
    end;
  finally
    LbBiFree(Hold);
  end;
end;

procedure LbBiPowerAndModInPLace(var I1: LbInteger;
  const Exponent, Modulus: LbInteger);
var
  Result: LbInteger;
begin
  LbBiInit(Result, cUSE_DEFAULT_PRECISION);
  try
    LbBiPowerAndMod(I1, Exponent, Modulus, Result);
    LbBiClear(I1);
    I1.dwUsed := Result.dwUsed;
    I1.bSign := Result.bSign;
    if (I1.IntBuf.dwLen < Result.IntBuf.dwLen) then
      LbBiRealloc(I1, Result.IntBuf.dwLen);
    LbBiCopy(I1, Result, Result.dwUsed);
  finally
    LbBiFree(Result);
  end;
end;

{TSerialDecoder}

function TSerialDecoder.GetDecodedBytes: TBytes;
var
  Serial, Exponent, Modulas, Output: LbInteger;
begin
  Serial := GetLargeIntFromBytes(FSerialNum);
  try
    Modulas := GetLargeIntFromBytes(FPublicMod);
    try
      Exponent := GetLargeIntFromBytes(FPublicExp);
      try
        LbBiInit(Output, cUSE_DEFAULT_PRECISION);
        try
          LbBiPowerAndMod(Serial, Exponent, Modulas, Output);
          Result := GetBytesFromLargeInt(Output);
        finally
          LbBiFree(Output);
        end;
      finally
        LbBiFree(Exponent);
      end;
    finally
      LbBiFree(Modulas);
    end;
  finally
    LbBiFree(Serial);
  end;
end;

function TSerialDecoder.GetBytesFromBase64(const Value: string): TBytes;
{$IFDEF FPC}
var
  Input: TStringStream;
  Output: TMemoryStream;
  Decoder: TBase64DecodingStream;
{$ENDIF}
begin
  {$IFDEF FPC}
  Input := TStringStream.Create(Value);
  try
    Decoder := TBase64DecodingStream.Create(Input);
    try
      Output := TMemoryStream.Create;
      try
        Result := nil;
        Output.CopyFrom(Decoder, Decoder.size);
        SetLength(Result, Output.size);
        Move(Output.Memory^, Result[0], Output.size);
      finally
        Output.Free;
      end;
    finally
      Decoder.Free;
    end;
  finally
    Input.Free;
  end;
  {$ELSE}
  Result := TNetEncoding.Base64.DecodeStringToBytes(Value);
  {$ENDIF}
end;

function TSerialDecoder.GetLargeIntFromBytes(const Bytes: TBytes): LbInteger;
var
  I: Integer;
begin
  Result.bSign := cDEFAULT_SIGN;
  Result.IntBuf.pBuf := AllocMem(Length(Bytes));
  Result.IntBuf.dwLen := Length(Bytes);
  Result.dwUsed := Length(Bytes);
  for I := High(Bytes) downto Low(Bytes) do
    Result.IntBuf.pBuf[High(Bytes) - I] := Bytes[I];
end;

function TSerialDecoder.GetBytesFromLargeInt(const Value: LbInteger): TBytes;
var
  I: Integer;
begin
  Result := nil;
  SetLength(Result, Value.dwUsed);
  for I := Low(Result) to High(Result) do
    Result[High(Result) - I] := Value.IntBuf.pBuf[I];
end;

function TSerialDecoder.GetBuildDate: TDate;
begin
  {Constant is in format YYYY-MM-DD}
  Result := EncodeDate(StrToInt(Copy(BuildDate, 1, 4)),
    StrToInt(Copy(BuildDate, 6, 2)), StrToInt(Copy(BuildDate, 9, 2)));
end;

procedure TSerialDecoder.SetPublicExp(const Value: string);
begin
  FPublicExp := GetBytesFromBase64(Value);
  if Length(FPublicExp) >= 8 then
    Assert(Length(FPublicExp) mod 8 = 0);
end;

procedure TSerialDecoder.SetPublicMod(const Value: string);
begin
  FPublicMod := GetBytesFromBase64(Value);
  if Length(FPublicMod) >= 8 then
    Assert(Length(FPublicMod) mod 8 = 0);
end;

procedure TSerialDecoder.SetSerialNum(const Value: string);
begin
  FSerialNum := GetBytesFromBase64(Value);
end;

procedure TSerialDecoder.SetProductCode(const Value: UInt64);
begin
  FProductCode := Value;
end;

function TSerialDecoder.VMProtectSetSerialNumber(
  const SerialNumber: string): LongWord;
var
  HashData: PByte;
  HashSize: Integer;
  DataStart: Integer;
  Digest: {$IFDEF FPC}TSHA1Digest{$ELSE}THashSHA1{$ENDIF};
  BytesStream: TBytesStream;
  Buffer: TBytes;
  Len: Byte;
begin
  try
    {Assign serial number}
    SetSerialNum(SerialNumber);
    {Check the black list}
    CheckSerialBlackList;
    {Decode license key}
    BytesStream := TBytesStream.Create(GetDecodedBytes);
    try
      {Find data section}
      while BytesStream.ReadByte <> 0 do
      begin
        {Skip random data}
      end;
      DataStart := BytesStream.Position;
      while BytesStream.Position < BytesStream.Size do
      begin
        case BytesStream.ReadByte of
          $01: {Version}
          begin
            if BytesStream.ReadByte <> 1 then
              Abort;
          end;
          $02: {User name}
          begin
            Len := BytesStream.ReadByte;
            SetLength(Buffer, Len);
            BytesStream.ReadBuffer(Buffer[0], Length(Buffer));
            {$IFDEF FPC}
            FSerialNumberData.wUserName := TEncoding.UTF8.GetString(Buffer);
            {$ELSE}
            WStrPLCopy(@FSerialNumberData.wUserName, TEncoding.UTF8.GetString(
              Buffer), Length(FSerialNumberData.wUserName) - 1);
            {$ENDIF}
          end;
          $03: {E-mail}
          begin
            Len := BytesStream.ReadByte;
            SetLength(Buffer, Len);
            BytesStream.ReadBuffer(Buffer[0], Length(Buffer));
            {$IFDEF FPC}
            FSerialNumberData.wEMail := TEncoding.UTF8.GetString(Buffer);
            {$ELSE}
            WStrPLCopy(@FSerialNumberData.wEMail, TEncoding.UTF8.GetString(
              Buffer), Length(FSerialNumberData.wEMail) - 1);
            {$ENDIF}
          end;
          $04: {Hardware ID}
          begin
            BytesStream.Seek(BytesStream.ReadByte, soFromCurrent);
          end;
          $05: {License expiration date}
          begin
            FSerialNumberData.dtExpire.bDay := BytesStream.ReadByte;
            FSerialNumberData.dtExpire.bMonth := BytesStream.ReadByte;
            FSerialNumberData.dtExpire.wYear := BytesStream.ReadWord;
          end;
          $06: {Maximum operation time}
          begin
            FSerialNumberData.bRunningTime := BytesStream.ReadByte;
          end;
          $07: {Product code}
          begin
            if BytesStream.ReadQWord <> FProductCode then
              Abort;
          end;
          $08: {User data}
          begin
            FSerialNumberData.nUserDataLength := BytesStream.ReadByte;
            BytesStream.ReadBuffer(FSerialNumberData.bUserData,
              FSerialNumberData.nUserDataLength);
          end;
          $09: {Maximum build date}
          begin
            FSerialNumberData.dtMaxBuild.bDay := BytesStream.ReadByte;
            FSerialNumberData.dtMaxBuild.bMonth := BytesStream.ReadByte;
            FSerialNumberData.dtMaxBuild.wYear := BytesStream.ReadWord;
          end;
          $FF: {Checksum}
          begin
            HashData := PByte(BytesStream.Memory) + DataStart;
            HashSize := BytesStream.Position - DataStart - 1;
            {$IFDEF FPC}
            Digest := SHA1Buffer(HashData^, HashSize);
            if NtoBE(BytesStream.ReadDWord) <> PLongWord(@Digest)^ then
              Abort;
            {$ELSE}
            Digest := THashSHA1.Create;
            Digest.Update(HashData^, HashSize);
            if THash.ToBigEndian(BytesStream.ReadDWord) <> PLongWord(
              @Digest.HashAsBytes[0])^ then
                Abort;
            {$ENDIF}
            Break;
          end
        else
          Abort;
        end;
      end;
    finally
      BytesStream.Free;
    end;
    {Loop finished and we check data}
    if (LongWord(FSerialNumberData.dtExpire) <> 0) and
      (Date > EncodeDate(
        FSerialNumberData.dtExpire.wYear,
        FSerialNumberData.dtExpire.bMonth,
        FSerialNumberData.dtExpire.bDay)) then
      begin
        FSerialNumberData.nState := SERIAL_STATE_FLAG_DATE_EXPIRED;
        Exit(FSerialNumberData.nState);
      end;
    if (LongWord(FSerialNumberData.dtMaxBuild) <> 0) and
      (GetBuildDate > EncodeDate(
        FSerialNumberData.dtMaxBuild.wYear,
        FSerialNumberData.dtMaxBuild.bMonth,
        FSerialNumberData.dtMaxBuild.bDay)) then
      begin
        FSerialNumberData.nState := SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED;
        Exit(FSerialNumberData.nState);
      end;
    {Everything appears to be fine}
    FSerialNumberData.nState := SERIAL_STATE_SUCCESS;
    Result := FSerialNumberData.nState;
  except
    FSerialNumberData.nState := SERIAL_STATE_FLAG_INVALID;
    Result := FSerialNumberData.nState;
  end;
end;

function TSerialDecoder.VMProtectGetSerialNumberData(
  Data: PVMProtectSerialNumberData; DataSize: Integer): Boolean;
begin
  Result := SizeOf(FSerialNumberData) = DataSize;
  if Result then
    Move(FSerialNumberData, Data^, DataSize);
end;

procedure TSerialDecoder.CheckSerialBlackList;
const
  BlackList: TArray<string> = [{$I BlackList.inc}];
var
  Value, Hash: string;
  Stream: TMemoryStream;
begin
  {$IFDEF FPC}
  Hash := MD5Print(MD5Buffer(FSerialNum[0], Length(FSerialNum)));
  {$ELSE}
  Stream := TMemoryStream.Create;
  try
    Stream.WriteBuffer(FSerialNum[0], Length(FSerialNum));
    Stream.Position := 0;
    Hash := THashMD5.GetHashString(Stream);
  {$ENDIF}
  for Value in BlackList do
    if Value = Hash then
      Abort;
  {$IFNDEF FPC}
  finally
    Stream.Free;
  end;
  {$ENDIF}
end;

{$IFNDEF FPC}

function TBytesStream.ReadByte: Byte;
begin
  ReadBuffer(Result, SizeOf(Result));
end;

function TBytesStream.ReadWord: Word;
begin
  ReadBuffer(Result, SizeOf(Result));
end;

function TBytesStream.ReadDWord: UInt32;
begin
  ReadBuffer(Result, SizeOf(Result));
end;

function TBytesStream.ReadQWord: UInt64;
begin
  ReadBuffer(Result, SizeOf(Result));
end;

{$ENDIF}

end.


В файл BuildDate.inc идет дата сборки в YYYY-MM-DD, например '2023-01-05'. В BlackList.inc идут MD5 хеши ключей по чьим ордерам был рефанд или чаржбек. Система сборки запускает скрипт на моем сервере, который получает список таких ордеров от пайпро и вычисляет хеши таких ключей. Таким образом украденные и отмененные ключи блокируются автоматически при пересборке софта.

  Использовать класс примерно так:
SerialDecoder := TSerialDecoder.Create;
SerialDecoder.SetProductCode(UInt64($XXXXXXXXXXXXXXX));//<- в VMP файле это Base64, его надо сконвертировать в число UInt64.
SerialDecoder.SetPublicExp('AAEAAQ==');
SerialDecoder.SetPublicMod('сюда публичный ключ в Base64');
Flags := SerialDecoder.VMProtectSetSerialNumber('текст ключа что ввел пользователь');
if Flags <> 0 then
begin
  //Что-то не так, проверяйте SERIAL_STATE_FLAG_***
end
else
begin
  Info := Default(TVMProtectSerialNumberData);
  if SerialDecoder.VMProtectGetSerialNumberData(@Info, SizeOf(Info)) then
  //Все хорошо, достаем из ключа данные 
end;
Re[3]: Шифрование кода – 2023
От: Черный 😈 Властелин Австралия https://www.softperfect.com
Дата: 27.02.23 12:08
Оценка: +2
Здравствуйте, sfsoft, Вы писали:
S>Здравствуйте, Черный 😈 Властелин, Вы писали:
S>Т.е. привязку к оборудованию ты не используешь? А как тогда отслеживается количество копий? Ключ можно всем друзьям раздать, например.

Никак, все на доверии. Если кто-то будет использовать бесплатно, это не страшно, другие заплатят.

У меня несколько десятков ордеров в день, если переписываться с каждым у кого не работает ключ, поменялись железки или там закончились активации, когда тогда писать код?
Re[17]: Шифрование кода – 2023
От: rudzuk  
Дата: 06.03.23 08:35
Оценка: -2
Здравствуйте, drVanо, Вы писали:

Знатно у Ваньки полыхнуло. А ведь моим единственным тезисом было
Автор: rudzuk
Дата: 28.02.23
, что при проверке целостности нет смысла проверять весь образ, достаточно ограничиться критически важными областями, и что позиционно-независимый код написать не сложно. Весна, что ты делаешь...
avalon/3.0.2
Re[3]: Шифрование кода – 2023
От: Carc Россия https://vk.com/gosha_mazov
Дата: 27.02.23 10:21
Оценка: 9 (1)
Здравствуйте, Khimik, Вы писали:

K>Здравствуйте, Черный 😈 Властелин, Вы писали:



ЧВ>>3) Антивирусы тоже не особо понимают код виртуальной машины, а следовательно на всякий случай задетектим его как вирус. Короче говоря ложных срабатываний полно, особенно у говно-антивирусов второго эшелона.


K>Это, как я понимаю, главная проблема. Может быть, стоит купить протектор, но использовать его без функции шифрования кода? Что он ещё умеет?

Причем тут шифрование? Проблема в другом: важное выделено жирным.

Антивирусы тоже не особо понимают код виртуальной машины

Хоть шифруй, хоть не шифруй — внутри все равно код виртуальной машины, которую анвири вкурить со смыслом не могут.

Но зло в другом!
Виртуальная машина или нет, шифрован код или нет — анвири все равно тупят, тупили и тупить будут.

История леденящая душу!
В одном из моих продуктов понадобилось мне, чтоб одна работающая копия могла завершить предыдущую, уже работающую.

Сказано-сделано!
Схема простая:
1) заводится именованный эвент, который ожидается в фоновом потоке в первом уже работающем экземпляре.
2) Второй экземпляр софтины, если запущен с определенным ключом командной строки (MySoft.exe /флаг_с_вещами_на_выход) открывает так называемый тьфу-млин, открывает вышеупомянутый эвент, сигналит, и тут же завершается (второй экземпляр)
3) первый (уже работающий) экземпляр, ловит в фоновом потоке событие, и как-то уже разруливается по делу, что мол пора завершаться: закрыть окна, сказать пользователю до свиданья, все дела.

Короче, проще пареной репы.
То бишь всю суть, делает этот именнованный эвент, ожидание и все такое. Фигня-война — написалось-причесалось за вечер со всеми делами (секурность, как закрываться в уже работающем экземпляре, что делать если закрываться пользователь все таки НЕ хочет — в общем все краевые ситуации).

Ну и прикрутилась к этой схеме простенькая отдельная exe-тулза для завершения работающего экземпляра приложения.
Совсем простенькая!
3 строчки кода(три, Карл! три): OpenEvent + SetEvent + CloseHandle.

И что тут блин началось!
Ага, млин да это мелварь, да всё такое, да она лезет в непонятные эвенты Виндовс.....
А я ж прихирел! Несколько раз перепроверял, пересобирал, еще раз перепроверял. И еще раз прихеривал, но уже сильнее...
Ан фиг! Мелварь и всё точка!

И ничего так, что основная софтина с десятком подобных эвентов дело имеет — с ней всё в порядке.
И опять же ничего так, что оная основная софтина шарится по чужим адресным пространствам, и какими только способами туда не пролезает. Моя софтина — в чужое адресное пространство "в дверь", а там заперто. Гуй с ним. Тогда через окно? Законопачено? Тады через чердак! И там не пущатЪ? Ну да фиг с Вами, тогда и по кул-хацки можно...
Ну и мало того, что лазиет по чужим адресным пространствам — дык это основная софтина, еще и данные оттуда, из-за забора к себе отгружает.

Казалось бы, вот на чтобы ругаться бы... Ан фиг! К основной софтине претензий нет.
А мелко-поделке в 3 строки кода (те самые OpenEvent+SetEvent...) — ПРЕТЕНЗИИ ТАКИ ЕСТЬ!!!

И это не говноантивирусы, это все таки был Avast — не самый поганый антивирус!

PS: зато родился лайф-хак как общаться в тех поддержкой Аваста на предмет false positive detection.
1) Начинать сходу по русски! Причем на русском-матерном! Пускай шекспирят на конференциях, да журналюгам очки втирают на аглицком. Здесь разговор по делу.

2) Начинать сие послание султанам-нах, нужно с эпической фразы: "Здорово щеглы, плятЪ!!!" (C).

3) Далее перечисление трех упомянутых функций, описание что и зачем, ссылка на официальную доку на официальном же сайте.

4-ое по списку но не менее важное: предложение выгнать нафиг студентов — рукожопых лоботрясов (C) аналитигоф первой линии!. Ну или хотя бы отправить в неоплачиваемый отпуск. А сэкомноленные средствА — всенепременно и заобязательно совместно — пропить. В хорошей компании, отличным чешским пивом.

Проверено!
Результат 100-процентный.
Забегали, засуеитились. Через секунду тикет висел уже в поддержке, через пару часов уже отписались в стиле "извини, колллега, хирню сморозили". Через два часа уже был именно что официальный ответ: ля-ля, ля-ля — наш косяк — в течении суток выложим исправления, и публичные базы обновим при ближайшем же ежедневном апдейте сигнатур.
Aml Pages Home
Отредактировано 27.02.2023 10:45 Carc . Предыдущая версия . Еще …
Отредактировано 27.02.2023 10:39 Carc . Предыдущая версия .
Отредактировано 27.02.2023 10:38 Carc . Предыдущая версия .
Отредактировано 27.02.2023 10:37 Carc . Предыдущая версия .
Отредактировано 27.02.2023 10:36 Carc . Предыдущая версия .
Отредактировано 27.02.2023 10:34 Carc . Предыдущая версия .
Отредактировано 27.02.2023 10:31 Carc . Предыдущая версия .
Отредактировано 27.02.2023 10:26 Carc . Предыдущая версия .
Re: Шифрование кода – 2023
От: wantus  
Дата: 26.02.23 22:30
Оценка: 1 (1)
Здравствуйте, Khimik, Вы писали:

K>Я раньше пользовался The Enigma Protector, сейчас по идее надо его обновить, вопрос насколько всё это сейчас актуально? Как сейчас на Западе с хакерами — их прижали, или наоборот они распоясались? Есть ли среди хакеров такие, которые продают наши программы за крипту?

K>Я наверно не смогу шифровать код Mac версии своей программы, поэтому такая мысль – может быть, довольно важно сделать кейген для Mac другой, с другим алгоритмом, чтобы хакеры не смогли, разобрав код Mac-программы, написать кейген для Windows-версии?
K>Какой шифратор лучше выбрать — Execryptor или есть что-то новое?
K>Не решена ли как-то проблема ложных срабатываний антивирусов на шифрованный код?

Шифрование кода, в подавляющем числе случаем — это абсолютный overkill. Нормальный рабочий вариант — это внутренний проверки на модификацию кода, как статическую (патчи и кряки) так и динамическую (API hooking, DLL side loading, dynamic patching, etc.). Если таких проверок в коде, скажем, несколько десятков и они разнесены по времени, то вычистить их все крайне сложно и, главное, трудо- и времяемко. Это принципиальный момент, потому что хакерская сцена состоит на 99.9% из детей-любителей с соотвествующим уровнем скилзов, который хакают, чтобы выложить "релиз" и поразить камрадов своими l33t скилзами. Они могут снять подпись с exe и пропатчить `if (valid_license)`. Какая-то часть может посмотреть чуть дальше, но в общем и целом они ломают только то, что быстро ломается. Оставшиеся 0.1% — супер-квалифицированые товарищи, которые ломают в основном динамическими патчами. Сломать они сломают, но чистый 100% хак займет приличное количество времени. Им по-большому счету связываться с этим не имеет смысла, только если это не платный заказ или не какой-то high-profile софт.

Где-то так.
Re[5]: Шифрование кода – 2023
От: wantus  
Дата: 28.02.23 09:48
Оценка: 1 (1)
Здравствуйте, Khimik, Вы писали:

K>Здравствуйте, wantus, Вы писали:


W>>1. Добавить проверку подписи собственного exe (через WinVerifyTrust). По случайному таймеру, не сразу. Проверили, запомнили результат, запустили еще один таймер на несколько минут. Когда таймер сработал, покрасили background окна в красный цвет, или перевернули его кверх ногами, или еще чего-нибудь очевидно намеренное и кривое. Это уже достаточно противно патчить.


K>Я сходу не нагуглил, что делает WinVerifyTrust, просьба пояснить.


https://learn.microsoft.com/en-us/windows/win32/seccrypto/example-c-program--verifying-the-signature-of-a-pe-file

K>"запомнили результат" — какой результат, число возвращаемое WinVerifyTrust?


Да, результат проверки подписи exe.

K>Я привык делать всё по-своему, мне легче изобрести велосипед, чем разбираться в чужих решениях.


Это нормально, все такие.

K>Можно подсчитать контрольную сумму exe файла собственной программы.


Можно. Причем можно её прописывать в сам файл уже после того как он был подписан.

https://blog.barthe.ph/2009/02/22/change-signed-executable/

K>Это не защитит от кейгена


2023 на дворе. Какие нафиг кейгены?

K>... юзер может получить новый ключ на сайте


То есть серверная часть уже есть? Это замечательно.

1. Напишите тривиальный HTTP/GET клиент на WinHTTP (пример есть на msdn), который из программы посылает на сайт MachineGUID и получает назад подписанную RSA ключом "лицензию" — [MachineGUID + срок годности + подпись]. Причем это можно даже по http делать, без https. На серверной стороне всё что требуется это вызвать "openssl rsautl -sign ..." из командной строки.
2. Зашейте public часть ключа в программу и периодически проверяйте наличие (а) лицензии (б) её подпись (в) соответствие MachineGUID.
3. Плюс добавьте отложенные по времени проверки целостности exe.

Этот комплект убирает риск 99% хаков.

И таки подписывайте ваши exe. Как минимум это делает очевидным, когда exe был модифицирован.
Re[2]: Шифрование кода – 2023
От: Khimik  
Дата: 27.02.23 08:03
Оценка: -1
Здравствуйте, Черный 😈 Властелин, Вы писали:


ЧВ>3) Антивирусы тоже не особо понимают код виртуальной машины, а следовательно на всякий случай задетектим его как вирус. Короче говоря ложных срабатываний полно, особенно у говно-антивирусов второго эшелона.


Это, как я понимаю, главная проблема. Может быть, стоит купить протектор, но использовать его без функции шифрования кода? Что он ещё умеет?
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[2]: Шифрование кода – 2023
От: Khimik  
Дата: 27.02.23 08:06
Оценка: :)
Здравствуйте, wantus, Вы писали:

W>Шифрование кода, в подавляющем числе случаем — это абсолютный overkill. Нормальный рабочий вариант — это внутренний проверки на модификацию кода, как статическую (патчи и кряки) так и динамическую (API hooking, DLL side loading, dynamic patching, etc.).


Я не очень понял, что это за проверки, поясните их принцип.
Полагаю, эти проверки устроены так, что в случае срабатывания отрубаются какие-то отдельные фичи программы? Так что программой в целом пользоваться по-прежнему можно, но будет мотивация всё-таки купить официальную версию. По идее, хакеры не разбираются во взламываемой программе и не будут проверять функциональность этих случайных фич.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[5]: Шифрование кода – 2023
От: rudzuk  
Дата: 28.02.23 14:09
Оценка: +1
Здравствуйте, Sharowarsheg, Вы писали:

S> ЧВ>У меня несколько десятков ордеров в день, если переписываться с каждым у кого не работает ключ, поменялись железки или там закончились активации, когда тогда писать код?


S> С другой стороны, если у тебя несколько десятков ордеров в день, зачем писать код?


Здорово же, когда работа совпадает с любимым делом
avalon/3.0.2
Re[13]: Шифрование кода – 2023
От: rudzuk  
Дата: 01.03.23 12:40
Оценка: :)
Здравствуйте, drVanо, Вы писали:

V> Ну хорошо, давайте вы напишете такой код + все что сюда понаписали ТС-у, а я его попробую взломать. Как вам такое?


Мне сантиметрами меряться не очень интересно. Давай я признаю, что у тебя длиннее, а ты признаешь, что написать код без релоков это не бином Ньютона?
avalon/3.0.2
Re[15]: Шифрование кода – 2023
От: rudzuk  
Дата: 01.03.23 13:20
Оценка: -1
Здравствуйте, drVanо, Вы писали:

V> R>Мне сантиметрами меряться не очень интересно. Давай я признаю, что у тебя длиннее, а ты признаешь, что написать код без релоков это не бином Ньютона?


V> Давай ты лучше признаешь, что все что ты предлагал ТС-у вместе с вантусом — это полный шлак.


Ты невнимательно читаешь. Я ТС'у ничего не предлагал. Я сказал, что контролировать целостность критически важного кода и данных не так сложно, как ты пытаешься преподнести. Поэтому — нет.
avalon/3.0.2
Re[17]: Шифрование кода – 2023
От: rudzuk  
Дата: 01.03.23 14:04
Оценка: -1
Здравствуйте, drVanо, Вы писали:

V> R>Ты невнимательно читаешь. Я ТС'у ничего не предлагал. Я сказал, что контролировать целостность критически важного кода и данных не так сложно, как ты пытаешься преподнести. Поэтому — нет.


V> Вот же ты сам предлагал:


V> Ты все усложняешь. Целостность всего образа проверять смысла нет. Достаточно проверить целостность критически важных данных и кода. Это делается проще, чем ты описываешь.


V> В итоге ты предлагаешь человеку самостоятельно писать PIC, чтобы потом проверять целостность участков кода. Я даже больше чем уверен, что он даже их (эти самые участки) не найдет.


Нет, ТС'у я ничего не предлагаю. Я сказал, отвечая совсем не ТС'у, что имеет место быть очевидное усложнение достаточно простой задачи.

V> К слову сказать твой открытый ключ можно подменить прямо в памяти в момент "десериализации" его в BigInteger даже не трогая код, который с ним работает, и все твои проверки на целостность будут просто бесполезны.


Это все фантазии на свободную тему.
avalon/3.0.2
Re[19]: Шифрование кода – 2023
От: rudzuk  
Дата: 05.03.23 12:50
Оценка: -1
Здравствуйте, drVanо, Вы писали:

V> K13>Просто публичный ключ должен вычисляться, а не лежать константой в области данных.


V> Для того чтобы понимать как защитить открытый ключ от подмены, нужно как минимум представлять как работают стандартные библиотеки, которые манипулируют большими числами. Открытый ключ это просто большое число, которое загоняется в класс BigInteger, и дальше над ним производят математические действия (внутри modpow). Дак вот перехватив любой из методов, которые используются при проверке правильности серийного номера, можно подменить открытый ключ прямо в памяти BigInteger-а (особо "умные" делают это через перехват стандартной функции выделения памяти из CRT).


Вот я и говорю — фантазии это, с прикольными допущениями, что будет использоваться стандартное API.
avalon/3.0.2
Re[21]: Шифрование кода – 2023
От: rudzuk  
Дата: 05.03.23 16:05
Оценка: -1
Здравствуйте, drVanо, Вы писали:

V> Я вам уже предлагал перейти от теории к практике, но вы зассали
Автор: rudzuk
Дата: 01.03.23
.


Да нет же, я сказал, что мне сантиметрами меряться не интересно, и что готов признать твое превосходство. У тебя удивительная способность не понимать, что тебе говорят

V> Зачем заново начинать этот разговор?


Похоже, зря я это, учитывая вышесказанное.
avalon/3.0.2
Re: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 05.03.23 16:06
Оценка: +1
Здравствуйте, Khimik, Вы писали:

Безотносительно используемого протектора могу порекомендовать несколько способов, которые могут усложнить жизнь крякеру:
1. Строковые константы. Многие программисты, которые не знакомы с основами реверсинга даже не пытаются шифровать критичные строки. Многие например хранят ключи в виде base64, выдают сообщения типа "Неверный регистрационный код", по ссылкам на который можно достаточно быстро найти код функции, которая выдает это сообщение, даже не запуская дебаггер.
Для некритичных строк достаточно будет динамического создания их прямо на стеке. Самый простой способ:
const char str[] = {'M','y', ' ', 's', 't', 'r', 'i', 'n', 'g', '0'};
MessageBox(str);

А также варианты для более продвинутых пользователей.
2. Проверка CRC участков кода, используемых для проверки регистрации ключей. В большинстве случаев совершенно бесполезно, т.к. начинающие программисты даже не догадываются как и где их будут ломать. Если вы собираетесь реализовать данный функционал собственными силами, то нужно учитывать, что в отладчике крякеру достаточно будет найдет место, с помощью которого вы считаете CRC по региону кода, после этого код будет пропатчен и будет выдавать всегда "правильный" результат. Как можно этому противостоять? Для начала можно познакомиться с возможностями NtReadVirtualMemory, с помощью которого можно обойти все бряки, устанавливаемые крякером в отладчике. Следующим этапом можно порекомендовать использовать NtReadVirtualMemory напрямую через SYSCALL/SYSENTER, но для этого придется познакомиться с простейшими способами
получения номеров сервисов, в том числе как их можно вызывать из WOW64.
3. Детект отладчиков. Если вы прокачали п. 2 до уровня "бог", то можете с легкостью детектить современные юзермодные отладчики, даже при использовании специальных плагинов, которые работают через хук функций из NTDLL и физически не могут перехватывать ваши собственные вызовы SYSCALL/SYSENTER.
Re[23]: Шифрование кода – 2023
От: rudzuk  
Дата: 05.03.23 17:09
Оценка: -1
Здравствуйте, drVanо, Вы писали:

V> У тебя удивительная способность писать откровенную хрень
Автор: rudzuk
Дата: 05.03.23
, а потом удивляться, почему тебя никто не понимает.


Да нет, откровенная хрень, это твои допущения. Допущения о том, что разработчик не в курсе, как работает асимметричка. О наличии одного единственного места, где делаются проверки. О том, что возможности защиты можно продемонстрировать простеньким крэкми. Вот это — хрень.
avalon/3.0.2
Re[25]: Шифрование кода – 2023
От: rudzuk  
Дата: 05.03.23 17:20
Оценка: -1
Здравствуйте, drVanо, Вы писали:

V> R>Да нет, откровенная хрень, это твои допущения. Допущения о том, что разработчик не в курсе, как работает асимметричка. О наличии одного единственного места, где делаются проверки. О том, что возможности защиты можно продемонстрировать простеньким крэкми. Вот это — хрень.


V> Воу, воу! Я смотрю у тебя немного сантиметров наросло в нужном месте. Я так понимаю ты уже готов предоставить свой бинарник для теста чья хрень круче?


Вот, ты либо не читаешь, либо смысл написанного от тебя ускользает.
avalon/3.0.2
Шифрование кода – 2023
От: Khimik  
Дата: 26.02.23 15:36
Оценка:
Я раньше пользовался The Enigma Protector, сейчас по идее надо его обновить, вопрос насколько всё это сейчас актуально? Как сейчас на Западе с хакерами — их прижали, или наоборот они распоясались? Есть ли среди хакеров такие, которые продают наши программы за крипту?
Я наверно не смогу шифровать код Mac версии своей программы, поэтому такая мысль – может быть, довольно важно сделать кейген для Mac другой, с другим алгоритмом, чтобы хакеры не смогли, разобрав код Mac-программы, написать кейген для Windows-версии?
Какой шифратор лучше выбрать — Execryptor или есть что-то новое?
Не решена ли как-то проблема ложных срабатываний антивирусов на шифрованный код?
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Отредактировано 26.02.2023 17:14 Khimik . Предыдущая версия .
Re: Шифрование кода – 2023
От: Unhandled_Exception Россия  
Дата: 27.02.23 09:07
Оценка:
Здравствуйте, Khimik, Вы писали:

K>Какой шифратор лучше выбрать — Execryptor или есть что-то новое?


Эх, это был первый проектор, который я использовал. Он уже очень много лет как не существует.
Re[2]: Шифрование кода – 2023
От: Unhandled_Exception Россия  
Дата: 27.02.23 09:10
Оценка:
Здравствуйте, Черный 😈 Властелин, Вы писали:

ЧВ>В итоге я решил от VMProtect отказаться, оставив однако схему лицензирования и генерации ключей. То есть используются точно такие же ключи, только проверку выполняет мой собственный код, а для пользователей ничего не изменилось.


Интересно, схема генерации ключей точно ничем не защищена, не должна ли лицензироваться и т.д.

ЧВ>Поделюсь модулем для FPC/DELPHI который декодирует ключи VMProtect:


Коллеги, нет у кого-нибудь аналогичного кода для C#?
Re[2]: Шифрование кода – 2023
От: sfsoft Россия  
Дата: 27.02.23 09:32
Оценка:
Здравствуйте, Черный 😈 Властелин, Вы писали:

Т.е. привязку к оборудованию ты не используешь? А как тогда отслеживается количество копий? Ключ можно всем друзьям раздать, например.
Re[3]: Шифрование кода – 2023
От: Carc Россия https://vk.com/gosha_mazov
Дата: 27.02.23 11:18
Оценка:
Здравствуйте, sfsoft, Вы писали:

S>Здравствуйте, Черный 😈 Властелин, Вы писали:


S>Т.е. привязку к оборудованию ты не используешь? А как тогда отслеживается количество копий? Ключ можно всем друзьям раздать, например.

Есть такая штука — Valla — может быть чем-то поможет.
Aml Pages Home
Re[4]: Шифрование кода – 2023
От: sfsoft Россия  
Дата: 27.02.23 11:31
Оценка:
Здравствуйте, Carc, Вы писали:

C>Есть такая штука — Valla — может быть чем-то поможет.


Вендор приказал долго жить ...
Re[3]: Шифрование кода – 2023
От: Carc Россия https://vk.com/gosha_mazov
Дата: 27.02.23 11:40
Оценка:
Здравствуйте, sfsoft, Вы писали:

S>Здравствуйте, Черный 😈 Властелин, Вы писали:


S>Т.е. привязку к оборудованию ты не используешь? А как тогда отслеживается количество копий? Ключ можно всем друзьям раздать, например.

Главное выделено жирным — привязка к оборудованию.
Нужно понимать его шире.

Привязка к оборудованию — это все таки просто некая сравнительно уникальная метка для конкретного пользователя\машины. Что это может быть на самом деле — да всё что угодно. А то упрутся в motherboard\hdd\что_еще серийный номер, и всё. Как будто ничего другого не бывает. И это не только, и главное, не обязательно именно железо.
Фантазируйте, коллеги, фантазируйте.

Всё равно всё сведется к банальному отношению (в математическом смысле). Сложность\надежность этого «замка» (hardware print) и ценность\важность\необходимость того функционала, который этот «замочек» защищает.

Ну, а «далее везде, со всеми остановками» (C)
Кто клиент (пользователь, компания)? Что делает код? «Где» это что-то делает ваш код (отдельный ПК или сервер)? Причем сервер в широком понимании: хошь веб-сервер, а хошь и нет — может это сервер печати бухгалтерского отдела конторы "Рога и Копыта"!?! Нужен ли по делу вашему приложению эти ваши интернеты?
Вот ответы на такие вопросы и дадут понимание, что нужно и что нет.

Примеры на вскидку: если веб для софтины нужен по делу, то тут не грех и онлайн-активацию приделать.
Примеру нумер Два: если ваш супер-пупер-сервер печати для бухгалтерского отдела конторы "Рога и копыта" начнётЪ печатитЪ вместо счетов фактур "Ойопта, нахЫр, наZ взломали!!!!" да еще и в псевдослучайном порядке, да еще и за день до сдачи балансового отчета...

Ну вы поняли... МарьВанна — она же главбух так называемого отдела, в момент подпишет платежку на ваш софт, и купит. Местный же кул-хацкер Пятя из IT-отела будет отметелен ногами в курилке если и не будет уволен, то увидит ближайшую премию только в случае скоропостижной кончины вышеозначенной МарьВанны, и то не факт.
А вот какую-нить онлайн активацию в эдаком (пусть и выдуманным) примере прикручивать может и не стОит. Ибо толковые и мнительные бухгалтерА (а толковые они заувсегда мнительные, и они в этом правы) могут подстремануться: а с какого перепуга это наш кул-супер-принтер в эти наши интернеты лезет? Чего он там забыл?

В общем размышлять следует в контексте и начиная со сценариев использования, а не с технической стороны: чего б такого бы шифрануть да захешить...
Aml Pages Home
Отредактировано 27.02.2023 11:41 Carc . Предыдущая версия .
Re[2]: Шифрование кода – 2023
От: Khimik  
Дата: 27.02.23 12:24
Оценка:
Здравствуйте, Unhandled_Exception, Вы писали:

K>>Какой шифратор лучше выбрать — Execryptor или есть что-то новое?


U_E>Эх, это был первый проектор, который я использовал. Он уже очень много лет как не существует.


Сорри, я опечатался, хотел сказать The Enigma Protector.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[4]: Шифрование кода – 2023
От: Khimik  
Дата: 27.02.23 12:38
Оценка:
Здравствуйте, wantus, Вы писали:

W>1. Добавить проверку подписи собственного exe (через WinVerifyTrust). По случайному таймеру, не сразу. Проверили, запомнили результат, запустили еще один таймер на несколько минут. Когда таймер сработал, покрасили background окна в красный цвет, или перевернули его кверх ногами, или еще чего-нибудь очевидно намеренное и кривое. Это уже достаточно противно патчить.


Я сходу не нагуглил, что делает WinVerifyTrust, просьба пояснить. "запомнили результат" — какой результат, число возвращаемое WinVerifyTrust?
Я привык делать всё по-своему, мне легче изобрести велосипед, чем разбираться в чужих решениях. Можно подсчитать контрольную сумму exe файла собственной программы. Если хакер что-то в нём изменит, контрольная сумма поменяется, значит надо чтобы программа с временем это проверила, как вы написали.
Это не защитит от кейгена; по крайней мере, лично я рассылки ключей не боюсь (боюсь именно кейгена), поскольку моя программа требует регулярно обновлять ключи, чтобы ими можно было активировать (юзер может получить новый ключ на сайте).
Вот ещё пример простой уязвимости: если человек проинсталлирует программу, введёт ключ, а потом скопирует весь каталог с программой и перенесёт на другой компьютер. Значит надо использовать hardwareid, или хотя бы при активации запоминать, в каком каталоге хранится программа.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[3]: Шифрование кода – 2023
От: Khimik  
Дата: 27.02.23 12:40
Оценка:
Здравствуйте, sfsoft, Вы писали:

S>Т.е. привязку к оборудованию ты не используешь? А как тогда отслеживается количество копий? Ключ можно всем друзьям раздать, например.


Полагаю, любая лицензия подразумевает, что человек может хотя бы на минуту дать попользоваться друзьям, и тут в любом случае слишком сложно бороться. У меня ключ кодируется по данным покупателя (имя, фамилия и т.д.), и эти данные отображаются вверху главного окна программы.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re: Шифрование кода – 2023
От: Maniacal Россия  
Дата: 27.02.23 12:55
Оценка:
Здравствуйте, Khimik, Вы писали:

K>Какой шифратор лучше выбрать — Execryptor или есть что-то новое?


Сейчас StarForce жив и развивается, но денег стоит.
Re[6]: Шифрование кода – 2023
От: Khimik  
Дата: 28.02.23 10:01
Оценка:
Здравствуйте, wantus, Вы писали:

K>>Это не защитит от кейгена


W>2023 на дворе. Какие нафиг кейгены?


Можно подробнее, чем 2023 год в этом плане отличается от 2012?

K>>... юзер может получить новый ключ на сайте


W>То есть серверная часть уже есть? Это замечательно.


W>1. Напишите тривиальный HTTP/GET клиент на WinHTTP (пример есть на msdn), который из программы посылает на сайт MachineGUID и получает назад подписанную RSA ключом "лицензию" — [MachineGUID + срок годности + подпись]. Причем это можно даже по http делать, без https. На серверной стороне всё что требуется это вызвать "openssl rsautl -sign ..." из командной строки.

W>2. Зашейте public часть ключа в программу и периодически проверяйте наличие (а) лицензии (б) её подпись (в) соответствие MachineGUID.
W>3. Плюс добавьте отложенные по времени проверки целостности exe.

Это всё надо искать web-программиста, я сам знаю только Delphi. Думал научиться писать cgi скрипты в Lazarus, а он какой-то нелепый, пока даже не могу без посторонней помощи скомпилировать Linux-программу. Но, может быть, скоро ChatGPT сможет нам писать web-скрипты, или учить нас это делать?
Мой нынешний код web-скрипта написал другой человек 15 лет назад, я давно потерял с ним контакты.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[5]: Шифрование кода – 2023
От: wantus  
Дата: 28.02.23 11:53
Оценка:
Здравствуйте, drVanо, Вы писали:

V>Здравствуйте, wantus, Вы писали:


W>>Из простых вариантов


W>>1. Добавить проверку подписи собственного exe (через WinVerifyTrust). По случайному таймеру, не сразу. Проверили, запомнили результат, запустили еще один таймер на несколько минут. Когда таймер сработал, покрасили background окна в красный цвет, или перевернули его кверх ногами, или еще чего-нибудь очевидно намеренное и кривое. Это уже достаточно противно патчить.


V>Пропатчат место вызова WinVerifyTrust.


Могут.

W>>2. Добавить еще несколько копий этой проверки — полных копий всего кода, а не просто вызовов check_exe_signature() — и навешать их на разные события, 56-ой клик мышки или типа того. Опять же, все проверки и реакции на их результаты — отложенные, через таймеры, простые counters или чего там еще.


V>Положат рядом прокси DLL с "правильным" WinVerifyTrust


Это да. Но это лечится аудитом загруженных DLL, который делается без единого api вызова, либо через LoadLibraryEx + LOAD_LIBRARY_SEARCH_SYSTEM32.

W>>3. Могут запатчить сам WinVerifyTrust. На это добавляем вызовов WinVerifyTrust с кривыми параметрами и смотрим, что он таки возвращает ошибку.


V>В прокси DLL сначала вызовут оригинальную WinVerifyTrust, если ничего страшного не произошло — вернут "правильный" результат.


По уму — да, в реальности — нет, им лень.

W>>Как бонус


V>На момент написания прокси DLL поставят ScyllaHide и обойдут все эти проверки на отладчик.


V>Из той же серии про ScyllaHide.


Ну да, я же написал, что обходится. Но не все школьники про это знают.

W>>Типа окопная партизанская война с целью измождения противника.


V>Я вижу тут измождение только со стороны разработчика. Даже написать вызов WinVerifyTrust у него займет гораздо больше времени чем у крякера занопить его в готовом бинарнике.


Каждый видит чего ему хочется, но в целом это зависит от разработчика. Далеко не все болваны.

W>>И как уже сказали — строго online licensing с public keys, никаких прошитых или алгоритмических ключей. Как побочный эффект, это помогает свести credit card fraud и chargebacks практически в ноль.


V>Public Keys для этого нужно так нихренова защитить, а то получится все таже шляпа, что и с WinVerifyTrust.


Проверять надо целостность exe и плясять от этого.

Но это все оффтоп. Поинт в том, что прокси dll не входят в арсенал подавляющего большинства l33t haxxors, от которых то и стоит по сути защищаться. От более квалифицированных товарищей тоже можно, причем в том же стиле, но это уже next level.
Отредактировано 28.02.2023 13:18 wantus . Предыдущая версия .
Re[7]: Шифрование кода – 2023
От: wantus  
Дата: 28.02.23 11:55
Оценка:
Здравствуйте, Khimik, Вы писали:

K>Это всё надо искать web-программиста, я сам знаю только Delphi. Думал научиться писать cgi скрипты в Lazarus, а он какой-то нелепый, пока даже не могу без посторонней помощи скомпилировать Linux-программу.


PHP
Re[6]: Шифрование кода – 2023
От: rudzuk  
Дата: 28.02.23 14:01
Оценка:
Здравствуйте, wantus, Вы писали:

w> K>Это не защитит от кейгена


w> 2023 на дворе. Какие нафиг кейгены?


Только вчера читал на дамаге статью о написании кейгена для совсем не дешевого софта Шел 2023 год.
avalon/3.0.2
Re[4]: Шифрование кода – 2023
От: Sharowarsheg  
Дата: 28.02.23 14:02
Оценка:
Здравствуйте, Черный 😈 Властелин, Вы писали:

ЧВ>У меня несколько десятков ордеров в день, если переписываться с каждым у кого не работает ключ, поменялись железки или там закончились активации, когда тогда писать код?


С другой стороны, если у тебя несколько десятков ордеров в день, зачем писать код?
Re[6]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 28.02.23 14:08
Оценка:
Здравствуйте, wantus, Вы писали:

V>>В прокси DLL сначала вызовут оригинальную WinVerifyTrust, если ничего страшного не произошло — вернут "правильный" результат.

W>По уму — да, в реальности — нет, им лень.

А откуда вы знаете что им лень, а что не лень? Вы предлагаете откровенно слабые варианты, которые может обойти даже начинающий реверсер.

V>>Из той же серии про ScyllaHide.


W>Ну да, я же написал, что обходится. Но не все школьники про это знают.


Школьники уже знают много чего, просто поверьте.

W>Проверять надо целостность exe и плясять от этого.


Вы где предлагаете проверять целостность на диске или в памяти? Если на диске, то в самом простом случае вам подсунут девственно чистый файл, а в более "сложном" сам бинарник будут патчить лоадером прямо в памяти. Если вы предлагаете проверять целостность имейджа в памяти, то разработчику как минимум нужно будет самому разбираться во внутренностях РЕ формата (понимать как файл проецируется на память, исключать из подсчета CRC релоки, IAT, сегменты данных и прочие прелести).

W>Но это все оффтоп. Поинт в том, что прокси dll не входят в арсенал подавляющего большинства l33t haxxors, от которых то и стоит по сути защищаться. От более квалифицированных товарищей тоже можно, причем в том же стиле, но это уже next level.


Прокси DLL я привел в качестве примера, в данном случае большинство из ваших вариантов обходятся банальным лоадером.
Re[7]: Шифрование кода – 2023
От: rudzuk  
Дата: 28.02.23 14:14
Оценка:
Здравствуйте, drVanо, Вы писали:

V> Если вы предлагаете проверять целостность имейджа в памяти, то разработчику как минимум нужно будет самому разбираться во внутренностях РЕ формата (понимать как файл проецируется на память, исключать из подсчета CRC релоки, IAT, сегменты данных и прочие прелести).


Ты все усложняешь. Целостность всего образа проверять смысла нет. Достаточно проверить целостность критически важных данных и кода. Это делается проще, чем ты описываешь.
avalon/3.0.2
Re[7]: Шифрование кода – 2023
От: wantus  
Дата: 28.02.23 15:30
Оценка:
Здравствуйте, drVanо, Вы писали:

V>А откуда вы знаете что им лень, а что не лень? Вы предлагаете откровенно слабые варианты, которые может обойти даже начинающий реверсер.


Я понимаю, что вы стекольщик и вам надо нагнетать, но преувеличивать уж настолько сильно тоже не стоит.

V>Школьники уже знают много чего, просто поверьте.


Школьники как были балбесами в погоне за street cred, так и остались. Ничего принципиально за последнее время не поменялось.

V>Прокси DLL я привел в качестве примера, в данном случае большинство из ваших вариантов обходятся банальным лоадером.


Ну так себе пример то был, хотя это и уже не l33t уровень. "Банальным лоадером" можно обойти, но их делать умеет еще меньший процент.

Для не high-profile софта даже небольшая защита снижает риск кряков практически до нуля, потому что никто из тех, кто может его сломать, не будет тратить на него время.
Re[8]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 28.02.23 18:58
Оценка:
Здравствуйте, rudzuk, Вы писали:

R>Ты все усложняешь. Целостность всего образа проверять смысла нет. Достаточно проверить целостность критически важных данных и кода. Это делается проще, чем ты описываешь.


Дык в области критичных данных и кода как раз могут встретиться релоки, особенно на х86.
Re[6]: Шифрование кода – 2023
От: Sharowarsheg  
Дата: 28.02.23 22:13
Оценка:
Здравствуйте, Черный 😈 Властелин, Вы писали:

ЧВ>>>У меня несколько десятков ордеров в день, если переписываться с каждым у кого не работает ключ, поменялись железки или там закончились активации, когда тогда писать код?

S>>С другой стороны, если у тебя несколько десятков ордеров в день, зачем писать код?

ЧВ>В смысле, зачем? Я так к ним и пришел, постоянно совершенствуя и разрабатывая свой софт.


ЧВ>Конечно можно кого-то нанять, но я не люблю людей, а вот код и компы да — потому по сути занимаюсь любимым делом с достойной оплатой.


Разумно, да.

С третьей стороны, найми кого-нибудь на техподдержку тогда? Научить человека понимать что там с ключами и проча и перевыдывать их — не слишком сложно, и ты будешь иметь дело с одним человеком, а не со всеми сразу?

С четвертой стороны, забей — можно сказать, что я неудачно пошутил, и это будет не слишком далеко от истины.
Re[8]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 01.03.23 07:14
Оценка:
Здравствуйте, wantus, Вы писали:

V>>А откуда вы знаете что им лень, а что не лень? Вы предлагаете откровенно слабые варианты, которые может обойти даже начинающий реверсер.

W>Я понимаю, что вы стекольщик и вам надо нагнетать,

Вы меня с кем-то путаете.

W>но преувеличивать уж настолько сильно тоже не стоит.


Вы реверсингом сколько лет занимаетесь, если не секрет?

W>Школьники как были балбесами в погоне за street cred, так и остались. Ничего принципиально за последнее время не поменялось.


Ну ну, вам с дивана виднее
Re[9]: Шифрование кода – 2023
От: rudzuk  
Дата: 01.03.23 10:31
Оценка:
Здравствуйте, drVanо, Вы писали:

V> R>Ты все усложняешь. Целостность всего образа проверять смысла нет. Достаточно проверить целостность критически важных данных и кода. Это делается проще, чем ты описываешь.


V> Дык в области критичных данных и кода как раз могут встретиться релоки, особенно на х86.


В области кода могут, но нужно написать его так, чтобы их небыло, это не сложно. В области данных... Откуда в области хранения открытого ключа взяться релокам?
avalon/3.0.2
Re[10]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 01.03.23 11:35
Оценка:
Здравствуйте, rudzuk, Вы писали:

R>Здравствуйте, drVanо, Вы писали:


V>> R>Ты все усложняешь. Целостность всего образа проверять смысла нет. Достаточно проверить целостность критически важных данных и кода. Это делается проще, чем ты описываешь.


V>> Дык в области критичных данных и кода как раз могут встретиться релоки, особенно на х86.


R>В области кода могут, но нужно написать его так, чтобы их небыло, это не сложно. В области данных... Откуда в области хранения открытого ключа взяться релокам?


Любые ссылки из кода на данные (в том числе и на ваш открытый ключ), данные на данные, данные на код гарантировано имеют релоки на x86. Учите матчасть.
Re[11]: Шифрование кода – 2023
От: rudzuk  
Дата: 01.03.23 11:42
Оценка:
Здравствуйте, drVanо, Вы писали:

V> V>> Дык в области критичных данных и кода как раз могут встретиться релоки, особенно на х86.


V> R>В области кода могут, но нужно написать его так, чтобы их небыло, это не сложно. В области данных... Откуда в области хранения открытого ключа взяться релокам?


V> Любые ссылки из кода на данные (в том числе и на ваш открытый ключ), данные на данные, данные на код гарантировано имеют релоки на x86. Учите матчасть.


Я же написал, что проверяемый код нужно написать так, чтобы этих ссылок небыло. И это не сложно.
avalon/3.0.2
Re[12]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 01.03.23 11:48
Оценка:
Здравствуйте, rudzuk, Вы писали:

V>> Любые ссылки из кода на данные (в том числе и на ваш открытый ключ), данные на данные, данные на код гарантировано имеют релоки на x86. Учите матчасть.


R>Я же написал, что проверяемый код нужно написать так, чтобы этих ссылок небыло. И это не сложно.


Ну хорошо, давайте вы напишете такой код + все что сюда понаписали ТС-у, а я его попробую взломать. Как вам такое?
Re[14]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 01.03.23 12:55
Оценка:
Здравствуйте, rudzuk, Вы писали:

R>Мне сантиметрами меряться не очень интересно. Давай я признаю, что у тебя длиннее, а ты признаешь, что написать код без релоков это не бином Ньютона?


Давай ты лучше признаешь, что все что ты предлагал ТС-у вместе с вантусом — это полный шлак.
Re[2]: Шифрование кода – 2023
От: sergey2b ЮАР  
Дата: 01.03.23 13:05
Оценка:
Почему его автор не стал поддерживать
И продал странной личности которая тоже забила болт
Re[16]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 01.03.23 13:38
Оценка:
Здравствуйте, rudzuk, Вы писали:

R>Ты невнимательно читаешь. Я ТС'у ничего не предлагал. Я сказал, что контролировать целостность критически важного кода и данных не так сложно, как ты пытаешься преподнести. Поэтому — нет.


Вот же ты сам предлагал:

Ты все усложняешь. Целостность всего образа проверять смысла нет. Достаточно проверить целостность критически важных данных и кода. Это делается проще, чем ты описываешь.


В итоге ты предлагаешь человеку самостоятельно писать PIC, чтобы потом проверять целостность участков кода. Я даже больше чем уверен, что он даже их (эти самые участки) не найдет.
К слову сказать твой открытый ключ можно подменить прямо в памяти в момент "десериализации" его в BigInteger даже не трогая код, который с ним работает, и все твои проверки на целостность будут просто бесполезны.
Re[4]: Шифрование кода – 2023
От: Aquilaware  
Дата: 01.03.23 14:32
Оценка:
Здравствуйте, Sharowarsheg, Вы писали:

S>Вечные лицензии, мне помнится. Деньги получаешь один раз, а поддержка навсегда. Через какое-то время проект приходится закрывать.


Представляю себе этот ужас. Наверное есть сегменты продуктов, где практика вечных лицензий может быть оправдана, но если это что-то техническое и тем более связанное с компиляцией и кодом — шансов выжить с такой моделью нет.
Re[5]: Шифрование кода – 2023
От: JustPassingBy  
Дата: 01.03.23 16:21
Оценка:
Здравствуйте, Aquilaware, Вы писали:

A>Представляю себе этот ужас. Наверное есть сегменты продуктов, где практика вечных лицензий может быть оправдана, но если это что-то техническое и тем более связанное с компиляцией и кодом — шансов выжить с такой моделью нет.


Вечные лицензии обычно практикуются в самом начале пути, чтобы привлечь первых пользователей. Использовать такое для устоявшегося продукта смысла никакого, конечно же. Нельзя предоставлять неограниченный ресурс за ограниченные деньги.
Re[4]: Шифрование кода – 2023
От: scf  
Дата: 01.03.23 16:56
Оценка:
Здравствуйте, wantus, Вы писали:

W>1. Добавить проверку подписи собственного exe (через WinVerifyTrust).


я бы еще добавил: помимо основного метода проверки ключа (RSA) иметь несколько простых дополнительных (а-ля третий байт ключа всегда делится на 7). После загрузки ключа в память он тиражируется в десяток-другой мест в памяти и точно так же, по таймеру или событию, проверяется. Тиражирование нужно, чтобы было сложнее ставить брейкпоинты на память. Разные проверки нужны, чтобы их нельзя было обезвредить анализом кода или поведения.
Re[9]: Шифрование кода – 2023
От: wantus  
Дата: 01.03.23 17:23
Оценка:
Здравствуйте, drVanо, Вы писали:

V>Здравствуйте, wantus, Вы писали:


V>>>А откуда вы знаете что им лень, а что не лень? Вы предлагаете откровенно слабые варианты, которые может обойти даже начинающий реверсер.

W>>Я понимаю, что вы стекольщик и вам надо нагнетать,

V>Вы меня с кем-то путаете.


Вы продаете продукт для защиты от хакеров. Чем страшнее хакеры, тем лучше бизнес.

W>>но преувеличивать уж настолько сильно тоже не стоит.


V>Вы реверсингом сколько лет занимаетесь, если не секрет?


С конца 90х, но это видимо вопрос с подвохом?

V>Ну ну, вам с дивана виднее


Сlassy, factual retort. Считайте победа ваша.
Re[15]: Шифрование кода – 2023
От: wantus  
Дата: 01.03.23 17:46
Оценка:
Здравствуйте, drVanо, Вы писали:

V>Давай ты лучше признаешь, что все что ты предлагал ТС-у вместе с вантусом — это полный шлак.


Да, елки-палки.

Разговор не о том, что несложные вещи не ломаются.

Разговор о том, что их зачастую достаточно для того, чтобы по сети не плавали взломанные версии.

Да, можно всё зашифровать и с вашим коробочным решением это может сделать любой балбес, но на практике можно обойтись куда меньшей кровью.
Re[6]: Шифрование кода – 2023
От: sergey2b ЮАР  
Дата: 03.03.23 14:23
Оценка:
Здравствуйте, JustPassingBy, Вы писали:

JPB>Вечные лицензии обычно практикуются в самом начале пути, чтобы привлечь первых пользователей. Использовать такое для устоявшегося продукта смысла никакого, конечно же. Нельзя предоставлять неограниченный ресурс за ограниченные деньги.


а как же winrar or windows commander
Re[7]: Шифрование кода – 2023
От: JustPassingBy  
Дата: 04.03.23 07:45
Оценка:
Здравствуйте, sergey2b, Вы писали:

S>а как же winrar or windows commander


А что у них сейчас экономически?
Re[17]: Шифрование кода – 2023
От: K13 http://akvis.com
Дата: 04.03.23 11:44
Оценка:
Здравствуйте, drVanо, Вы писали:

V>К слову сказать твой открытый ключ можно подменить прямо в памяти в момент "десериализации" его в BigInteger даже не трогая код, который с ним работает, и все твои проверки на целостность будут просто бесполезны.


Просто публичный ключ должен вычисляться, а не лежать константой в области данных.
Re[18]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 05.03.23 12:09
Оценка:
Здравствуйте, K13, Вы писали:

K13>Просто публичный ключ должен вычисляться, а не лежать константой в области данных.


Для того чтобы понимать как защитить открытый ключ от подмены, нужно как минимум представлять как работают стандартные библиотеки, которые манипулируют большими числами. Открытый ключ это просто большое число, которое загоняется в класс BigInteger, и дальше над ним производят математические действия (внутри modpow). Дак вот перехватив любой из методов, которые используются при проверке правильности серийного номера, можно подменить открытый ключ прямо в памяти BigInteger-а (особо "умные" делают это через перехват стандартной функции выделения памяти из CRT).
Re[20]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 05.03.23 15:18
Оценка:
Здравствуйте, rudzuk, Вы писали:

R>Вот я и говорю — фантазии это, с прикольными допущениями, что будет использоваться стандартное API.


Я вам уже предлагал перейти от теории к практике, но вы зассали
Автор: rudzuk
Дата: 01.03.23
. Зачем заново начинать этот разговор?
Re[22]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 05.03.23 16:14
Оценка:
Здравствуйте, rudzuk, Вы писали:

R>Да нет же, я сказал, что мне сантиметрами меряться не интересно, и что готов признать твое превосходство. У тебя удивительная способность не понимать, что тебе говорят


У тебя удивительная способность писать откровенную хрень
Автор: rudzuk
Дата: 05.03.23
, а потом удивляться, почему тебя никто не понимает.
Отредактировано 05.03.2023 16:15 drVanо . Предыдущая версия . Еще …
Отредактировано 05.03.2023 16:15 drVanо . Предыдущая версия .
Re[10]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 05.03.23 16:16
Оценка:
Здравствуйте, wantus, Вы писали:

W>Вы продаете продукт для защиты от хакеров. Чем страшнее хакеры, тем лучше бизнес.


И делюсь в этой ветке своим реальным опытом. Что в этом плохого?
Re[24]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 05.03.23 17:14
Оценка:
Здравствуйте, rudzuk, Вы писали:

R>Да нет, откровенная хрень, это твои допущения. Допущения о том, что разработчик не в курсе, как работает асимметричка. О наличии одного единственного места, где делаются проверки. О том, что возможности защиты можно продемонстрировать простеньким крэкми. Вот это — хрень.


Воу, воу! Я смотрю у тебя немного сантиметров наросло в нужном месте. Я так понимаю ты уже готов предоставить свой бинарник для теста чья хрень круче?
Re[26]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 05.03.23 17:38
Оценка:
Здравствуйте, rudzuk, Вы писали:

R>Вот, ты либо не читаешь, либо смысл написанного от тебя ускользает.


Чтобы смысл ускользал или не ускользал он (смысл) как минимум должен быть. А его нет, есть только твоя теоретическая хрень и ты почему-то боишься проверить свою хрень теорию на практике.

P.S. Предлагаю прекратить этот бессмысленный разговор, тем более что кроме отсутствия недостающих сантиметров тебе больше нечего предложить в качестве аргументов.
Re[27]: Шифрование кода – 2023
От: rudzuk  
Дата: 05.03.23 18:06
Оценка:
Здравствуйте, drVanо, Вы писали:

V> P.S. Предлагаю прекратить этот бессмысленный разговор, тем более что кроме отсутствия недостающих сантиметров тебе больше нечего предложить в качестве аргументов.


Когда смысл ускользнул, продолжать, действительно, нет смысла.
avalon/3.0.2
Re[18]: Шифрование кода – 2023
От: TailWind  
Дата: 06.03.23 10:16
Оценка:
R>Знатно у Ваньки полыхнуло

Он, конечно, не совсем культурно себя ведёт
Но он прав

Делал такую защиту:
— проверка CRC exe файла (не в памяти, а на диске)
— отложенная по таймеру

Ну и опытные хакеры надо мной поржали, взломав за день
Отредактировано 06.03.2023 10:17 TailWind . Предыдущая версия .
Re[20]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 06.03.23 13:12
Оценка:
Здравствуйте, rudzuk, Вы писали:

R>Сейчас даже просто использование нативной компиляции уже является достаточным препятствием для "юных дарований".


Вот оно че, Михалыч! Нативная компиляция — это все что нужно знать об уровне знаний нашего иксперта! Я ему про релоки рассказываю, а он мне про C#. Теперь все встало на свои места. Вопросов больше не имею.
Отредактировано 06.03.2023 13:13 drVanо . Предыдущая версия .
Re[21]: Шифрование кода – 2023
От: rudzuk  
Дата: 06.03.23 13:30
Оценка:
Здравствуйте, drVanо, Вы писали:

V> Вот оно че, Михалыч! Нативная компиляция — это все что нужно знать об уровне знаний нашего иксперта! Я ему про релоки рассказываю, а он мне про C#. Теперь все встало на свои места. Вопросов больше не имею.


Мде. Качай понималку написаннго, оно в жизни помогает.
avalon/3.0.2
Re[22]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 06.03.23 15:46
Оценка:
Здравствуйте, rudzuk, Вы писали:

V>> Вот оно че, Михалыч! Нативная компиляция — это все что нужно знать об уровне знаний нашего иксперта! Я ему про релоки рассказываю, а он мне про C#. Теперь все встало на свои места. Вопросов больше не имею.


R>Мде. Качай понималку написаннго, оно в жизни помогает.


Я тебе просто дам совет на будущее — перед теми как залезать в спор со своим икспертным мнением, соизволь хоть немного изучить матчасть. В приличном обществе аргументы типа "качай понималку" не канают.
Re[23]: Шифрование кода – 2023
От: rudzuk  
Дата: 06.03.23 18:12
Оценка:
Здравствуйте, drVanо, Вы писали:

V> Я тебе просто дам совет на будущее — перед теми как залезать в спор со своим икспертным мнением, соизволь хоть немного изучить матчасть. В приличном обществе аргументы типа "качай понималку" не канают.


Так это и не было аргументом. Это было пожелание человеку, который, очевидно, не понимает смысла написанного.

з.ы. Кстати, ты еще не все мои сообщения заминусовал. Не сдерживай себя!
avalon/3.0.2
Re[24]: Шифрование кода – 2023
От: drVanо Россия https://vmpsoft.com
Дата: 06.03.23 18:33
Оценка:
Здравствуйте, rudzuk, Вы писали:

R>з.ы. Кстати, ты еще не все мои сообщения заминусовал. Не сдерживай себя!


Наслаждайся, клоун
Re[2]: Шифрование кода – 2023
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 06.03.23 20:08
Оценка:
Здравствуйте, Черный 😈 Властелин, Вы писали:

ЧВ>пришел к выводу, что те кто не купят ваш софт в любом случае воспользутся или левой кредиткой или патчем. Да ну и пусть, c них все равно денег не получить. А те кто могут купить прогу, покупают ее как и раньше.


У меня сложилось еще более радикальное мнение: до некоторого порога цены (для рядового пользователя это где-то $30-50) даже наличие ключей практически не влияет на продажи, а уж наличие шифрования — и подавно.

Сужу по своему основному продукту, в котором нет вообще никакой защиты (купившим лицензию дается ссылка на скачивание полной версии). Найти в сети пиратку — раз плюнуть, они валяются повсеместно, но количество продаж всегда четко коррелирует с количеством показов в гугле. То есть, народ или опасается пираток (и обоснованно, как известно), либо ему банально в лом заниматься криминалом ради экономии суммы, примерно равной продуктовой корзине на один-два дня.

Отсюда вывод: основная ценность ключей — не в борьбе с пиратством, а в возможности эффективно блокировать лицензии после возвратов, а также блокировать кардеров.

Уже много лет собираюсь приделать к этому продукту какие-нибудь примитивные ключи (о шифровании кода даже не задумываюсь — нет смысла), но все руки не доходят.
Re: Шифрование кода – 2023
От: Aquilaware  
Дата: 07.03.23 01:46
Оценка:
Здравствуйте, Khimik, Вы писали:

K>Какой шифратор лучше выбрать — Execryptor или есть что-то новое?


Для нативного кода наверное еще можно как-то обходится без протектора, но если это что-то типа Java или .NET и вы собираетесь продавать коммерческий продукт — то разорвут на части, какую бы вы нишу не выбрали. С одной стороны грызут кул-хацкеры, с другой стороны — конкуренты. Как только у вас рождается что-то более-менее востребованное и популярное — вся эта братия быстро налетает как мухи на мёд.

Кто-то скажет — да ну его, пиратство — это бесплатная реклама. Отчасти это так, но только до тех пор пока не начинается срез дохода. Вот тогда-то и приходится суетиться, но лучше решение этой ситуации закладывать сразу пока вас не сдоили за бесплатно.
Re: Шифрование кода – 2023
От: icezone  
Дата: 12.03.23 14:52
Оценка:
Здравствуйте, Khimik, Вы писали:

K>Какой шифратор лучше выбрать — Execryptor или есть что-то новое?


можно просто шифровать данные, которые необходимы для полной версии
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.