
                               "DELAYED CODE"
                                 technology

                                 версия 1.1

[*] Введение

    Итак, мы написали вирус. Будет написан антивир, и через некоторое время
    все инфицированные компьютеры будут вылечены.
    Эта статья о том, как увеличить продолжительность этого периода.

[*] Идея

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

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

    Существует только один способ запретить анализ "изменяющего кода" -
    скрыть его от всех. И есть следующие реализации этого способа:

    1. Ждать получения изменяющего кода из Интернета, либо зашифровать код
       и ждать получения ключа расшифровки.
    2. Зашифровать код так, чтобы его расшифровка заняла как раз требуемое
       время, например обозначенные выше два месяца. ("delayed code")

    Как можно видеть, последний вариант позволяет написать полностью
    автоматический вирус со скрытыми свойствами.

[*] Теория (кривая хрень, использовать которую мы на самом деле не будем)

    Извратим буфер из случайных чисел A[] некоторым хэшируем алгоритмом
    столько раз N, чтобы это заняло некоторый период T.
    В результате чего получим буфер B[], который и будет использоваться
    для шифровки/расшифровки "отложенного" кода.

    Отметим, что эти N итераций никак нельзя распараллелить на несколько
    компьютеров, так что минимальное время шифровки/расшифровки будет
    ограничиваться только максимальной скоростью процессора.

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

    Итак, все свое свободное время вирус будет посвящать
    зашифровке буфера A[], пока он не превратится в B[].
    После того, как на шифровку будет потрачено N итераций (время T)
    полученным буфером B[] будет расшифрован "отложенный" код.
    Ну и запущен, само собой.

[*] Теория (теперь правильная)

    Основная проблема предыдущих рассуждений в том, что преобразование
    A[] в B[] и у нас и у вируса занимает одинаковое время.
    А мы не хотим тратить пару месяцев своего времени на всякую хуйню.

    Так что будем использовать RSA.
    Тот самый хитроизъебский алгоритм, который используется в PGP.
    Основное его достоинство в том, что шифровка и расшифровка производятся
    с использованием разных ключей.

    Итак, сгенерим рандомный буфер A[] и зашифруем им наш код.
    После чего N раз зашифруем A[] и получим B[].
    Теперь, в отличие от описанной ранее шифровки хэширующим алгоритмом,
    вирус будет повторять отнюдь не ту же самую операцию.
    Вирус будет расшифровывать буфер B[] обратно в A[].

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

[*] Зависимость времени шифрования и расшифрования

    Здесь задача такая: вычислить, сколько времени T (или итераций N)
    нужно потратить на зашифровку, чтобы на расшифровку пришлось, скажем
    пара месяцев.

    Как вы знаете, шифрование алгоритмом RSA выглядит так:
    шифровка:    encr = (text ^ e) % m
    расшифровка: text = (encr ^ d) % m
    где {e,m} и {d,m} - открытый и закрытый ключи.
    ('^' означает возведение в степень, '%' означает модуль, или же
     остаток от деления)

    Как правило, число e маленькое, например 3, 17, 50003 и так далее.
    А вот число d весьма нехилое, состоящее из например 1023х бит.

    В нашей схеме шифрования не будет таких понятий как открытый/закрытый
    ключ, числа d и m - открытые, а чему равно e несложно догадаться.
    Вот наша схема:

              Шифровка:                           Расшифровка:
              ~~~~~~~~~                           ~~~~~~~~~~~
     (шифруем "изменяющий код",           (расшировка "изменяющего кода",
      у себя дома)                         на инфицированных машинах)

    * A[] <-- любые данные               * x = B[]
    * шифруем код буфером A[]              N раз: x = (x ^ d) % m
    * x = A[]                              A[] = x
      N раз: x = (x ^ e) % m             * расшифровываем код буфером A[]
      B[] = x
    * сохраняем буфер B[] в вирусе

    Далее расмотрим процедуру 'modexp'.

    // modexp:   возводит в степень и возвращает модуль
    // действие: x = (a ^ e) % m

    void modexp(bignumber& x,   // выход
                bignumber a,    // вход
                bignumber e,    // степень
                bignumber m,    // модуль
    {
        x = 1;
        bignumber t = a;
        for (int i = 0; i <= MaxBit(e); i++)
        {
            if (e.GetBit[i] == 1)
              x = (x * t) % m;        // modmult()
            t = (t * t) % m;          // modmult()
        }
    }

    Как можно видеть, modexp() вызывает процедуру modmult()
    (e.#bit + e.#bit1 - 1) раз.
    (-1 взялась оттуда, что самое последнее умножение не происходит;
     хоть это и не показано в приведенной подпрограмме)
    Задачей подпрограммы modmult() является умножить два числа и вернуть
    результат по модулю.
    Короче говоря, число вызовов процедуры modmult() пропорционально
    времени шифровки и расшифровки. Отсюда:

  Decr.time = Encr.time * (D.#bit+D.#bit1 - 1) / (E.#bit1+E.#bit1 - 1) * K

    где:

    #bit  == общее количество бит в D или E
    #bit1 == количество единичных бит
             (принимая число за рандомное, можно сказать что их половина.
              хотя в дальнейшем будем их подсчитывать для конкретных ключей)
    #bit + #bit1 - 1 == общее число вызовов modmult()

    K = 0.9+-10% -- Коэффициент, появился из-за того, что каждый modmult()
                    выполняется переменное время, плюс там всякая
                    оптимизация. По видимому, при  N-->oo  K-->1

[*] Пример

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

    Во-первых, сделаем RSA ключ. Пусть это будет 1024-битный ключ, e = 3.
    Выполняем: 'KEYGEN.EXE KEY\DPGN 1024 3 3'
    Параметры полученного ключа: 1024-bit N, E==3, D=1023-bit/519*'1'

    Пропорциональность времени шифровки/расшифровки (в теории):
    ratio = (1023+519-1)/(2+2-1)*0.9 = 462+-10%

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

    Выполняем: 'DGPGN.EXE e 100'
    результат: 100 итераций, время шифровки = 815 мс
    Выполняем: 'DGPGN.EXE d 100'
    результат: 100 итераций, время расшифровки = 360228 мс

    ratio = 360228/815 = 441

    Теперь посчитаем N для 10-минутной расшифровки.

    Если 100 итераций расшифровки занимают 360228 мс,
    а N итераций должны занять 10*60*1000 мс (10 минут), то
    N = 60*10*1000 * 100 / 360228 = 167

    Если 167 итераций расшифровки должны занять 60*10*1000 мс, то
    время шифровки = 60*10*1000 / 441 = 1360 мс.

    Таким образом чуть больше секунды шифровки
    потребуют расшифровки в течение 10-ти минут.

    Проверим.

    Выполняем: 'DPGN.EXE e 167'
    время шифровки: 1268 мс
    Выполняем: 'DPGN.EXE d 167'
    время расшифровки: 600477 мс = 10 минут 0.5 секунд

    Некоторые другие результаты:
    /* пропорциональность времени верна только для этого ключа */

    Расшифровка:    Шифровка:          N (1024х-битный ключ)
                                     K5-100    Celeron-500
    10 минут        1.3 секунды          167         950
    1 час           7.8 секунды         1000        5700
    1 день            3 минуты         24000      136800
    1 неделя         21 минута        168000      957600
    1 месяц         1.5 часа          672000     3830400
    1 год            18 часов        8064000    45964800
    16 месяцев        1 день
    10 лет            1 неделя
    40 лет            1 месяц

[*] Как увеличить скорость вычислений

    Алгоритм расшифровки 'text = (encr ^ d) % m' может быть
    представлен так:

      a = ((encr % p) ^ dp) % p            // dp = d % (p-1)
      b = ((encr % q) ^ dq) % q            // dq = d % (q-1)
      if (b < a) b += q
      text = a + p * (((b - a) * u) % q)       // u: (u * p) % q = 1

    Таким образом время расшифровки будет увеличено в несколько раз.
    Но мы публикуем только d и m, и я не знаю, можно ли с их помощью
    найти p и q.

[*] Как замедлить скорость вычислений

   Поскольку d не является уникальным ключом, а некоторые
   его варианты могут быть вычислены по формуле:

   d' = d + (p-1)*(q-1) * t, где t=0,1,2,...,

   то можно увеличить d до необходимой длины,
   тем самым замедлив скорость вычислений в десятки раз.

   Однако тут все упирается в следующее: смогут и станут ли те, кто
   хочет произвести DPGN-расшифровку быстрее всех остальных,
   найти p и q, зная d'.
   Если смогут, то они вычислят оригинальное d, и тогда
   их расшифровка будет произведена в те же десятки раз быстрее,
   что нежелательно.

[*] Остальные фичи

    1. На практике, совсем не нужно хранить число итераций N,
       достаточно будет простой CRC.
       Таким образом никто не будет знать,
       что ЭТО такое и когда ОНО будет запущено.
       Также, следует добавлять к N некоторый случайный элемент,
       не делая его кратным например 1000 и не делая время расшифровки
       кратным например одному дню.

    2. Я не уверен, но - может быть - операция {N раз: x = (x ^ d) % m}
       может быть заменена на что нибудь более быстрое (не p и q),
       используя какую-нибудь извращенную математику или что-то,
       чего я не заметил.
       Чтобы такого не произошло, можно ввести дополнительную шифровку
       между вызовами modexp()а, например проксорить (ну а что ж еще):
         N раз:
         {
           x = (x ^ d) % m;
           x = x xor <что-нибудь>;
         }
       Программа DPGN.EXE ксорит всего пару двордов, но этого хватит.

[*] Использование "отложенного кода" (что криптовать?)

    * Как было сказано раньше, код, модифицирующий точку входа и,
      соответственно, контрольную сумму.
      Представьте: вирус разошелся, антивир вышел, 99% машин полечили.
      И тут оставшийся 1% изменяется (перестает определяться) и вирус
      начинает всх иметь с новыми силами, но уже стартуя не с одной-двух
      тачек, как было сначала, а с тысяч их.

    * Все мы думаем о выкачивании плугинов из Интернета.
      Технология "delayed code" позволяет вирусу содержать любые url-ки,
      да так, что никто ваши сайты не закроет, до времени.

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

    * Расшифрованный код может содержать внутри себя еще один такой же
      "сюрприз". И так далее.

    * Вообще интересна позиция аверов: разошелся какой-нибудь loveletter,
      который через месяц может запросто превратиться в циха.
      И что писать про такой вирус, "очень опасный" или "неопасный"?
      Предлагаю вариант с намеком: "а хуй его знает". ;-)

                                                            (x) 2000 Z0MBiE
                                   * * *
