Z0mbie
Top Device Online [10]
Октябрь 2000
Итак, мы написали вирус. Будет написан антивир, и через некоторое время все инфицированные компьютеры будут вылечены. Эта статья о том, как увеличить продолжительность этого периода.
Вставим в вирус "изменяющий код", то есть код, который сработает, скажем, через два месяца, и изменит некоторые команды около вирусной точки входа (и/или любые другие характеристики вируса). В результате чего изменится контрольная сумма, и вирус перестанет определяться.
Если изменяющий код будет присутствовать в вирусе изначально, антивирус будет содержать такие контрольные суммы, на которые наше изменение не подействует.
Существует только один способ запретить анализ "изменяющего кода" - скрыть его от всех. И есть следующие реализации этого способа:
Как можно видеть, последний вариант позволяет написать полностью автоматический вирус со скрытыми свойствами.
Извратим буфер из случайных чисел 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 несложно догадаться. Вот наша схема:
| Шифровка (шифруем "изменяющий код", у себя дома) | Расшифровка (расшировка "изменяющего кода", на инфицированных машинах) |
|---|---|
|
|
Далее расмотрим процедуру 'modexp'.
Как можно видеть, 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 где:
Давайте подсчитаем, сколько раз нам нужно зашифровать сообщение чтобы его расшифровка заняла 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 min | 1.3 sec | 167 | 950 |
| 1 hour | 7.8 sec | 1000 | 5700 |
| 1 day | 3 min | 24000 | 136800 |
| 1 week | 21 min | 168000 | 957600 |
| 1 month | 1.5 hour | 672000 | 3830400 |
| 1 year | 18 hours | 8064000 | 45964800 |
| 16 months | 1 day | ||
| 10 years | 1 week | ||
| 40 years | 1 month | ||
Алгоритм расшифровки '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, и тогда их расшифровка будет произведена в те же десятки раз быстрее, что нежелательно.
N раз:
{
x = (x ^ d) % m;
x = x xor <что-нибудь>;
}
Программа DPGN.EXE ксорит всего пару двордов, но этого хватит.