flat assembler
Message board for the users of flat assembler.

Index > Macroinstructions > Macro for runtime lincing (at load time ;) )

Author
Thread Post new topic Reply to topic
S.T.A.S.



Joined: 09 Jan 2004
Posts: 173
Location: Ru#27
S.T.A.S. 10 Feb 2004, 04:35
Hi all
I have some macros to replace library / include ones.

It allows code like this:
Code:
usedll    user32
some code
.....
usedll  user32, gdi32
some code
.....
    

It could be useful, if you use many modules in the programm, that defines librarys more than once.
( no need to use library / include )

Built by my macro import table is different from others. It has just one reference to imported function (GetProcAddress) in PE import section.
All others dlls are loaded and linked when programm starts automatically.

The kernel32.dll base address is searching not through ESP reg, but in "legal" way, so I hope it should work without any problem at all.

I wrote some comments in the source, so hope it would be clean.
Still, I'm new to FASM and think there are lots of things to improve in there..

NOTE: the paths to "APIA" and "EQUATES" includes are hardcoded in the source (I not use environment variable), so you should change them.

Also, not forget to put "BuildLockupTable" macro at the end of the prog, to reserve space for lockup table and define labels.

Have fun!


Code:
;;===========================================================================
;;  This include is to make alternative import table and create imports at runtime

;;  "usedll dllname1,dllname2.." - adds dll(s) to dlls list. they aren't added twice.
;;  use this macro to build UsedDlls list for "importdll" macro

;;  "entry" is re-defined to auto-generate code that builds import table at rumtime
;;  if "entry" is used w/o parameters it is equivalent to "entry $"

;;  "BuildLockupTable" - must be placed at the end of the programm 
;;                            to reserve space and create labels for lockup table

;;  S.T.A.S.
;;===========================================================================

display '***** Building Import Table..' %%L


;;===========================================================================
;;  re-assign entry point to __ImportLibraries proc and build some data
macro      entry old_EP
 { __importdll     __UsedDlls      ;;  this macro builds dll's names list ("import table")
  entry __RTImportLibraries       ;;  define OEP to \
        __RTImportLibraries                     ;;  <-- this (macro) porc that does the job
      display '***** OEP ',<__RTImportLibraries %%A,'   Programm start ', <..ENTRY_POINT %%A %%L
        if      ~ old_EP eq
         ..ENTRY_POINT = old_EP  ;;  *real* start point
      else
                ..ENTRY_POINT = $               ;;  if no entry point defined, use current address
          display '  Entry point is not defined as a label, used $' %%L  
   end     if }

data           import
;;  Import Directory starts here
align 4
IMPORT_DIRECTORY_TABLE:
                                dd      0, 0, 0, RVA @f, RVA GetProcAddress$$   ;;  Note: proc is renamed
                               dd      0, 0, 0, 0, 0                   ;;  end of directiry
@@:                         db      'kernel32.dll',0              ;;  only one dll
@@:                             db      0,0, 'GetProcAddress',0       ;;  only one name is to be imported
align 4
GetProcAddress$$      dd      RVA @b, 0       ;;  we need just one function to be imported when loading
end                data

;;===========================================================================
;; build the Lockup Table at the runtime
macro  __RTImportLibraries     {
__RTImportLibraries:
          mov             EBX,    [GetProcAddress$$]      ;; this function IS inside the kernel32.dll
         mov             EBP, EBX
@@:             xor             BX,     BX                              ;; dlls are loaded at addresses XXXX0000h, so align
         cmp             word    [EBX], 'MZ'           ;; check first 2 bytes of PE
                jz              @f                                      ;; Kernel32.dll found
               dec     EBX                                     ;; search backward. Now -1, ^^ align by 10000h
              jmp             @b
;; here we have got handle to module kernel32.dll in EBX. Really, I like word "address"
@@:              cld
         mov             EDI, IMPORTED_NAMES
         mov             ESI, LOCKUP_TABLE
@@:            stdcall EBP, EBX, EDI                   ;;  invoke GetProcAddress
           mov             [ESI], EAX                      ;;  store it in Locup Table
         add             ESI, 4
;;  search next imported name
.s:              xor             Al, AL
              mov             ECX, EDI                ;;  well, I'm sure strings won't be so big
                repnz   scasb           ;;  find next string
                cmp             [EDI], AL
           js              ..ENTRY_POINT   ;;  end of table found, start the programm
          jnz             @b                      ;;  proc name found
;;  if we are here, new dll name is found
            inc             EDI
         invoke  LoadLibrary, EDI
            mov             EBX, EAX                ;;  new dll base address
            jmp             .s      }


;;===========================================================================
;;  add one dll to dll's list without checking
macro     __adddll        dllname
 {      __UsedDlls      fix __UsedDlls, dllname
     dllname=0       }  ;;  reset counter of definitions

;;===========================================================================
;;  this macro creates the list of used dlls. they are not included more than once
macro    usedll [dllname] 
 {    ifdef   dllname#used, ,__adddll dllname
     define  dllname#used            ;;  we don't nedd to use import twice
      dllname = dllname+1     }          ;;  increase counter of definitions

;;  this dll is always used
usedll        kernel32

;;===========================================================================
;;  build "import table" for standard dlls' procs. kernel32 must be the first!!
;; (may be used like: "__importdll ,kernel32,user32,gdi32" - at the beginning of the programm)
macro    __importdll     dummy,[dllname] ;;  first arg is empty
 {       forward
     display '  '#`dllname#'.dll .....',09h
  local   u
   if      `dllname eq 'kernel32'                ;;  no need to store this name
IMPORTED_NAMES:                                   ;;  create label instead
    else
                db      0,`dllname,'.dll',0           ;;  first zero byte is marker of dll name
   end     if
  u = $
       include '..\_Inc\equates\'#`dllname#'.inc'       ;;  include some equates
    include '..\_Inc\apia\'#`dllname#'.inc'  ;;  add procs names to table
        if      u > $-5              ;;  check, how many bytes were created
              display 'WARNING!!  defined '
             display <dllname %%D
             display ' time(s), but never used.' %%L
   else
                display 'OK  ::  defined '
                display <dllname %%D
             display ' time(s) ' %%L
   end     if
  common
      db      0FFh            ;;  marker of the end of inmport table
      display '  imported names take '      
    display <($-IMPORTED_NAMES) %%D
  display ' bytes' %%L  }

;;===========================================================================
;;  imports names from a dll to "import table". used by importdll / dll.inc
macro   import  dummy,[iproc,iproc_name]        ;; ignore first arg
 {  if      used iproc
          db      iproc_name,0    ;;  store dll's proc name string
   end if
      ImportedProcs   fix     ImportedProcs, iproc    }  ;;  create a list of procs

;;===========================================================================
;; this macro must be at the end of programm
macro        BuildLockupTable { __BuildLockupTable ImportedProcs }
macro        __BuildLockupTable dummy,[iproc]        ;; first arg is always empty
 { common
      align   4
LOCKUP_TABLE:
      forward
             if      used iproc
iproc             rd      1
           end     if
  common
              display 'Lockup table created: '
              display <(($-LOCKUP_TABLE)/4) %%D
                display ' element(s), '
           display <($-LOCKUP_TABLE) %%D
            display ' bytes ', %%L        }

;;  EOF
;;===========================================================================
    


Last edited by S.T.A.S. on 10 Feb 2004, 04:40; edited 1 time in total
Post 10 Feb 2004, 04:35
View user's profile Send private message Reply with quote
S.T.A.S.



Joined: 09 Jan 2004
Posts: 173
Location: Ru#27
S.T.A.S. 10 Feb 2004, 04:38
Oh, yes.. there is some modifiyed Privalov's "display" macro.
It shows some info when above macro are compiling

Code:
;;===========================================================================
;;  extended display macro

;;  use: display 'a',b,<c,d> ..
;;  'a'-text,       b-text label,   c-number, d-radix.
;;
%%L fix     ,0Dh,0Ah                ;;  next line
%%H    fix     ,10h>                ;;  hex number
%%D   fix     ,10>                 ;;  decimal number
%%R       fix     ,'RVA'>            ;;  offet hex value
%%A      fix     ,'address'>        ;;  address hex value (based on EXE_IMAGE_BASE)

macro    __digit num
 {  if      num < 10
         display '0'+num
   else
                display 'A'+num-10
        end if }

macro      __display arg1,arg2
 {if        ~ arg1 eq               ;;  fix "2 commas" problem
        if      arg2 eq
             display arg1
        else
                local   ..tmp, ..radix
              if      arg2 eq 'RVA'
                     display 'RVA: '
                       ..tmp = RVA arg1
                    ..radix = 10h   
            else    if      arg2 eq 'address'
                 display 'address: '
                   ..tmp = RVA arg1 + EXE_IMAGE_BASE
                   ..radix = 10h                   
            else
                        ..tmp = arg1
                        ..radix = arg2
              end     if
          virtual at 0
                        repeat  32
                          if      ..tmp > 0
                                        db      ..tmp mod ..radix
                                   ..tmp = ..tmp / ..radix
                             end if
                      end     repeat
                      repeat  $
                           load    ..tmp byte from $-%
                         __digit ..tmp
                       end     repeat
                      if $ = 0
                            display '0'
                       end     if
          end     virtual
             if      ..radix = 10h
                       display 'h'
               end     if
  end     if
 end      if      }

macro     display [arg]   { __display arg } 
;;===========================================================================    
Post 10 Feb 2004, 04:38
View user's profile Send private message Reply with quote
JohnFound



Joined: 16 Jun 2003
Posts: 3499
Location: Bulgaria
JohnFound 10 Feb 2004, 05:11
Hi S.T.A.S.
What actually is the advantage of using so complex approach? I am using standard "library" and "import" macroses from FASM package. They check whether the function is used and insert it in import table only if it is used, so I can simply include all files from include/apia/ directory and stop bothering any longer about imports. Some time ago, I make some extended tests to determine how this approach will slow compilation and I found out that additional compilation time is between 1 and 2 seconds on my office computer - 400MHz PII laptop. So I decide that this is absolutely acceptable time and stop to make future atempts to improve import functions of FASM.

Regards
Post 10 Feb 2004, 05:11
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
S.T.A.S.



Joined: 09 Jan 2004
Posts: 173
Location: Ru#27
S.T.A.S. 10 Feb 2004, 06:23
Hi JohnFound
The "advantage" (if it is so) is - different import mode. Usually, PE is linked to dlls by OS loader, but in this case we have only one proc in import table. Names of all others are in some 'array' and used *after* program starts. This could be useful in some cases. For example in some packers, etc.
As a side effect we usually have a bit smaller exe.
Also IMHO it's always good to have an alternative.

BTW, this will be "so complex approach" with MASM.
In FASM it's absolutly real Wink
Post 10 Feb 2004, 06:23
View user's profile Send private message Reply with quote
JohnFound



Joined: 16 Jun 2003
Posts: 3499
Location: Bulgaria
JohnFound 10 Feb 2004, 06:50
Yea, from this point of view I am absolutely agree. Smile
Post 10 Feb 2004, 06:50
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.