Давно обдумывал идею морфа с максимальной отдачей. Изучение доступных
источников указывало на необходимость большого количества кода с чем-то
наподобие специфичного дизассемблера и таблицы пере(под)становок. Однако
метод изменения уже готового кода имеет свои технические ограничения -
всего не предусмотришь и не поменяешь, а чем больше кодеса таскать с
собой, тем выше шансы залететь по банальной сигнатуре. Значит надо нечто
такое, чтобы сигнатуры не было в принципе.
Один из вариантов - полиморфный криптор. В первом приближении состоит из расшифровывающего полиморфного кода и криптоконтейнера. Внутри контейнера согласно банальной логике будет некий постоянный код. Требует постоянного наращивания примочек для предотвращения полной эмуляции.
Идеалом морфа был бы такой код, чтобы две его морф-копии отличались максимально, вплоть до отсутствия похожих инструкций. Достичь такого сложно, однако можно попытаться приблизиться. Взяв за основу макроязык пакета MASM, основательно покопавшись в его небогатой документации, приходим к выводу, что можно на стадии сборки исходного кода подменять инструкции на аналоги, встраивать мусорный код, генерить рандомную таблицу импорта, шифровать строковые ресурсы и многое другое, от чего у среднестатистического вирусного аналитика волосы зашевелятся не только на голове.
Основой и первым модулем в наборе будет генератор рандомных чисел. Все было бы прекрасно, однако макро препроцессор не знает такого. Попытка прочитать рандомное значение через переменную окружения RANDOM не увенчалась успехом. Пришлось изобретать велосипед - обертку вокруг компилятора ML.exe которая при каждом вызове генерила рандомное значение в переменной окружения RNDSEED (локально для текущего процесса и его потомков). Это значение уже считывалось макросом. Итак, кодес:
Один из вариантов - полиморфный криптор. В первом приближении состоит из расшифровывающего полиморфного кода и криптоконтейнера. Внутри контейнера согласно банальной логике будет некий постоянный код. Требует постоянного наращивания примочек для предотвращения полной эмуляции.
Идеалом морфа был бы такой код, чтобы две его морф-копии отличались максимально, вплоть до отсутствия похожих инструкций. Достичь такого сложно, однако можно попытаться приблизиться. Взяв за основу макроязык пакета MASM, основательно покопавшись в его небогатой документации, приходим к выводу, что можно на стадии сборки исходного кода подменять инструкции на аналоги, встраивать мусорный код, генерить рандомную таблицу импорта, шифровать строковые ресурсы и многое другое, от чего у среднестатистического вирусного аналитика волосы зашевелятся не только на голове.
Основой и первым модулем в наборе будет генератор рандомных чисел. Все было бы прекрасно, однако макро препроцессор не знает такого. Попытка прочитать рандомное значение через переменную окружения RANDOM не увенчалась успехом. Пришлось изобретать велосипед - обертку вокруг компилятора ML.exe которая при каждом вызове генерила рандомное значение в переменной окружения RNDSEED (локально для текущего процесса и его потомков). Это значение уже считывалось макросом. Итак, кодес:
@Init_rnd macro
local t
;check if RNDSEED set
if @SizeStr(@Environ(RNDSEED)) le 1
.ERR < RNDSEED environment var wasnt set!! Use ml.exe wrapper to randomly set it >
endif
t SubStr @Environ(RNDSEED),1,@SizeStr(@Environ(RNDSEED))
RNDSEED = t
endm
; gen random [0..Num]
@Random macro Num:REQ
RNDSEED = (((RNDSEED*25733d) and 0FFFFh) 13849d) and 0FFFFh
exitm < (RNDSEED*(Num 1)) shr 16 >
endm
@Init_rnd
Модуль самоинициализируется вызовом @Init_rnd, где проверяется наличие
переменной окружения и выполняется ее сохранение в переменную RNDSEED.
Теперь вызовом макроса @Random(х) мы получим макро-случайное-значение
(макропеременная числового типа), с приемлемой "случайностью", которое
сможем использовать в дальнейших макросах.
Теперь можно осилить и генератор мусорного кода на макросах. Поскольку вставлять этот код мы будем в различные места исходного творения, необходимо предусмотреть возможность защиты реального кодеса от действий мусорного (треш в регистрах, флагах к примеру). Самое банальное - обрамлять мусор в pushad / popad или аналогичные конструкции. Не подходит, ибо в данном случае появляется возможность создать алгоритм, который бы этот мусор отсеивал. Значит сделаем генерацию мусора так, чтобы необходимые нам на данном этапе регистры не использовались мусором.
На первом этапе не будем усложнять жизнь условными переходами, вызовами процедур и прочей радостью, просто будем создавать мусор для того чтобы сбить сигнатурную маску. Для этого воспользуемся следующим набором команд:
[mov/add/sub/xor/or/cmp/rol/ror/shl/shr/inc/dec/neg/not/nop]
Необходимо учитывать, что у каждой из команд свое количество операндов. Его определением займется следующий макрос:
Теперь можно осилить и генератор мусорного кода на макросах. Поскольку вставлять этот код мы будем в различные места исходного творения, необходимо предусмотреть возможность защиты реального кодеса от действий мусорного (треш в регистрах, флагах к примеру). Самое банальное - обрамлять мусор в pushad / popad или аналогичные конструкции. Не подходит, ибо в данном случае появляется возможность создать алгоритм, который бы этот мусор отсеивал. Значит сделаем генерацию мусора так, чтобы необходимые нам на данном этапе регистры не использовались мусором.
На первом этапе не будем усложнять жизнь условными переходами, вызовами процедур и прочей радостью, просто будем создавать мусор для того чтобы сбить сигнатурную маску. Для этого воспользуемся следующим набором команд:
[mov/add/sub/xor/or/cmp/rol/ror/shl/shr/inc/dec/neg/not/nop]
Необходимо учитывать, что у каждой из команд свое количество операндов. Его определением займется следующий макрос:
@GetParamCount macro arg:REQ
if @InStr(1,mov add sub xor or cmp,arg) gt 0
exitm < 2 >
elseif @InStr(1,rol ror shl shr,arg) gt 0
exitm < 21 >
elseif @InStr(1,inc dec neg not,arg) gt 0
exitm < 1 >
elseif @InStr(1,nop das daa aaa aad aam aas,arg) gt 0
exitm < 0 >
else
.ERR unknown param passed to @GetParamCount macro
endif
endm
На выходе мы получаем следующие значения:
2 - два операнда, второй может быть как регистром соответствующей разрядности, так и числом
21 - два операнда, второе обязательно число
1 - один операнд
0 - без операндов
На всякий случай еще надо бы учесть что если нам передадут в качестве регистра который нельзя использовать eax, то соотвественно и ah, и al трогать нельзя. В итоге после небольшого шаманства получаем следующий кодес:
2 - два операнда, второй может быть как регистром соответствующей разрядности, так и числом
21 - два операнда, второе обязательно число
1 - один операнд
0 - без операндов
На всякий случай еще надо бы учесть что если нам передадут в качестве регистра который нельзя использовать eax, то соотвественно и ah, и al трогать нельзя. В итоге после небольшого шаманства получаем следующий кодес:
; Generate Random Instruction, n times
@GRIn macro Count:REQ, ExclRegsIn: REQ
LOCAL i
LOCAL Op1
LOCAL Op2
LOCAL Cmd
LOCAL R
LOCAL ExclRegs
LOCAL x
i=Count
ExclRegs TEXTEQU < ExclRegsIn >
;expand eax - > ah, al, etc
* if @InStr(1, *ExclRegs, < eax >) gt 0
ExclRegs CatStr ExclRegs, < ah al >
endif
if @InStr(1, *ExclRegs, < ebx >) gt 0
ExclRegs CatStr ExclRegs, < bh bl >
endif
if @InStr(1, *ExclRegs, < ecx >) gt 0
ExclRegs CatStr ExclRegs, < ch cl >
endif
if @InStr(1, *ExclRegs, < edx >) ne 0
ExclRegs CatStr ExclRegs, < dh dl >
endif
WHILE i gt 0
;select instruction
Cmd Substr < mov/add/sub/xor/or /cmp/rol/ror/shl/shr/inc/dec/neg/not/nop >,(1+(@Random(14)*4)),3
;select 16/32 registers which is not in passed exclude list
if @Random(1) eq 1
R=1
*WHILE R ne 0
;Op1 Substr @GetReg16(),1,2
Op1 Substr < ah/al/dh/dl/bh/bl/ch/cl >,(1+(@Random(7)*3)),2
R=@InStr(1, *ExclRegs, *Op1)
ENDM
R=1
*WHILE R ne 0
;Op2 Substr @GetReg16(),1,2
Op2 Substr < ah/al/dh/dl/bh/bl/ch/cl >,(1+(@Random(7)*3)),2
R=@InStr(1, *ExclRegs, *Op2)
ENDM
else
R=1
*WHILE R ne 0
;Op1 Substr @GetReg32(),1,3
Op1 Substr < eax/edx/edi/esi/ebx/ecx >,(1+(@Random(5)*4)),3
R=@InStr(1, *ExclRegs, *Op1)
ENDM
R=1
*WHILE R ne 0
;Op2 Substr @GetReg32(),1,3
Op2 Substr < eax/edx/edi/esi/ebx/ecx >,(1+(@Random(5)*4)),3
R=@InStr(1, *ExclRegs, *Op2)
ENDM
endif
;select type
x=@GetParamCount(*Cmd)
if x eq 0 ;no operands
Cmd
endif
if @GetParamCount(*Cmd) eq 2 ;standart 2 opers 2nd may be a number
if @Random(1) eq 1
Cmd Op1, Op2
else
Cmd Op1, @Random(255)
endif
endif
if @GetParamCount(*Cmd) eq 21 ;2 params second must be a number
Cmd Op1, @Random(31)
endif
if @GetParamCount(*Cmd) eq 1 ;only 1 param
Cmd Op1
endif
i=i-1
ENDM
endm
Пришлось заменить все знаки процента перед переменными и операторами на
звездочку, не то изза кривизны собственных рук, не то из за ебанутости
данной блогоплощадки. Кому надо, думаю поймет и исправит.
Итак, теперь вставив в код вызов:
Итак, теперь вставив в код вызов:
@GRIn 10, eax
мы получим 10 рандомных инструкций которые не будут трогать регистр eax.
Сталобыть камменты приветствуются, а в следующих постах я опишу другие
важные аспекты макроморфа.
=====
~$ Удачи.
Комментариев нет:
Отправить комментарий