Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Windows Executable Infection
- 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 that 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 the relocation entry at 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.
- The segment counter is a word at offset 1ch. Increment it by one because
- since we are adding another segment, we need to indicate this in the
- header.
- At offset 14h is the dword pointer to the program entry point, CS:IP
- where the CS is actually the segment table entry number. Save this pointer
- for use in your relocation entry (that part will be explained in detail
- later on). Now point the CS:IP instead to the virus segment. Do this
- by writing the necessary starting IP value to 14h (offset of the entry into
- your segment) and the segment counter value into 16h (since the virus
- segment is the last in the segment table it will be equal to the segment
- counter value which we have incremented).
- You can work this out for yourself, but what you need to do now is move
- the entire NE header, up to and including the segment table, but not
- the data behind it, forward by eight bytes. Here's an equation on
- calculating how much to move:
- ((segmentcounter-1)*8)+word ptr [22h]
- Directly after this position is where to insert the new virus segment entry.
- The Virus Segment Entry.
- ------------------------
- Format of new executable segment table record:
- 00h WORD offset in the file (shift left by alignment shift for byte offs)
- 02h WORD length of the image in file (0000h = 64K)
- 04h WORD segment attributes (see below)
- 06h WORD number of bytes to allocate for the segment (0000h = 64K)
- 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 headers.
- Writing the Virus.
- ------------------
- Lseek to the end of the executable and write the virus.
- Writing the Relocation Entry.
- -----------------------------
- You need a relocation entry so that you can return control to the host
- after the virus has done its dirty work. The relocation entries follow
- the segment itself.
- Format of relocation entry:
- Offset Size Description
- 00 WORD Number of relocation entries
- Offset Size Description
- 00 BYTE relocation type
- 01 BYTE relocation flags
- 02 WORD offset within segment
- 04 WORD target address segment
- 06 WORD target address offset
- The first word will be 1 because only 1 relocation entry.
- The first byte is 3 to signal a 32-bit pointer (a far jump).
- The second byte is 4 to signal additive relocation. (doesn't work without this)
- Word at offset 2 is the offset of the dword after the 'far jump' opcode.
- Word at offset 4 is the CS of the original host CS:IP
- Word at offset 6 is the IP of the original host CS:IP
- To put all this into code:
- db 0eah ;Opcode of JMP FAR PTR
- ret_ip dw 0
- dw 0ffffh
- relocation dw 1 ;One relocation entry
- db 3 ;32bit pointer relocation
- db 4 ;Additive relocation
- dw offset ret_ip ;Offset of the relocation in segment
- hostcs dw 0 ;CS:IP of place we want our relocation to
- hostip dw 0 ; point.
- Note: the relocation address data (ret_ip) _must_ be FFFF:0000 as above
- because Windows treats it as a linked list. If you don't understand
- don't worry, just do it! It won't work otherwise. It is important
- that before writing the virus body to the executable you set
- this address to FFFF:0000 so that the executable will be set up correctly.
- Write the relocation entry at the end of the file.
- Windows allows a standard Int 21h call just fine so all file manipulation
- is as simple as ever, with a few minor changes.
- Writting TSR/ISR's under Windows.
- ---------------------------------
- Windows is a protected mode environment and thus to set a vector under it we
- must manipulate the various tables that protected mode requires to be
- updated. Of these tables, one is of interest to us. The "Interrupt
- Descriptor Table" (IDT). The IDT holds all the Protected Mode interrupt
- vectors that are in the "Interrupt Vector Table" (IVT) - if they are
- supported - and some more. This is all well and good - all we have to do is
- set a vector in the IDT to point to our code as 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 is 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 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 separate 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 are 2 reasons: firstly windows doesn't "reflect"
- the IVT as we would like to think, actually copies the IVT into the IDT
- on startup then replaces certain routines with "protected mode call
- structures" which call the real mode routines 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
- in 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 exceptional 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 have 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
- the 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
- the fact that moving an entire file is very slow and a much larger number of
- pointers in the header need to be modified to account for the change.
- There are DPMI calls for allocating memory (Int 31h ax=501h) but it is
- unknown at this point whether the memory remains allocated after the program
- is closed. Mark Ludwig makes extensive use of these calls for temporary
- buffers in his direct action 'Windows Virus #2'.
- Mark Ludwig also demonstrates the use of the WinAPI calls, which involve adding
- a new relocation entry for each call and pushing some stuff on the stack.
- This may become important when win95 comes out but for the moment it is
- more convenient to simply use int 21h.
- Some food for thought.
- ----------------------
- Microsoft has a dream [here we go], the dream is to control the world. They
- intend to do this with applications like Windows [scary isn't it ?], and by
- the looks of it they might succeed. A lot of people think computers should
- be easy, that there should be no skill involved in it and that everyone
- should HAVE to use one. Microsoft is using this idea to flood the market
- with shit like Windows, in the hope that it will be accepted as a "standard".
- Windows is already this but Microsoft isn't happy with one success, they
- want to be on top of the hill.
- This is where we come in. If Microsoft is going to succeed in taking over
- the world then we should be there making their lives difficult. The
- WinSurfer virus created by us is so stable that an average Windows
- user wouldn't even notice it. [Then again the average Windows user
- wouldn't notice if his hair caught on fire.] And thus will stay with us
- for quite some time. At least until AVers recognize the windows market
- and start releasing scanners.
- The New Executable format. (From Ralf Browns Interrupt listing 21 AH=4B)
- --------------------------
- new executables begin running with the following register values
- AX = environment segment (Wrong! AX=0)
- BX = offset of command tail in environment segment
- CX = size of the automatic data segment (0000h = 64K)
- ES,BP = 0000h (Wrong! ES=PSP)
- DS = automatic data segment
- SS:SP = initial stack
- the command tail corresponds to an old executable's PSP:0081h and
- following, except that the 0Dh is turned into a NUL (00h); new
- format executables have no PSP (All wrong)
- Format of .EXE file header:
- Offset Size Description
- 00h 2 BYTEs .EXE signature, either "MZ" or "ZM" (5A4Dh or 4D5Ah)
- 02h WORD number of bytes in last 512-byte page of executable
- 04h WORD total number of 512-byte pages in executable (includes any
- partial last page)
- 06h WORD number of relocation entries
- 08h WORD header size in paragraphs
- 0Ah WORD minimum paragraphs of memory to allocation in addition to
- executable's size
- 0Ch WORD maxi paragraphs to allocate in addition to the executable's size
- 0Eh WORD initial SS relative to start of executable
- 10h WORD initial SP
- 12h WORD checksum (one's complement of the sum of all words in the executable)
- 14h DWORD initial CS:IP relative to start of executable
- 18h WORD offset within the header of the relocation table
- 40h or greater for new-format (NE,LE,LX,PE,etc.) executable
- 1Ah WORD overlay number (normally 0000h = main program)
- ---new executable---
- 1Ch 4 BYTEs ???
- 20h WORD behavior bits
- 22h 26 BYTEs reserved for additional behavior info
- 3Ch DWORD offset of new executable (NE,LE,etc) header within disk file,
- or 00000000h if plain MZ executable
- Format of the new executable header:
- Offset Size Description
- 00h 2 BYTEs "NE" (4Eh 45h) signature
- 02h 2 BYTEs linker version (major, then minor)
- 04h WORD offset from the start of this header to the entry table (see below)
- 06h WORD length of entry table in bytes
- 08h DWORD file load CRC (0 in Borland's TPW)
- 0Ch BYTE program flags (see below)
- 0Dh BYTE application flags (see below)
- 0Eh WORD auto data segment index
- 10h WORD initial local heap size
- 12h WORD initial stack size (added to data seg, 0000h if SS <> DS)
- 14h DWORD program entry point (CS:IP), "CS" is index into segment table
- 18h DWORD initial stack pointer (SS:SP), "SS" is segment index
- if SS=automatic data segment and SP=0000h, the stack pointer
- is set to the top of the automatic data segment, just below
- the local heap
- 1Ch WORD segment count
- 1Eh WORD module reference count
- 20h WORD length of nonresident names table in bytes
- 22h WORD offset from the start of this header to the segment table (see below)
- 24h WORD offset from the start of this header to the resource table
- 26h WORD offset from the start of this header to the resident names table
- 28h WORD offset from the start of this header to the module reference table
- 2Ah WORD offset from the start of this header to the imported names table
- (array of counted strings, terminated with a string of length
- 00h)
- 2Ch DWORD offset from the start of the file to nonresident names table
- 30h WORD count of moveable entry point listed in the entry table
- 32h WORD file alignment size shift count
- 0 is equivalent to 9 (default 512-byte pages)
- 34h WORD number of resource table entries
- 36h BYTE target operating system
- 00h unknown
- 01h OS/2
- 02h Windows
- 03h European MS-DOS 4.x
- 04h Windows 386
- 05h BOSS (Borland Operating System Services)
- 37h BYTE other EXE flags
- bit 0: supports long filenames
- bit 1: 2.X protected mode
- bit 2: 2.X proportional font
- bit 3: gangload area
- 38h WORD offset to return thunks or the start of gangload area
- 3Ah WORD offset to segment reference thunks or length of gangload area
- 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 programs (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 of new executable segment table record:
- 00h WORD offset in the file (shift left by align shift to get byte offs)
- 02h WORD length of the image in file (0000h = 64K)
- 04h WORD segment attributes (see below)
- 06h WORD number of bytes to allocate for the segment (0000h = 64K)
- Note: the first segment table entry is entry number 1
- Bitfields for segment attributes:
- Bit(s) Description
- 0 data segment rather than code segment
- 1 unused???
- 2 real mode
- 3 iterated
- 4 movable
- 5 sharable
- 6 preloaded rather than demand-loaded
- 7 execute-only (code) or read-only (data)
- 8 relocations (directly following code for this segment)
- 9 debug info present
- 10,11 80286 DPL bits
- 12 discardable
- 13-15 discard priority
- Format of new executable relocation data (immediately follows segment image):
- Offset Size Description
- 00h WORD number of relocation items
- 02h 8N BYTEs relocation items
- Offset Size Description
- 00h BYTE relocation type
- 00h LOBYTE
- 02h BASE
- 03h PTR
- 05h OFFS
- 0Bh PTR48
- 0Dh OFFS32
- 01h BYTE flags
- bit 2: additive
- 02h WORD offset within segment
- 04h WORD target address segment
- 06h WORD target address offset
- [end]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement