- 12
- 46
Сразу скажу, что в статье разбирается база, чтобы ввести читателя в полиморфизм, в следующих статьях(если они будут, зависит от актива, планирую рассказать глубже про полиморфизм)
--
Как мы знаем антивирусные защиты системы безопасности используют статический анализ файла или процесса для выявление потенциально вредоносных программ, они смотрят на: энтропию, подозрительные секции которые могут создавать упаковщики, и так далее. Но, также у антивируса есть эвристический анализ, это когда антивирус смотрит на поведение программы, куда, как и зачем она туда обращается.
AV вклинивается между программой и операционной системой. Когда подозрительный процесс пытается, например, вызвать функцию CreateRemoteThread (часто используется для инжекта в другой процесс), эвристический движок ставит выполнение на паузу и оценивает непосредственно контекст - "зачем текстовому редактору лезть в память svchost.exe?". Если логического объяснения нет, срабатывает блокировка по подозрению.
Другой важный момент - это борьба с обфускацией и полиморфизмом, авторы вредоносных программ постоянно меняют свой код, даже на лету(SMC - самомодифицирующийся код) чтобы его нельзя было обнаружить по статической маске. Эвристический же анализ обходит это за счет частичной эмуляции и дампинга голой архитектуры вируса, даже если код будет замусорен junk-кодом, эвристика вычленит опасные алгоритм скрытый за слоями шифрования, и распознает его как вредоносный паттерн если нет анти-эмуляции.
Но стоит понимать, как я уже написал выше, что это может сломать статический анализ в поисках определенных байтовых масок, но при этом может повысить энтропию, т.к АБСОЛЮТНО любая попытка шифрования или упаковки - повышение энтропии..
Полиморфизм на практике
Немного поговорим про полиморфизм, что это вообще такое и с чем его едят? Полиморфизм - это свойство программы использовать разные алгоритмы, но при этом результат остается один и тот же. Техника полиморфизма - это передовой путь любого разработчика вируса. Поэтому подобные вирусы получаются сложными для разбора и анализа, но настоящие ассемблерийщики не ищут легких путей.
К примеру, программа может выполнить сложение двух операндов 2 разными алгоритмами но с одним и тем же результатом:
Прямолинейно:
Абсолютно тот же результат, но другой алгоритм:
Существует тысячи готовых полиморфных движков, но они нам и не нужны, просто как фактор, мы все пишем с нуля, чтобы их переплюнуть конечно придется постараться, в любом случае эта статья просто введение, если людям с бластхака зайдет - сделаю 2 часть. В любом случае, профессиональный разработчик должен бросить вызов своей лени и удивить мир еще одним шедевром, но не суть.
Подглава: разные вариации обнуления регистра EAX.
Для тестирования я реализовал максимально простую программу, которая идеально подходит для этой статьи.
В первую очередь я реализовал функцию которая отвечает за рандомизацию, она выбирает число от 1 до 6, в дальнейшем она будет использоваться как функция, которая выбирает алгоритм через который будет обнуляться регистр EAX:
Также у меня есть функция, которая в зависимости от того, что выберет функция randomYzer(1-2-3-4-5-6), она перейдет на соответствующий случай этого алгоритма и исполнит его:
В первом случае используется XOR (байты 31 C0), во втором - вычитание SUB(29 C0), в третьем - прямая запись нуля через банальный MOV(B8 00000000). Далее же идет более экзотичные варианты: логическое И(and) пара команд push 0 и pop eax, то есть помещаем в стек значение, после чего извлекаем его в регистр. Также есть алгоритм умножения регистра на ноль через IMUL. Результат, во всех шести случаях как я думаю, понятен, - он идентичен.
Для статического анализа такая техника - настоящий кошмар. Если антивирус ищет вирус по конкретной сигнатуре, к примеру: "31 C0", он пропустит этот же код, если мутатор выберет вариант с PUSH/POP или IMUL. Однако для эвристического анализа, о котором мы говорили раньше, сам факт наличия такой функции является подозрительным. Эвристика видит, что программа генерирует код на лету и записывает его в секции памяти, предназначенные для выполнения. С одной стороны - это значительно может повысить энтропию, и это надо учитывать, полиморфный код спасает именно от статического анализа.
Также прошу обратить ваше внимание на add rdi - количество байтов переданных в буфер.
Если мы не будем так скажем, СДВИГАТЬ указатель на их размер, одни байты могут затереть другие, - это важно учитывать.
После чего создаем буфер внутри секции .data:
Передаем его в RDI чтобы загрузить в регистр RDI не сами данные, которые мы поместили в буфер, а именно указатель на его начало в памяти:
Компилируем нашу программу и смотрим, что все прекрасно работает:
Давайте же теперь зайдем в отладчик и посмотрим что хранится в нашем буфере!
Подглава: анализируем наш буфер
Запустив отладчик, предварительно в коде вызвав буфер мы можем перейти на адрес где вызывается буфер, в нашем случае это перед вызовом функции ExitProcess:
Запускаем нашу программу в отладчике, жмем F9, переходим метку где вызывается ExitProcess, через строчку там будет вызов нашего буфера:
Жмем F9, доходим до адреса 0000000000401027 и смотрим в дамп, феноменально - видим наши байты!:
Теперь вновь переходим в нашу программу и смотрим, что же это за байты, а оказывается, что 0xC029 это байты, отвечающие за обнуление регистра через sub eax, eax:
Рандомизация работает, прекрасно, то есть у нас есть алгоритм, который выбирает рандомизатор, они разные - но исход один и тот же.
Заключение
В завершение я бы хотел подчеркнуть, что полиморфизм - это мощный инструмент в руках разработчика. Как я вам показал, и как вы увидели, даже простая операция обнуления регистра может быть реализована десятками способов, что делает статический анализ, основанный на поиске фиксированных байтовых масок, практически бесполезным против полиморфного кода. Однако это не означает, что полиморфный код неуязвим. Современные системы защиты всё чаще полагаются на эвристический анализ, который отслеживает не столько код, сколько поведение программы: динамическую генерацию инструкций, манипуляции с памятью, подозрительные вызовы API и их последовательности.
Каждый новый метод обфускации или полиморфизма рано или поздно встречает противодействие в виде усовершенствованных эвристик, эмуляции и анализа контекста.
Кстати, в следующей статье я планирую углубиться в анти эмуляцию(конечно же, разбирая полиморфизм) и показать несколько эффективных методов против нее, в отличие от того что я писал ТУТ, есть много других способов которые гораздо безопаснее, и будут работать стабильно даже на старых машинах.
Всем удачи!
Также оставляю исходный код.
--
Как мы знаем антивирусные защиты системы безопасности используют статический анализ файла или процесса для выявление потенциально вредоносных программ, они смотрят на: энтропию, подозрительные секции которые могут создавать упаковщики, и так далее. Но, также у антивируса есть эвристический анализ, это когда антивирус смотрит на поведение программы, куда, как и зачем она туда обращается.
AV вклинивается между программой и операционной системой. Когда подозрительный процесс пытается, например, вызвать функцию CreateRemoteThread (часто используется для инжекта в другой процесс), эвристический движок ставит выполнение на паузу и оценивает непосредственно контекст - "зачем текстовому редактору лезть в память svchost.exe?". Если логического объяснения нет, срабатывает блокировка по подозрению.
Другой важный момент - это борьба с обфускацией и полиморфизмом, авторы вредоносных программ постоянно меняют свой код, даже на лету(SMC - самомодифицирующийся код) чтобы его нельзя было обнаружить по статической маске. Эвристический же анализ обходит это за счет частичной эмуляции и дампинга голой архитектуры вируса, даже если код будет замусорен junk-кодом, эвристика вычленит опасные алгоритм скрытый за слоями шифрования, и распознает его как вредоносный паттерн если нет анти-эмуляции.
Но стоит понимать, как я уже написал выше, что это может сломать статический анализ в поисках определенных байтовых масок, но при этом может повысить энтропию, т.к АБСОЛЮТНО любая попытка шифрования или упаковки - повышение энтропии..
Полиморфизм на практике
Немного поговорим про полиморфизм, что это вообще такое и с чем его едят? Полиморфизм - это свойство программы использовать разные алгоритмы, но при этом результат остается один и тот же. Техника полиморфизма - это передовой путь любого разработчика вируса. Поэтому подобные вирусы получаются сложными для разбора и анализа, но настоящие ассемблерийщики не ищут легких путей.
К примеру, программа может выполнить сложение двух операндов 2 разными алгоритмами но с одним и тем же результатом:
Прямолинейно:
asm:
mov eax, 4
add eax, 5
ASM:
mov ecx, 4
lea eax, [ecx + 5] ; // вычисляет адрес 4+5 и помещает значение в eax
Подглава: разные вариации обнуления регистра EAX.
Для тестирования я реализовал максимально простую программу, которая идеально подходит для этой статьи.
В первую очередь я реализовал функцию которая отвечает за рандомизацию, она выбирает число от 1 до 6, в дальнейшем она будет использоваться как функция, которая выбирает алгоритм через который будет обнуляться регистр EAX:
Также у меня есть функция, которая в зависимости от того, что выберет функция randomYzer(1-2-3-4-5-6), она перейдет на соответствующий случай этого алгоритма и исполнит его:
asm:
MutateXorEax:
cmp rcx, 1
je .case_xor
cmp rcx, 2
je .case_sub
cmp rcx, 3
je .case_mov
cmp rcx, 4
je .case_and
cmp rcx, 5
je .case_push
cmp rcx, 6
je .case_imul
ret
.case_xor:
mov word [rdi], 0xC031
add rdi, 2
ret
.case_sub:
mov word [rdi], 0xC029
add rdi, 2
ret
.case_mov:
mov byte [rdi], 0xB8
mov dword [rdi+1], 0
add rdi, 5
ret
.case_and:
mov dword [rdi], 0x00E083
add rdi, 3
ret
.case_push:
mov dword [rdi], 0x58006A
add rdi, 3
ret
.case_imul:
mov dword [rdi], 0x00C06B
add rdi, 3
ret
Для статического анализа такая техника - настоящий кошмар. Если антивирус ищет вирус по конкретной сигнатуре, к примеру: "31 C0", он пропустит этот же код, если мутатор выберет вариант с PUSH/POP или IMUL. Однако для эвристического анализа, о котором мы говорили раньше, сам факт наличия такой функции является подозрительным. Эвристика видит, что программа генерирует код на лету и записывает его в секции памяти, предназначенные для выполнения. С одной стороны - это значительно может повысить энтропию, и это надо учитывать, полиморфный код спасает именно от статического анализа.
Также прошу обратить ваше внимание на add rdi - количество байтов переданных в буфер.
Если мы не будем так скажем, СДВИГАТЬ указатель на их размер, одни байты могут затереть другие, - это важно учитывать.
После чего создаем буфер внутри секции .data:
asm:
section '.data' readable writeable executable ; важно добавить флаг executable с целью не получить лулей от DEP
DestBuffer db 64 dup(0)
Компилируем нашу программу и смотрим, что все прекрасно работает:
Давайте же теперь зайдем в отладчик и посмотрим что хранится в нашем буфере!
Подглава: анализируем наш буфер
Запустив отладчик, предварительно в коде вызвав буфер мы можем перейти на адрес где вызывается буфер, в нашем случае это перед вызовом функции ExitProcess:
Запускаем нашу программу в отладчике, жмем F9, переходим метку где вызывается ExitProcess, через строчку там будет вызов нашего буфера:
Жмем F9, доходим до адреса 0000000000401027 и смотрим в дамп, феноменально - видим наши байты!:
Теперь вновь переходим в нашу программу и смотрим, что же это за байты, а оказывается, что 0xC029 это байты, отвечающие за обнуление регистра через sub eax, eax:
Рандомизация работает, прекрасно, то есть у нас есть алгоритм, который выбирает рандомизатор, они разные - но исход один и тот же.
Заключение
В завершение я бы хотел подчеркнуть, что полиморфизм - это мощный инструмент в руках разработчика. Как я вам показал, и как вы увидели, даже простая операция обнуления регистра может быть реализована десятками способов, что делает статический анализ, основанный на поиске фиксированных байтовых масок, практически бесполезным против полиморфного кода. Однако это не означает, что полиморфный код неуязвим. Современные системы защиты всё чаще полагаются на эвристический анализ, который отслеживает не столько код, сколько поведение программы: динамическую генерацию инструкций, манипуляции с памятью, подозрительные вызовы API и их последовательности.
Каждый новый метод обфускации или полиморфизма рано или поздно встречает противодействие в виде усовершенствованных эвристик, эмуляции и анализа контекста.
Кстати, в следующей статье я планирую углубиться в анти эмуляцию(конечно же, разбирая полиморфизм) и показать несколько эффективных методов против нее, в отличие от того что я писал ТУТ, есть много других способов которые гораздо безопаснее, и будут работать стабильно даже на старых машинах.
Всем удачи!
Также оставляю исходный код.