Академический Документы
Профессиональный Документы
Культура Документы
by
Qark and Quantum [VLAD]
This document attempts to explain technically NewExe infection for the
virus writer. This isn't for the novice coder and you'll need to
understand DOS EXE infection before being able to use any of it.
The infection described in detail here is the same as used by the
accompanying WinSurfer virus, there are other methods that can be done
but they aren't our concern (it's up to you to develop new code!)
You will want a copy of the New Exe header information which is available
in Ralf Browns interrupt listings under Int21h AH=4Bh, a copy of which is
included at the end of this article.
This is a map of what we are trying to do:
Before infection
After Infection
0h--------------------0h--------------------|
|
|
|
| DOS EXE header |
| DOS EXE header |
|
|
|
|
----------------------------------------|
|
|
|
| DOS Code
|
| DOS Code
|
|
|
|
|
|
|
3F8h--------------------400h--------------------|
|
|
| <- Move
| New EXE Header |
| New EXE Header |
this
| and some tables |
| and some tables |
up
|
|
|
|
| ----------------- |
| ----------------- |
| Segment
|
| Segment
|
| Table
|
| Table
|
|
|
|
|
|
|
|
| Insert -> | ----------------- | Virus segment
| ----------------- |
this
| ----------------- | entry
| Misc Other
|
| Misc Other
|
| Tables
|
| Tables
|
|
|
|
|
x
x
x
x
x
x
x
x
x
x
x
x
CS:IP ----------------------------------------|
|
|
|
| Windows Code
|
| Windows Code
|
| and Misc
|
| and Misc
|
| Segments
|
| Segments
|
|
|
|
|
End --------------------CS:IP --------------------| Virus segment
|
Append all | with code etc
|
this -> |
|
--------------------- Virus
|
| Relocation
End --------------------- Entry
Infection Theory:
----------------Test the EXE to make sure its windows and that the NE is at offset 1024.
Reduce the NE pointer at 3ch by eight.
Reduce DOS SP by eight.
Any NE offsets which are larger than the segment table offset must be
increased by eight.
Increment the segment counter.
Save the CS:IP.
Point the CS:IP to the new segment entry
Calculate the position of the end of the segment table, move all data up to
and including the segment table up by eight bytes.
Add another segment entry
Write the virus to the end of the file
Write relocation entry to the end of the file.
--That is a very basic overview of what is wanted.
The main idea of it all is moving the NE header forward to add a new
segment table entry, and changing the CS:IP to point to it.
We'll go through all the stages step by step.
Testing for a Windows executable.
--------------------------------A NewEXE file always begins with a DOS header and some DOS executable
code. With a windows executable:
The word at offset 0 is 'MZ'.
The word at offset 18h is 40h or greater.
assuming that these conditions are met then we also want the pointer to the
NE header (3ch) to be equal to 400h. The reason for this is that room is
needed to move the NE header forward by 8.
Rewriting the DOS header.
------------------------Before rewriting the DOS header, reduce the DOS SP field (10h) by 8, because
if it was pointing to the end of the DOS code section it would point to
the start of the NE header. (No real reason for doing it really)
The NE header pointer at 3ch must be reduced by 8, because we intend on
moving most of the NE header forward to that position.
Modifications to the NE header.
------------------------------From this point onwards we are referring to the offsets in relation to the
NE header.
The pointer to the segment table is a word at 22h. If any of the pointers
at 4,24h,26h,28h,2ah are larger than [22h], then increase that
pointer by eight. The reason for this is that since we are inserting a
new segment table entry, all tables behind the segment table will be
eight bytes further from the start of the header.
Offsets 2 and 6 in the segment record are just the virus size. Offset
4 is the segment attribute, we use 180h, which makes the segment executable
with relocations.
Calculating offset 0 is more difficult. It is necessary to get the file
length and shift it right by the alignment shift value which was saved
earlier. I wouldn't know how to do a dword shift so I convert it to a
division.
File length into DX:AX :
mov
ax,4202h
xor
cx,cx
cwd
int
21h
Shift right DX:AX by alignment shift:
mov
cl,byte ptr alignment_shift
push
bx
mov
bx,1
shl
bx,cl
mov
cx,bx
pop
bx
div
cx
AX=required offset 0 value
Write this eight byte table behind all the moved header.
Writing the Virus.
-----------------Lseek to the end of the executable and write the virus.
db
dw
dw
relocation dw
db
db
dw
hostcs
dw
hostip
dw
0eah
;Opcode of JMP FAR PTR
0
0ffffh
1
3
4
offset
0
0
set a vector in the IDT to point to our code like we do with dos and the
IVT but - there are complications.
V86 mode.
--------When Windows is running in 386 enhanced mode the processor is locked into
v86 mode. In v86 mode each application has its own 1mb memory block. All
EMS/XMS is locked away from the application and (usually) each process has
its own IDT/IVT. The idea being to allow the application to think it is
alone on the system. Any attempt to access any other tasks that may be
running is a "violation of system integrity" and will cause an exception.
This is no good for a virus and by now you're thinking we're screwed but
windows solves the problem for us. Windows has but one IDT and of course
only one IVT to shadow it and thus if we are to a hook a vector in the IDT
it will be reflected in every task. (Ever seen that OS/2 warp advert where
they say "... and true multi-tasking" - well this simply means that OS/2
warp holds a seperate IDT for every task under v86 mode.)
Setting the Vector.
------------------So let's get this straight.. the IVT is the one at 0:0 that we all know and
love and the IDT is a protected mode/v86 shadow of it. So you're most
probably asking "why can't we just hook the IVT and let windows reflect it
into the IDT?" - well there's 2 reasons: firstly windows doesn't "reflect"
the IVT as we would like to think, actually it copies the IVT into the IDT
on startup then replaces certain routines with "protected mode call
structures" which call the real mode routines and some routines it doesn't
even do this, secondly trying to access the IVT directly is a big nono and
will cause an exception error.
Can we use DOS ? well yes, you can.. most dos functions have been reflected
up into windows although windows is supposed to be trying to get rid of
them. So you can call int 21,25/35 to set the vector but remember that this
will be setting a vector in the GVT and only at the discretion of windows.
A better way: use DPMI. Windows is NOT your host under protected mode or
even v86 mode as microsoft would have you believe. Windows has an
overlooker that provides windows time-slicing and exception abilities. In
fact all windows does is act as a mediator to DPMI and basically slow things
down. So how do we use DPMI to set the vector ? well.. by using functions
0204h/0205h - with the vector in BL - of the DPMI provider int 31. This
way windows has no say over what goes in its IDT and thus we have a LOT of
control.
Writing the Interrupt.
---------------------This is perhaps one of the easier things to do - once you understand the
principles. In protected mode/v86 mode your code segment is supposed to be
read only and thus you're supposed to have a code, stack and data segment.
Everyone knows this is bad so what we need is a way to write to the code
segment. You won't find one - writing to the code segment just isn't
possible. BUT - if we were to have a data segment that pointed to the same
code segment then we could do it. (actually we don't have any segments in
protect mode.. we have a thing called a "selector" which gives access
parameters to areas of memory.)
To get an "alias selector" to the code segment we can use another DPMI
function and bypass windows altogether: function ax=000ah with the code
selector in bx of int 31h, it returns a read/writeable alias selector to
the code segment in ax.
So now Protected mode isn't protected and we're free to write to the code
segment. One problem arises from this idea. Qark wanted to put this code
to generate the alias in the loader part of the virus and store it in the
code segment for the interrupt routine. This seemed fine until I found out
that windows likes to move segments around. One minute your code might be
at one address the next it may be at another. Thus you should always
regenerate the alias on every execution of the ISR or lock down the segment
- we chose against this last approach as it can cause exception errors from
windows (DPMI has no problems with this strategy.)
For a detailed look at DPMI consult Ralf Brown's interrupt listing under
int 31h. These functions haven't been included in this text.
Staying Resident.
----------------Finally: don't go resident off something that can be closed. If you go
resident off something like mine sweeper or something and the user closes
the application, your code segment will be deleted and removed from the IDT
and will point to an empty segment. This will cause windows to hang.
Solution: look for a process that will never be closed, direct action
infect this as soon as you find it and only ever go resident off it.
In the 'system.ini' file in your windows directory, is a variable 'shell='
that points to a file that is never closed. Mostly it will be
'progman.exe' but sometimes other programs are used, so read it in and
infect it.
To find the path of the windows directory, search the environment for a
variable called 'windir=' (yes, lowercase!). On entry to any windows
executable ES will point to the PSP (or something functionally similar).
The word at 2ch is the segment (selector) of the environment segment.
Scan through this as you would within a DOS application. We didn't discover
this through any documentation, but when Quantum accidentally typed 'set'
from within a DOS window.
Facts and Possible Techniques.
-----------------------------At offset 0eh in the New Executable header is the 'auto data segment index'.
This is another segment table index, which will be automatically in DS
on execution entry. Although this is pure guesswork, if you set that
'auto data segment' to the same segment as CS, (ie in every way the same
except the segment attributes) then you could write to your CS without
using DPMI and would also open the gateway to polymorphic windows viruses.
(Polymorphism would be pretty useless if you had to put a DPMI call before
you could write to your code segment.)
Mark Ludwig's 'Windows Virus #2' uses a different form of infection in that
it extends the length of the code segment as indicated in the segment
table, and moves the entire host program starting from the end of the file
back by virus length until the end of the code segment is reached, at which
point the virus is written. The IP is then repointed to the virus.
There are some advantages to this, such as no need to add relocation entries
because the jmp is intra-segment but this is greatly outweighed by the
fact that moving an entire file is very slow and a much larger number of
WORD
WORD
WORD
0Ch
WORD
file header:
Description
.EXE signature, either "MZ" or "ZM" (5A4Dh or 4D5Ah)
number of bytes in last 512-byte page of executable
total number of 512-byte pages in executable (includes any
partial last page)
number of relocation entries
header size in paragraphs
minimum paragraphs of memory to allocation in addition to
executable's size
maxi paragraphs to allocate in addition to executable's size
0Eh
10h
12h
14h
18h
WORD
WORD
WORD
DWORD
WORD
3Ch
WORD
minimum code swap area size
3Eh 2 BYTEs expected Windows version (minor version first)
Note: this header is documented in detail in the Windows 3.1 SDK
Programmer's Reference, Vol 4.
Bitfields for new executable program flags:
Bit(s) Description
0-1
DGROUP type
0 = none
1 = single shared
2 = multiple (unshared)
3 = (null)
2
global initialization
3
protected mode only
4
8086 instructions
5
80286 instructions
6
80386 instructions
7
80x87 instructions
Bitfields for new executable application flags:
Bit(s) Description
0-2
application type
001 full screen (not aware of Windows/P.M. API)
010 compatible with Windows/P.M. API
011 uses Windows/P.M. API
3
is a Family Application (OS/2)
5
0=executable, 1=errors in image
6
non-conforming program (valid stack is not maintained)
7
DLL or driver rather than application
(SS:SP info invalid, CS:IP points at FAR init routine called with
AX=module handle which returns AX=0000h on failure, AX nonzero on
successful initialization)
Format
00h
02h
04h
06h
Note:
Offset Size
00h
BYTE
01h
BYTE
02h
04h
06h
WORD
WORD
WORD
[end]
Description
relocation type
00h LOBYTE
02h BASE
03h PTR
05h OFFS
0Bh PTR48
0Dh OFFS32
flags
bit 2: additive
offset within segment
target address segment
target address offset