flat assembler
Message board for the users of flat assembler.

Index > Macroinstructions > Alternative approaches to PE imports

Author
Thread Post new topic Reply to topic
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 7421
Location: Kraków, Poland
Tomasz Grysztar
I just though someone might find this useful (or interesting at least): a set of macros to call Win32 API and generate imports in a bit different way.

The definitions:
Code:
libraries@import equ kernel32
procs@kernel32@import equ ExitProcess

macro call param
{
  define target@call param
  match library:proc,param
  \{
     match libs,libraries@import
     \\{
         define libraries@import
         match x =library x , <libs>
         \\\{ restore libraries@import \\\}
         match ,libraries@import
         \\\{
              define libraries@import libs,library
              define procs@\#library\#@import proc
         \\\}
     \\}
     match procs,procs@\#library\#@import
     \\{
         match =procs@\#library\#@import,procs@\#library\#@import \\\{ define procs@\#library\#@import proc \\\}
         define procs@\#library\#@import procs,proc
         match x =proc x , <procs>
         \\\{ restore procs@\#library\#@import \\\}
     \\}
     define target@call [library\#.\#proc]
  \}
  call target@call
}

macro imports
{
  data import
   match libs,libraries@import
   \{
      irp name,libs
      \\{
          \\forward
           \\local _label
           dd RVA name\\#.lookup,0,0,RVA _label,RVA name\\#.address
          \\common
           dd 0,0,0,0,0
          \\forward
           _label db \\`name\\#'.dll',0
                  rb RVA $ and 1
      \\}
      irp name,libs
      \\{
          match procs,procs@\\#name\\#@import
          \\\{
               irp proc,procs
               \\\\{
                     \\\\common
                      name\\#.lookup:
                     \\\\forward
                      \\\\local _label
                      dd RVA _label
                     \\\\common
                      dd 0
                      name\\#.address:
                     \\\\forward
                      name\\#.\\\\#proc dd RVA _label
                     \\\\common
                      dd 0
                     \\\\forward
                      _label dw 0
                             db \\\\`proc,0
                             rb RVA $ and 1
               \\\\}
          \\\}
      \\}
   \}
  end data
}

macro stdcall proc,[arg]
{
  reverse pushd arg
  common call proc
}    

The program:
Code:
format PE GUI
entry start

section '.code' code readable executable

  start:

        stdcall user32:MessageBoxA,0,_message,_caption,0

        stdcall kernel32:ExitProcess,0

section '.data' data readable writeable

  _caption db 'Win32 assembly program',0
  _message db 'Hello World!',0

section '.idata' data readable writeable

  imports    

(no includes other than the above definitions are needed).


Last edited by Tomasz Grysztar on 07 Jul 2006, 08:03; edited 1 time in total
Post 06 Jul 2006, 21:27
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 16858
Location: In your JS exploiting you and your system
revolution
I once did something similar to this, but it caused two main problems. 1) For larger programs the compilation time is too long because of the "call" macro, and 2) The memory usage is very large because of the redefining of labels and the extra macro expansion. I think it is suitable only for smaller programs.
Post 06 Jul 2006, 23:08
View user's profile Send private message Visit poster's website Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7106
Location: Slovakia
vid
it's nice, but not for real projects, if it wouldn't be you, i would start explaining why such thing shouldn't be used Wink
Post 07 Jul 2006, 05:05
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 7421
Location: Kraków, Poland
Tomasz Grysztar
Yeah, it eats very much of memory, but the compilation time is not that bad - perhaps because you get out of memory much earlier than it gets really slow Wink With triple calls to all the standard imports from kernel32 and user32 (there's about 500 functions in each) it still got not that bad compilation time (about of current timing for compiling Fresh project), but consumed terrible amount of 150 MB of RAM.

Now the another alternative I planned to provide here, the modification of standard import macro to support direct-call-to-indirect-jump method for invoking APIs. It would be harder to make it separate all the jumps from imports, but this way it doesn't seem to create any problems anyway:
Code:
macro import name,[label,string]
 { common
    name#.lookup:
   forward
    if used label
     if string eqtype ''
      local _label
      dd RVA _label
     else
      dd 80000000h + string
     end if
    end if
   common
    if $ > name#.lookup
     name#.redundant = 0
     dd 0
    else
     name#.redundant = 1
    end if
    name#.address:
   forward
    if used label
     if string eqtype ''
      __import.#label dd RVA _label
     else
      __import.#label dd 80000000h + string
     end if
    end if
   common
    if ~ name#.redundant
     dd 0
    end if
   forward
    if used label & string eqtype ''
    _label dw 0
           db string,0
    label: jmp [__import.#label]
           rb RVA $ and 1
    end if }    

Note that you have to change every "invoke" to "stdcall" on switching to this method!
Post 07 Jul 2006, 08:01
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 7421
Location: Kraków, Poland
Tomasz Grysztar
PS. If you've made some own macros, why not share them? Wink Even if they are inefficient, they still may be interesting.
Post 07 Jul 2006, 08:45
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 16858
Location: In your JS exploiting you and your system
revolution
Quote:
direct-call-to-indirect-jump method
Is there an advantage to using this method? It seems there is an extra layer of instructions that need to be executed. I can see that the call is one byte shorter but that sort of saving is not normally noticable even for heavily optimised code.
Post 07 Jul 2006, 08:49
View user's profile Send private message Visit poster's website Reply with quote
comrade



Joined: 16 Jun 2003
Posts: 1137
Location: Russian Federation
comrade
If you call the the API very often, the direct-call-to-indirect-jump method will result in smaller code: 6 bytes for the indirect-jump, and 5 bytes for each indirect call, VERSUS 6 bytes for each regular FF15 call.

That's how MS import libraries are made - if you check MASM/MSLINK generated code, they always have that level of indirection.
Post 07 Jul 2006, 09:02
View user's profile Send private message Visit poster's website AIM Address Yahoo Messenger MSN Messenger ICQ Number Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 7421
Location: Kraków, Poland
Tomasz Grysztar
Perhaps the best would be to automatically generate either of them depending on whether the given API is called more than 6 times or not.
Post 07 Jul 2006, 09:06
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 16858
Location: In your JS exploiting you and your system
revolution
Code size is rarely an issue due to the caches. However because this is calling an API it is unlikely that any optimisation is being applied to that particular section of code. It seems much clearer to me in the debugger to use the direct "call [API]". I never did like the MS way of call-jmp [API].
Post 07 Jul 2006, 11:08
View user's profile Send private message Visit poster's website Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7106
Location: Slovakia
vid
Tomasz Grysztar wrote:
Perhaps the best would be to automatically generate either of them depending on whether the given API is called more than 6 times or not.

how would you make it? Force "invoke" macro and disallow usage of "call [abcd], or how?
Post 07 Jul 2006, 15:55
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 7421
Location: Kraków, Poland
Tomasz Grysztar
Rather redefine "call" and use "stdcall" everywhere.
Post 07 Jul 2006, 16:18
View user's profile Send private message Visit poster's website Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7106
Location: Slovakia
vid
redefining instructions is not good idea at all. when you write "call [something]", you await call instruction, not jump
Post 07 Jul 2006, 16:41
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 7421
Location: Kraków, Poland
Tomasz Grysztar
It would still be a call instruction, only direct instead of indirect.

As for redefining instructions - for some of us it may be not a good idea, some other may like it. The purpose for which I started this thread is to provide some alternative solutions for those that would prefer to go with something other than the standards.
Post 07 Jul 2006, 16:58
View user's profile Send private message Visit poster's website Reply with quote
Joshua



Joined: 12 Jul 2003
Posts: 56
Location: Belgium
Joshua
Code:
; IMPORT_NO_IAT = 0
;
; imp library*,[function*]
; imp.insert

macro imp library*,[function*] {
 common
  match =Import.#library#.Functions,Import.#library#.Functions \{ 
   match a,Import.Libraries \\{ define Import.Libraries a,<library> \\}
   match ,Import.Libraries \\{ define Import.Libraries <library> \\}
   include "%fasminc%\imphinta\"#`library#".inc"
   ;you can comment this line if you want, it's used to include the hints for the calls
  \}
 forward
  match a b c d,function,,, \{
   match e,Import.#library#.Functions \\{
    match =, =, =,,b c d \\\{ define Import.#library#.Functions e,<a>,<`function> \\\}
    match =as =, =, =,,b d \\\{ define Import.#library#.Functions e,<a>,<c> \\\}
   \\}
  \}
}

macro imp.insert { 
 match a,Import.Libraries \{ IMPORT.BUILD.TABLE a \}
 Import.Libraries equ
}

macro IMPORT.BUILD.TABLE [library] {
 common
  local i
  if defined i
   rb 3-(rva $+3) mod 4
   if ~defined IMPORT_NO_IAT
    data 12
     forward
      local u
      if defined u
       label library dword
       label library#.Iat dword
       match a,Import.#library#.Functions \{ IMPORT.FUNCTION.IAT library,a \}
       if $ > library#.Iat
        dd 0
       end if
      end if
     common
    end data
   end if
   data import
    forward
     if defined u
      dd RVA library#.Offset,0,0,RVA library#.Data,RVA library
     end if
    common
     dd 0,0,0,0,0
   end data
  end if
 forward
  local d,e
  if (~defined library#.Offset | defined d)
   if defined IMPORT_NO_IAT & defined u
    label library dword
   end if
   label library#.Offset dword
   match a,Import.#library#.Functions \{ IMPORT.FUNCTION.OFFSET library,a \}
   if $ > library#.Offset
    if ~defined i | defined e
     i = 0
     e = 0
    end if
    u = 0
    dd 0
   end if
   d = 0
  end if
 forward
  if defined u
   library#.Data db `library,0
   rb rva $ mod 2
   match a,Import.#library#.Functions \{ IMPORT.FUNCTION.DATA library,a \}
  end if
}

macro IMPORT.FUNCTION.IAT library*,dummy*,[function*,alias*] {
 forward
  local d
  if used function & (~defined function#.Iat | defined d)
   label function dword
   if alias eqtype ""
    function#.Iat dd RVA function#.Data
   else
    function#.Iat dd 80000000h+alias
   end if
   d = 0
  end if
}

macro IMPORT.FUNCTION.OFFSET library*,dummy*,[function*,alias*] {
 forward
  local d
  if used function & (~defined function#.Offset | defined d)
   if defined IMPORT_NO_IAT
    label function dword
   end if
   if alias eqtype ""
    function#.Offset dd RVA function#.Data
   else
    function#.Offset dd 80000000h+alias
   end if
   d = 0
  end if
}

macro IMPORT.FUNCTION.DATA library*,dummy*,[function*,alias*] {
 forward
  local d
  if used function & alias eqtype "" & (~defined function#.Data | defined d)
   label function#.Data byte
   if defined imphint_#function
    dw imphint_#function
   else
    dw 0
   end if
   db alias,0
   rb rva $ mod 2
   d = 0
  end if
}

Import.Libraries equ    
Made to be able to create more modular include files.
Also automaticly creates IAT, you can define IMPORT_NO_IAT to cancel this.
Example:
Code:
in file1:
    imp KERNEL32.DLL \
        ,ExitProcess \
        ,GetCurrentThreadId \

    imp USER32.DLL \
        ,AttachThreadInput \
        ,ClientToScreen \
        ,EnumChildWindows \
        ,FindWindowEx as "FindWindowExA" \
        ,GetAsyncKeyState \

in file2:
    imp KERNEL32.DLL \
        ,ExitProcess \ ;Even tho this is defined twice, it will cause no problems
        ,GetModuleFileName as "GetModuleFileNameA" \
        ,GetPrivateProfileInt as "GetPrivateProfileIntA" \
        ,Sleep \
    
in Main file:
    imp.insert
    
Post 09 Jul 2006, 18:56
View user's profile Send private message Reply with quote
Ancient One



Joined: 28 Feb 2005
Posts: 55
Ancient One
this is my personal import macro i write sometimes ago.. (read the comment in source)
Code:
;USAGE
;to import items from a dll, use
;
;  import <dllRefName>['('<dllString>')'], <importItemName>[':'<importItemStringOrOrdinal>] [,<importItemName>[':'<importItemStringOrOrdinal>]]*
;
;to declare the import table, use
;
;  .import ;no new section
;
;or
;
;  @import ;new section
;
;or
;
;  @import '('<sectionName>')' ;new section with name
;
;NOTE : '.import' MUST BE USE AFTER ALL 'import'
;
;to reference imported item, use the importItemName, e.g
;
;  format pe gui 4.0
;  import user32('user32.dll'), MessageBox:'MessageBoxA'
;
;  main:
;  push  0 mbTitle mbMessage 0x40
;  call  [MessageBox]
;  ret
;
;  mbMessage db 'Hello World from Fasm', 0
;  mbTitle db 'Test', 0
;
;  .import
;
;lastly, can use 'api' (MUST BE USED BEFORE .import) to import function and call them, e.g
;
;  api u32('user32.dll')::MessageBox('MessageBoxA')
;
;next call to user32.dll function can just use u32
;
; api u32::MessageBoxW
;

macro importListCreate [@listName*] {
  match = @listName#created, @listName#created \{
    define @listName#created
    define @listName
  \}
}

macro importListAdd @listName, [@item] {
common
  ;display `@listName,13,10
  match .item, @item \{
    match , @listName#created \\{
      match , @listName \\\{
        restore @listName
        define @listName .item
        define ..do
      \\\}
      match =..do .prev, ..do @listName \\\{
        restore @listName
        define @listName .prev, .item
      \\\}
      restore ..do
    \\}
  \}
}

macro importListDestroy [@listName*] {match, @listName#created \{restore @listName, @listName#created\}}

importListCreate __dllList
macro importItems @dllRefName, [@itemSpec] {
common
  ;display `@dllRefName, '-'
forward
  ;display `@itemSpec
  match .itemRefName:.itemStr, @itemSpec \{
    match =.itemRefName\#Exist, .itemRefName\#Exist \\{
      define .itemRefName\#Exist
      importListAdd @dllRefName#itemList, .itemRefName, .itemStr
    \\}
    define do
  \}
  match =do .itemRefName, do @itemSpec \{
    match =.itemRefName\#Exist, .itemRefName\#Exist \\{
      define .itemRefName\#Exist
      importListAdd @dllRefName#itemList, .itemRefName, \`.itemRefName
    \\}
  \}
  restore do
  ;display 13,10
}

macro import @dllSpec, [@itemSpec] {
common local dllRefName
  match .dllRefName(.dllStr), @dllSpec \{
    define .dllRefName\#Str .dllStr
    define dllRefName .dllRefName
    define do
  \}
  match =do .dllRefName, do @dllSpec \{
    define .dllRefName\#Str \`.dllRefName
    define dllRefName .dllRefName
  \}
  restore do
  match .dllRefName, dllRefName \{
    match ,.dllRefName\#Exist \\{
      restore .dllRefName\#Str
      importItems .dllRefName, @itemSpec
    \\}
    match =.dllRefName\#Exist,.dllRefName\#Exist \\{
      define .dllRefName\#Exist
      importListAdd __dllList, .dllRefName
      importListCreate .dllRefName\#itemList
      importItems .dllRefName, @itemSpec
    \\}
  \}
  restore dllRefName
}

macro @import @nameSpec {
  match (.name), @nameSpec \{
    section .name data readable
    .import
  \}
  match , @nameSpec \{
    section '.idata' data readable
    .import
  \}
  restore do
}

macro .import {
  match .dllList,__dllList \{buildImportTable .dllList\}
  importListDestroy __dllList
}

macro buildImportTable [@dllRefName] {
common local ..hasImport, ..dllCount
  ..dllCount=0
  align 4
  if ..hasImport
    data import
  end if
forward
      if defined @dllRefName#Used
        dd rva @dllRefName#Oft, 0, 0, rva @dllRefName#Name, rva @dllRefName#Ft
        ..dllCount=..dllCount+1
      end if
common
      if ..dllCount
        dd 5 dup 0
      end if
  if ..hasImport
    end data
  end if
  ..hasImport = ..dllCount
forward
  match .listName, @dllRefName#itemList \{
    match .itemList, .listName \\{buildImportData @dllRefName, .itemList\\}\}
  if defined @dllRefName#Used
    @dllRefName#Name db @dllRefName#Str, 0
  end if
common
  restore @dllRefName#Str, @dllRefName#Exist
  importListDestroy @dllRefName#itemList,  __dllList
}

macro buildImportData @dllRefName, [@itemRefName, @itemStr] {
common local ..itemCount
  ..itemCount=0
  align 4
  @dllRefName#Ft:
forward
  ;display `@itemRefName
  if used @itemRefName
    ;display 'foo'
    if @itemStr eqtype ''
local ..label
      @itemRefName dd rva ..label
    else
      @itemRefName dd 0x80000000 or @itemStr
    end if
    ..itemCount = ..itemCount+1
  end if
common local ..temp
  if $ > @dllRefName#Ft
    @dllRefName#Used=1
    dd 0
    ..itemCount = ..itemCount+1
  end if
  @dllRefName#Oft:
  dd ..itemCount dup 0
  repeat ..itemCount
    load ..temp dword from @dllRefName#Ft+(%-1)*4
    store dword ..temp at @dllRefName#Oft+(%-1)*4
  end repeat
forward
  if used @itemRefName & @itemStr eqtype ''
    ..label dw 0
            db @itemStr, 0
            align 2
  end if
  restore @dllRefName#@itemRefName#Exist
}

macro api @funcSpec, [@arg] {
common
  match .any, @arg \{
reverse
    pushd @arg
common
  \}
  match .dllSpec::.funcSpec, @funcSpec \{
    import .dllSpec, .funcSpec
    match .func:.ignore, .funcSpec \\{
      call [.func]
      define .do
    \\}
    match =.do .func, .do .funcSpec \\{call [.func]\\}
    restore .do
    define do
  \}
  match =do .funcName, do @funcSpec \{call [.funcName]\}
  restore do
}
    
Post 14 Jul 2006, 07:42
View user's profile Send private message MSN Messenger 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-2019, Tomasz Grysztar.

Powered by rwasa.