invoke macro simulator - updated

Started by Vortex, October 24, 2006, 07:50:29 PM

Previous topic - Next topic

Vortex

Here is an example of simulating the invoke statement, it demonstrates the power of MASM's macro engine.

.386
.model flat, stdcall
option casemap :none

include \GeneSys\include\windows.inc
include \GeneSys\include\kernel32.inc
include \GeneSys\include\msvcrt.inc
include invoke.inc

includelib \GeneSys\lib\kernel32.lib
includelib \GeneSys\lib\msvcrt.lib

.data
message db "Hello world!",0
template db "%s",0

.code

start:

cinvoke _strupr,ADDR message
cinvoke printf,ADDR template,eax
_invoke ExitProcess,0

END start


invoke.inc :


_invoke MACRO funcname:REQ,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20
LOCAL pos

    FOR arg,<p20,p19,p18,p17,p16,p15,p14,p13,p12,p11,p10,p9,p8,p7,p6,p5,p4,p3,p2,p1>

        IFNB <arg>

             pos=@InStr(1,arg,<ADDR>) OR @InStr(1,arg,<addr>) OR @InStr(1,arg,<Addr>)

             IF pos

                IF (OPATTR(@SubStr(arg,%pos+5))) EQ 98
                        lea eax,@SubStr(<arg>,%pos+5)
                        push eax
                ELSE
                        push OFFSET @SubStr(<arg>,%pos+5)
                ENDIF

             ELSE
                        push arg
             ENDIF
        ENDIF
    ENDM
call funcname
ENDM

cinvoke MACRO funcname:REQ,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20
LOCAL pos,counter
counter=0

    FOR arg,<p20,p19,p18,p17,p16,p15,p14,p13,p12,p11,p10,p9,p8,p7,p6,p5,p4,p3,p2,p1>

        IFNB <arg>

             counter=counter+4
             pos=@InStr(1,arg,<ADDR>) OR @InStr(1,arg,<addr>) OR @InStr(1,arg,<Addr>)

             IF pos

                IF (OPATTR(@SubStr(arg,%pos+5))) EQ 98
                        lea eax,@SubStr(<arg>,%pos+5)
                        push eax
                ELSE
                        push OFFSET @SubStr(<arg>,%pos+5)
                ENDIF

             ELSE
                        push arg
             ENDIF
        ENDIF
    ENDM
call funcname

IF counter NE 0
    add esp,counter
ENDIF

ENDM

[attachment deleted by admin]

PBrennick

#1
Here is another one, this one allows the user to use the exact same format as invoke, we have included this in our macro set.


.386
.model flat, stdcall
option casemap :none

include \GeneSys\include\windows.inc
include \GeneSys\include\kernel32.inc
include \GeneSys\include\msvcrt.inc

include \GeneSys\macros\macros.asm

includelib \GeneSys\lib\kernel32.lib
includelib \GeneSys\lib\msvcrt.lib

.data
message db "Hello world!",0

.code

start:

CallIt  printf, offset message, eax
Add     esp, 8
CallIt  ExitProcess, 0

END start



CallIt MACRO procedure, parameters:VARARG
  Local param, reversed

  reversed TEXTEQU <>
  %For param, <parameters>
    reversed CATSTR <param>, <!,>, reversed
  EndM
  %For param, <reversed>
    push param
  EndM
  call procedure
ENDM




[attachment deleted by admin]
The GeneSys Project is available from:
The Repository or My crappy website

Vortex

Now, the functionalities of both macros are united into one :

.code

start:

_invoke _strupr,ADDR message
_invoke printf,ADDR template,eax
_invoke ExitProcess,0

END start


invoke.inc :

_invoke MACRO funcname:REQ,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20
LOCAL pos,counter
counter=0

    FOR arg,<p20,p19,p18,p17,p16,p15,p14,p13,p12,p11,p10,p9,p8,p7,p6,p5,p4,p3,p2,p1>

        IFNB <arg>

             counter=counter+4
             pos=@InStr(1,arg,<ADDR>) OR @InStr(1,arg,<addr>) OR @InStr(1,arg,<Addr>)

             IF pos

                IF (OPATTR(@SubStr(arg,%pos+5))) EQ 98
                        lea eax,@SubStr(<arg>,%pos+5)
                        push eax
                ELSE
                        push OFFSET @SubStr(<arg>,%pos+5)
                ENDIF

             ELSE
                        push arg
             ENDIF
        ENDIF
    ENDM
call funcname

IF (OPATTR(funcname)) EQ 421
   IF counter NE 0
      add esp,counter
   ENDIF
ENDIF

ENDM

[attachment deleted by admin]

Vortex

Hi Paul,

printf is a C function, so your line :

CallIt  printf, offset message, eax

requires a manual stack balancing :

add esp,8

PBrennick

Thanks,
I hate that stack balancing requirement required by C functions. I am not a wizard at using C stuff like you are so if this is a silly question, please understand. How come these C functions do not balance the stack themselves. I cannot ever remember creating anything that did not do its own clean up when done. I have not coded in C in over 20 years so I cannot remember if this was always an issue. I know that printf, for example comes from C and was first created in the '80s.

Paul
The GeneSys Project is available from:
The Repository or My crappy website

hutch--

Paul,

It is because the C calling convention handles a variable number of parameters which cannot be determined within the proc so it is designed for the caller to balance the stack rather than automatically doing it like STDCALL where the stack arg size is known.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

PBrennick

Hutch,
Thank you for that reply, it is helpful but also leads into my next question. Do you think I could count parameters. If so, perhaps I could modify CallIt to count parameters, multipy by 4 and do a add esp,eax. How does that look to you? I know that you are a macro guru so I definitely value your input. The only bottleneck is the macro would have to know if the call is to a C function. That might not be possible.  :'(

Actually, perhaps it is better for me to write my own preprocessor for any C function that I happen to use, but I would rather know if there is some way of knowing from the macro. It would be the perfect solution.

Paul
The GeneSys Project is available from:
The Repository or My crappy website

Vortex

Hi Paul,

Check my latest version of invoke macro simulator above. ( invk_simul2.zip )

These two lines are keeping track of the number of parameters passed to the macro :

IFNB <arg>

             counter=counter+4
.
.


The code portion to detect C functions :

IF (OPATTR(funcname)) EQ 421
   IF counter NE 0
      add esp,counter
   ENDIF
ENDIF

zooba

Quote from: Vortex on October 30, 2006, 06:10:20 PM
The code portion to detect C functions :

IF (OPATTR(funcname)) EQ 421
   IF counter NE 0
      add esp,counter
   ENDIF
ENDIF


The IF (OPATTR(funcname)) EQ 421 should, I believe, use the AND operator (or a combination of AND and EQ), since OPATTR may return other details which will cause an equality check to fail. Though I haven't checked your 421 value, it looks like quite a few bits are set.

Cheers,

Zooba :U

MichaelW

Quote from: zooba on October 31, 2006, 11:11:11 PM
The IF (OPATTR(funcname)) EQ 421 should, I believe, use the AND operator (or a combination of AND and EQ), since OPATTR may return other details which will cause an equality check to fail. Though I haven't checked your 421 value, it looks like quite a few bits are set.

The set bits are:

0 – references a code label
2 – is immediate expression
5 – references no undefined symbols and is without error
7 – references an external label
8 – language type C

For an invoke I can't see how these bits could be set:

1 – is a memory expression or has a relocatable data label
3 – uses direct memory addressing
4 – is a register expression
6 – is SS relative memory expression

But what about bit 7 for a local C procedure (however unlikely that might be)?

eschew obfuscation

Vortex

The set bits are:

0 – references a code label
2 – is immediate expression
5 – references no undefined symbols and is without error
7 – references an external label
8 – language type C


MichaelW is right, these are the bits. The trick to obtain the 421 value is to play with the OPATTR statement :

.386
.model flat,stdcall
option casemap:none

include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib

temp    TEXTEQU %(OPATTR(wsprintf))

.code

start:

% echo temp

ret

END start


You get 421 as the result.

Vortex

This one is a sight modification :

IF ((OPATTR(funcname)) AND 11100000000y) EQ 00100000000y
   IF counter NE 0
      add esp,counter
   ENDIF
ENDIF

[attachment deleted by admin]

Vortex

Here is a new version. Thanks to E^cube to report the bugs in the macro.

_invoke MACRO funcname:REQ,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20
LOCAL pos,counter
counter=0

    FOR arg,<p20,p19,p18,p17,p16,p15,p14,p13,p12,p11,p10,p9,p8,p7,p6,p5,p4,p3,p2,p1>

        IFNB <arg>

counter=counter+4
pos=@InStr(1,arg,<ADDR >) OR @InStr(1,arg,<addr >) OR @InStr(1,arg,<Addr >)  OR \
                @InStr(1,arg,<ADDR >) OR @InStr(1,arg,<addr >) OR @InStr(1,arg,<Addr >)

             IF pos

                IF ((OPATTR(@SubStr(arg,%pos+5))) EQ 98) OR ((OPATTR(@SubStr(arg,%pos+5))) EQ 34)

                        lea eax,@SubStr(<arg>,%pos+5)
                        push eax
                ELSE
                        push OFFSET @SubStr(<arg>,%pos+5)
                ENDIF

             ELSE
                        push arg
             ENDIF
        ENDIF
    ENDM
call funcname

IF ((OPATTR(funcname)) AND 11100000000y) EQ 00100000000y ; Handle C functions
    IF counter NE 0
        add esp,counter ; correct the stack
    ENDIF
ENDIF

ENDM

[attachment deleted by admin]

gfalen

#13
This functionality is already provided by invoke.  A more usefull implementation would allow
for immediate strings such as - invok SomeFunction, "Literal String".

[Edit]

$str MACRO _STR
local L
    if @InStr(1, <_STR>, <!">)
        .data
        ifdif <_STR>, <"">
            L db _STR
        endif
        db 0
        .code
        exitm <offset L>
    else
        exitm <_STR>
    endif
endm

invok MACRO _FUNC, _ARGS:VARARG
    $ARGS equ <>
    irp $V, <_ARGS>
        $ARGS catstr $ARGS, <,>, $str($V)
    endm
    % invoke _FUNC $ARGS
endm

ecube

Alright great!, thanks Vortex, i'll test it out  :bg