Страницы

Из источника: Метаморф в сырцах

Давно обдумывал идею морфа с максимальной отдачей. Изучение доступных источников указывало на необходимость большого количества кода с чем-то наподобие специфичного дизассемблера и таблицы пере(под)становок. Однако метод изменения уже готового кода имеет свои технические ограничения - всего не предусмотришь и не поменяешь, а чем больше кодеса таскать с собой, тем выше шансы залететь по банальной сигнатуре. Значит надо нечто такое, чтобы сигнатуры не было в принципе.
    Один из вариантов - полиморфный криптор. В первом приближении состоит из расшифровывающего полиморфного кода и криптоконтейнера. Внутри контейнера согласно банальной логике будет некий постоянный код. Требует постоянного наращивания примочек для предотвращения полной эмуляции.
    Идеалом морфа был бы такой код, чтобы две его морф-копии отличались максимально, вплоть до отсутствия похожих инструкций. Достичь такого сложно, однако можно попытаться приблизиться. Взяв за основу макроязык пакета 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]
    Необходимо учитывать, что у каждой из команд свое количество операндов. Его определением займется следующий макрос:

@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 трогать нельзя. В итоге после небольшого шаманства получаем следующий кодес:
; 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. Сталобыть камменты приветствуются, а в следующих постах я опишу другие важные аспекты макроморфа.

 Из источника: http://interpol.blog.ru Продолжение - Часть2




=====
~$ Удачи.





Комментариев нет:

Отправить комментарий