
                                        /-----------------------------\
                                        | Xine - issue #2 - Phile 009 |
                                        \-----------------------------/

; GetProcAdd for Kernel32 - finding Kernel32 API's
; by jhb

;  One of the major hurdles for Virii writers who want to extend there selves
; to Win95 virii, is the ability to call the Win95 API. But the usual way to
; call these API is to have them already link and in the import area of the PE
; file. So how do you call the API if you do not have them linked at run time?
;   Well VLAD show us we can hard code the API address's but if Win95 changes 
; the kernel location ,we have a problem.  Next VLAD shows us a way to "patch"
; the import area to get getproccaddress and loadlibrary, this allows us to get
; the address of all the import by name API. Another way is demo'ed in this
; code by assuming one thing the kernel address (0BFF70000h) we can use the 
; info in the PE file format of the Kernel32 to locate any API named/ordinal.
; This means we can have access to any API that is Exported... Well a modified
; form of the routine is used in the Puma virus to import all the need API.
; The only problem is the hardcoded Kernel32 address. Well after I wrote this
; code I found that if the program imported any kernel32 routine then the 
; address was located in the entry of the Kernel32 in the import area.
;   Here is a diagram that hopefully illustrates what I am poorly explaining
;  an entry in the import table is like so:
;
;   offset            b4 runtime                         runtime
;   0            rva Pnter to first API imported         same
;
;   4            000000                                  timedate of DLL
;
;   8            000000                                  load address of
;                                                        the dll 
;
;   C            rva pnter to dll name                   same
;
;   10           rva Pnter to first API imported         address to jmp
;                                                        tble to API
;
;  Well to some this is still nonsense, read the PE format docs and it mite
; make more sense ;),  The important thing is that the Dll address is there
; and can be used. Problem here if the file does not have a Kernel API,
; doubtful since to even end the file you use ExitProcess. Second problem
; if the file is "Binded" this means the file has the import address already
; loaded into it, Basically this can be checked b4 runtime offset 4 already
; have a date and time offset 8 has FFFFFFF in it, but change the date time
; field and then all is ok, the file is no longer "binded". This way would
; just setup a routine to get the kernal address and then run the regular
; routine here to get the Address of whatever kernel we need.
;  Anyway this routine is just a simple example I use to get the address of
; routines I want to test ideas on.  Getting the CallVxd0 or ordinal 1
; of the Kernel this gives us access to  VXD routines like Vwin32_int21Dispatch
; so with one routine you could infect the a file using int 21 calls. Well 
; this should work in theory. Well I hope this routine helps others understand
; the writing of Virii in Win95.
;
; Yes I know that if the ordinal is the last routine found there is garbage
; in the title of the box but oh well you fix it it demos the idea ok
; jhb 1/97

;   The main idea is to find the Kernel32 loaded address which is the
; number we will need to add to all the rva to get the actual location in
; memory now located the import data area by using  the info in the data
; directory the import data is located at the 78h bytes into the PE header
; now we take this rva add the kernel and we have the import segment.
; now we get the pointers to the 3 arrays nameindex, functionindex,
; AddressofFunction we also need the ordinal base but thats easy also
; Each index is a pointer to the next bit of info to find the Address of the
; function. First take the Name and find which string it matches to now use
; this number as an index into the ordinals now use the ordinal as an index
; into the Array of Funcutions address then ya got it. Well I doubt that made
; sense I hope but doubt my comments in the code help
;
;
; To compile:
;  tasm32 /ml /m3 getadd,,;
;  tlink32 /Tpe /aa /c getadd,getadd,, import32.lib
;


; just trying to visualize how i am storing all the data on the stack
;
;               -14h
;               -10h
;               -0Ch
;holders        -10
;ret_address    -8
;old_bp         -4
Export_rva      equ     0
kern1           equ     Export_rva + 4 
baseF           equ     kern1 + 4 
AddFunc         equ     baseF + 4
AddName         equ     AddFunc + 4
Nindex          equ     AddName + 4 
AddOrd          equ     Nindex + 4
limit           equ     AddOrd + 4        
Where           equ     limit + 4
Looking         equ     Where + 4
worksp          equ     Looking + 4

.386
locals
jumps
.model flat ,stdcall


;Working version of a get address routine ends with di containing the
;address of the process
;set to work with KERNEl32.dll and nothing else
;will only look till there is no more names to check 
;

;Define the needed external func tions and constants here.

extrn           ExitProcess:PROC
extrn           MessageBoxA:PROC                ;note in the user32.dll
 

.data                                        ;the data area
title1          db      'this is a title',0
mess            db      'this is a message',0
storage         dd      4 dup(0)       

.code                                   ;executable code starts here

HOST:
       
                jmp     OverData
;fake data
kern            dd      0BFF70000h      ;must add to all rva's

;ok below is the functions we are going to look for
;format is the standard end in 0 if you pass an ordinal
; give me the ascii = of the number in the first 2 bytes then
; the number in hex you looking for. 
LookFor         db      'ReadFile',0
                db      'ExitProcess',0
                db      39h, 30h, 90d          ;'AllocLSCallback',0
last            db      30h, 31h, 01d 
                db      0Bh
                db      0


OverData:
        mov     esi,offset LookFor              ;where the list of functions
                                                ;is

        mov     eax,4                           ;how many functions
         

        call    GetProcAdd                      ;on return the stack wil
                                                ;have addresses in it
                                                ;last request first pop
                                                ;format  LIFO ;)


;-

        ;edi = the address of the function in question
        ;for this test case we will use a dialog box and show the
        ;the last function found and its address
 

Box_it:

;=====================================================================
;this simply makes the value in edi converted to a ascii string with a null
; 0 at the end Borlands version was too long


        push    edi
        mov     edi,offset mess
        mov     cx,1ch

digit_loop:
        pop     eax
        push    eax
        
        shr     eax,Cl
        and     ax,000fh
        sub     cx,4
        cmp     al,9
        jle     number

        sub     al,0ah
        add     al,41h
        jmp     letter

number:
        or      al,30h
letter:
        stosb
        cmp     cx,0fffCh
        jne     digit_loop
        mov     al,0
        stosb

        pop     edi

;now we call the MenuBox API
;------------------------------------------------------------------------
        mov     eax, -1
        push    eax

        mov     eax, offset last                        ;
        push    eax                                     ;

        mov     eax,offset mess                         ;
        push    eax                                     ;

        mov     eax,0
        push    eax

        call    MessageBoxA

;just some playing around
        ;push    offset return_here

        ;push    0bff638d9h
        ;ret

;return_here:
        


fini:           ;left over from a Teacher who use to teach me C++

        push    LARGE -1
        ;call    dword ptr edi          ;when I used the routine to locate
                                        ;the ExitProcess address
        call    ExitProcess             ;this simply terminates the program

;===========================================================================
GetProcAdd:
;edi = where to place the address ;Where
;esi = the list of address's   delimited by Zero end in 0Bh ;Looking
;

        pop     ebx                             ;set up and save
                                                ;some storage for the
        shl     eax,2                           ;routine
        sub     esp,eax                         ;
        mov     edi, esp                        ;
                                                ;
        push    ebx                             ;
                                                ;
        push    ebp                             ;
        sub     esp,worksp                      ;
        mov     ebp,esp                         ;
         

        mov     [ebp +  Where],edi              ;save where will store them 
        mov     [ebp + Looking],esi             ;save which ones looking fo

        mov     esi,dword ptr [kern]

        lodsw
        cmp     ax,'ZM'                ;we assume Bff70000h for location
        jne     Not95                  ;of the Kernel

        xor     eax,eax                ;ok its the start of a exe file
        add     esi,3Ah                ;get pointer to the PE start
        lodsw

        mov     esi,dword ptr [kern]   ;if this is not true get out
        add     esi,eax                ;now load that word
        lodsd                          ;

        cmp     ax,'EP'                ;same thing if this is not true
        jne     Not95                  ;something is wrong

;found the PE header
;        sub     esi,4           ;back up si to the PE header
;                                ;        

        mov     [ebp + kern1],eSI ;save the address of the PE header
                                  ; no reason thouht i might need it      

        add     esi,74h         ;should be 78h but the last lodsw
        lodsd                   ;should be rva to the .edata

        mov     ebx, dword ptr [kern]

        add     eax,ebx                 ;should be the .edata area
                                        ;offset to the to name of dll
        add     eax,10h                 ;
        mov     esi,eax                 ;get us to the base
                                        ;
; ok found the .export area now get the info we need to find
; the address

        lodsd
        mov     [ebp + baseF],eax       ;this is the starting ordinal that
                                        ;that all must be added to
                                        ;usualy 1 for kernel32

        lodsd                           ;total number of exported functions
                                        ;by name and ordinal

        lodsd
        mov     [ebp + limit],eax       ;how many functions are exported
                                        ;by name this is the limit of how
                                        ;many we will search for        

;the next three are all RVA's to the
;fields so we need to add the offset
;them
        lodsd                           ;this is the address to
        add     eax,dword ptr [kern]    ;an array of pointers to the
        mov     [ebp + AddFunc],eax     ;entry point rva's of each 

        lodsd                           ;this is the RVA to ascii names
        add     eax,dword ptr [kern]    ;of each named exported fuction
        mov     [ebp + AddName],eax     ;

        lodsd                           ; rva points to  array of words 
        add     eax,dword ptr [kern]    ;which are the export ordinals
        mov     [ebp + AddOrd],eax      ; - the base

;ok we have everthing we need to go ahead and locate the address's of the
;KERNEL32.dll functions

        mov     ebx,[ebp + Looking]     ;get address of the first name



LookLoop:
        mov     esi,[ebp + AddName]     ;get the first rva pointer to a name
        mov     [ebp + Nindex],esi      ;

        mov     edi,[esi]               ;now make it a true pointer to the
        add     edi,[kern]              ;name by adding the offset

        mov     ecx,0                   ;sets the counter to 0
 

TryAgain:
        mov     esi,ebx                 ;what function we are looking for
                                        ;now simple cmp strs

                  
        test    byte ptr [ebx], 00110000b    ; check if we have a ordinal  
        jne      UseOrdinal                  ;instead of a String here


MatchByte:
        cmpsb
        jne      NextOne                ;not it try next nameof fucntion

        cmp     byte ptr [edi],0        ;if equal to 0 we found a match
        je      GotIt                   ;

        jmp     MatchByte               ;nope try next byte

NextOne:
       inc      cx
       cmp      cx,[ebp + limit]
       jge      not_found

       add      dword ptr [ebp + Nindex],4      ;get the next pointer rva
       mov      esi,[ebp + Nindex]              ;and try again
       mov      edi,[esi]                       ;
       add      edi,[kern]                      ;
       jmp       TryAgain                       ;

;---------------------------------------------------------------------
GotIt:
;note if not a match is found the all other from then on in are blank
;in other words dont mispell ,or ask for for a function that is not in
; KERNEL32
;esi = the 0 at the end of the name of the strings given to us to look for
;
;cx = the index into the AddOrd
;take Nindex * 4 + [AddOrd] = Ordinal
;Ordinal * 4 + [AddFunc] should be the rva to the fuction

        mov     ebx,esi         ;get set for next search routine
        inc     ebx             ;

        shl     ecx,1           ;*2 looking into a word array
        mov     Esi,[ebp + AddOrd]
        add     Esi,ecx
        xor     eax,eax
        mov     ax,word ptr [esi]

        jmp     GotOrdinal
;here ax equals the ordinal - the base
;if ordinal is passed to hear then we should be able to skip
;searching for a name and hit here
;not sure of course but it tested on a few that I tried
;

UseOrdinal:

           mov  al, byte ptr [ebx + 2]         ;I do not check for
           cbw                                 ;bad ordinals so this
           cwde                                ;may return wrong anwsers
           inc ebx                             ;if wrong ordinal sent
           inc ebx
           inc ebx
           

GotOrdinal:
        shl     eax,2                ;*4 looking into a dword array
        mov     esi,[ebp + AddFunc]
        add     Esi,eax

        mov     Edi,dword ptr [esi]
        add     Edi,[kern]

        push    ebp

        mov     esi, [ebp + Where]
        mov     ebp,esi
        mov     [ebp ],edi

        pop     ebp
        add     dword ptr [ebp + Where],4

        cmp     byte ptr [ebx],0bh
        jne     LookLoop

        jmp     over_error
        
Not95:
        xor     esi,esi                 ;if the header of the kernel
                                        ;is not there forget it, its not
                                        ; the Win95 we know and love ;)        

not_found:                              ; String pass to us is not a 
        xor     edi,edi                 ; valid fucntion name

over_error:
                
        ADD     ESP,worksp
        pop     ebp

        ret
;----------------------------------------------------------------------------


         end     HOST


