News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Invoke

Started by mabdelouahab, August 07, 2016, 06:11:32 PM

Previous topic - Next topic

mabdelouahab

This is just an attempt  :biggrin:
My Invoke macro with best performence


  .invoke CreateFont,16,8,0,0,600,0,0,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,PROOF_QUALITY,FF_DONTCARE,"fixedsys"

.text:00000000004014e0       sub rsp, 0x70
.text:00000000004014e4       mov rcx, 0x10
.text:00000000004014eb       mov rdx, 8
.text:00000000004014f2       xor r8, r8
.text:00000000004014f5       xor r9, r9
.text:00000000004014f8       mov qword ptr [rsp+0x20], 0x258
.text:0000000000401501       mov qword ptr [rsp+0x28], r8
.text:0000000000401506       mov qword ptr [rsp+0x30], r8
.text:000000000040150b       mov qword ptr [rsp+0x38], 0x1
.text:0000000000401514       mov qword ptr [rsp+0x40], r8
.text:0000000000401519       mov qword ptr [rsp+0x48], r8
.text:000000000040151e       mov qword ptr [rsp+0x50], 2
.text:0000000000401527       mov qword ptr [rsp+0x58], r8
.text:000000000040152c       lea rax, [0x004032ef]
.text:0000000000401533       mov qword ptr [rsp+0x60], rax
.text:0000000000401538       call qword ptr [CreateFontA]
.text:000000000040153e       add rsp, 0x70

.invoke MessageBox,0,"This is a test",0,RCX

.text:0000000000401542       sub rsp, 0x20
.text:0000000000401546       lea rdx, [0x004032f8]
.text:000000000040154d       xor r8, r8
.text:0000000000401550       mov r9, rcx
.text:0000000000401553       xor rcx, rcx
.text:0000000000401556       call qword ptr [MessageBoxA]
.text:000000000040155c       add rsp, 0x20

.invoke MessageBox,R9,"This is a test",0,edx

.text:0000000000401560       sub rsp, 0x20
.text:0000000000401564       mov rcx, r9
.text:0000000000401567       lea rax, [0x00403307]
.text:000000000040156e       mov qword ptr [rsp+0x8], rax
.text:0000000000401573       xor r8, r8
.text:0000000000401576       mov r9d, edx
.text:0000000000401579       mov rdx, qword ptr [rsp+0x8]
.text:000000000040157e       call qword ptr [MessageBoxA]
.text:0000000000401584       add rsp, 0x20






OPTION DOTNAME                         

.Putstr macro sstr
LOCAL dstr,?Len,cur_Pos,ch_unq,tmpStr


IFDEF __UNICODE__
?Len = @SizeStr(<sstr>)
?Len = ?Len - 2
cur_Pos = 1
tmpStr CATSTR <>
repeat ?Len
cur_Pos=cur_Pos+1
ch_unq SubStr <sstr>,cur_Pos,1
tmpStr CATSTR tmpStr,<!">,ch_unq,<!",>
endm
.data
dstr dw tmpStr 0
.code
ELSE
.data
dstr db sstr,0
.code
ENDIF
EXITM< dstr >
endm

.RAX macro __val
LOCAL n
n equ type( __val)
IF n eq 1
MOV AL,__val
ELSEIF n eq 2
MOV AX,__val
ELSEIF n eq 4
MOV EAX,__val
ELSE
MOV RAX,__val
ENDIF
endm
.RCX macro __val
LOCAL n
n equ type( __val)
IF n eq 1
MOV CL,__val
ELSEIF n eq 2
MOV CX,__val
ELSEIF n eq 4
MOV ECX,__val
ELSE
MOV RCX,__val
ENDIF
endm
.RDX macro __val
LOCAL n
n equ type( __val)
IF n eq 1
MOV DL,__val
ELSEIF n eq 2
MOV DX,__val
ELSEIF n eq 4
MOV EDX,__val
ELSE
MOV RDX,__val
ENDIF
endm
.R8 macro __val
LOCAL n
n equ type( __val)
IF n eq 1
MOV R8B,__val
ELSEIF n eq 2
MOV R8W,__val
ELSEIF n eq 4
MOV R8D,__val
ELSE
MOV R8,__val
ENDIF
endm
.R9 macro __val
LOCAL n
n equ type( __val)
IF n eq 1
MOV R9B,__val
ELSEIF n eq 2
MOV R9W,__val
ELSEIF n eq 4
MOV R9D,__val
ELSE
MOV R9,__val
ENDIF
endm
.MEM macro __val,__ad
LOCAL n
n equ type( __val)
IF n eq 1
MOV BYTE PTR [RSP+__ad],__val
ELSEIF n eq 2
MOV WORD PTR [RSP+__ad],__val
ELSEIF n eq 4
MOV DWORD PTR [RSP+__ad],__val
ELSE
MOV QWORD PTR [RSP+__ad],__val
ENDIF
endm

.SetMEM macro addit_,argN
IF IsNullv eq 1
IF hNullReg
MOV QWORD PTR [RSP+addit_],rNullReg
ELSE
IF nRAX 
MOV QWORD PTR [RSP+addit_],RAX
ELSE
nRAX =1 ;rax=0
XOR RAX,RAX
MOV QWORD PTR [RSP+addit_],RAX
ENDIF
ENDIF
ELSEIF IsAddr eq 1
nRAX =2 ;rax=??
LEA RAX,@SubStr(<argN>,2)
MOV QWORD PTR [RSP+addit_],RAX
ELSEIF IsString eq 1
nRAX =2 ;rax=??
LEA RAX,.Putstr(argN)
MOV QWORD PTR [RSP+addit_],RAX
ELSE
IF (instrRAX and uRAX)
IF nRAX ne 3
nRAX =3 ;rax=rax
MOV RAX,QWORD PTR [RSP-8] ; pop rax
ENDIF
.MEM argN, addit_
ELSEIF uIMM eq 1
nRAX =2 ;rax=??
.RAX argN
MOV QWORD PTR [RSP+addit_],RAX
ELSE
.MEM argN, addit_
ENDIF
ENDIF
endm

.invoke macro ProcName,ProcArg:vararg
nbProcArg = 0
uRCX = 0
uRDX = 0
uR8 = 0
uR9 = 0
uRAX = 0
nRCX = 0
nRDX = 0
nR8 = 0
nR9 = 0
nRAX = 0
uIMM = 0
instrInterface  INSTR 1,<ProcName>,<.>
IF instrInterface
nbProcArg = nbProcArg +
ENDIF
FOR argN,<ProcArg>
nbProcArg=nbProcArg+1   
upArg CatStr <__UpStr(&argN)>
% instrRCX  INSTR 1,<-RCX-ECX-CX-CH-CL->,<-&upArg&->
% instrRDX  INSTR 1,<-RDX-EDX-DX-DH-DL->,<-&upArg&->
% instrR8   INSTR 1,<-R8-R8D-R8W-R8B->,<-&upArg&->;
% instrR9   INSTR 1,<-R9-R9D-R9W-R9B->,<-&upArg&->
% instrRAX  INSTR 1,<-RAX-EAX-AX-AH-AL->,<-&upArg&->
instrAddr SubStr <argN>,1,1
IsNullArg = 0
IFDIF instrAddr,<&>
IFDIF instrAddr,<">
??oparg = OPATTR(argN)
IF (??oparg eq 024h)
?v = argN
IF ?v eq 0
IF nbProcArg eq 1
nRCX = 1
ELSEIF nbProcArg eq 2
nRDX = 1
ELSEIF nbProcArg eq 3
nR8 = 1
ELSEIF nbProcArg eq 4
nR9 = 1
ENDIF
IsNullArg = 1
ENDIF
ENDIF
ENDIF
ENDIF
IF (( uRCX eq 0 ) and ( nbProcArg ne 1 ) )
IF instrRCX
uRCX = 1
ENDIF
ENDIF
IF (( uRDX eq 0 ) and ( nbProcArg gt 1 ) )
IF instrRDX
uRDX = 1
ENDIF
ENDIF
IF (( uR8 eq 0 ) and ( nbProcArg gt 2 ) )
IF instrR8
uR8 = 1
ENDIF
ENDIF
IF (( uR9 eq 0 ) and ( nbProcArg gt 3 ) )
IF instrR9
uR9 = 1
ENDIF
ENDIF
IF (( uRAX eq 0 ) and ( nbProcArg gt 4 ) )
IF instrRAX
uRAX = 1
ENDIF
ENDIF
IF IsNullArg eq 0
IF ((uIMM eq 0) and ( nbProcArg gt 4) );and (IsNullArg eq 0)
IFIDN instrAddr,<&>
uIMM = 1
ENDIF
IFDIF instrAddr,<&>
IFIDN instrAddr,<">
uIMM = 1
ENDIF
IFDIF instrAddr,<">
??oparg = OPATTR(argN)
IF ((??oparg eq 024h) OR (??oparg eq 026h) OR (??oparg eq 030h) OR (??oparg eq 062h)) eq 0;Reg or Imm
uIMM = 1
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDM
adj??=nbProcArg-((nbProcArg/2)*2)
alnbProcArg=nbProcArg+adj?? ; align to 16
IF alnbProcArg lt 4
alnbProcArg=4
ENDIF
alnbProcArg=(alnbProcArg*8)
hNullReg = 1
IF (nRCX and (uRCX eq 0))
rNullReg equ <RCX>
ELSEIF (nRDX and (uRDX eq 0))
rNullReg equ <RDX>
ELSEIF (nR8 and (uR8 eq 0))
rNullReg equ <R8>
ELSEIF (nR9 and (uR9 eq 0))
rNullReg equ <R9>
ELSE
uIMM = 1
hNullReg = 0
ENDIF

SUB RSP,alnbProcArg
; If rax is in args and and there are an other mem arg that we must use rax to pass it
IF ((uRAX eq 1) and (uIMM eq 1))
MOV QWORD PTR [RSP-8],RAX ; PUSH RAX
ELSE
uRAX = 0
ENDIF

nbProcArg=0
IF instrInterface
nbProcArg=nbProcArg+1 
ENDIF
FOR argN,<ProcArg>
IsNullv = 0
upArg CatStr <__UpStr(&argN)>
% instrRCX  INSTR 1,<-RCX-ECX-CX-CH-CL->,<-&upArg&->
% instrRDX  INSTR 1,<-RDX-EDX-DX-DH-DL->,<-&upArg&->
% instrR8   INSTR 1,<-R8-R8D-R8W-R8B->,<-&upArg&->;
% instrR9   INSTR 1,<-R9-R9D-R9W-R9B->,<-&upArg&->
% instrRAX  INSTR 1,<-RAX-EAX-AX-AH-AL->,<-&upArg&->
instrAddr SubStr <argN>,1,1
uIMM = 0
IsAddr = 0
IsString = 0
IFIDN instrAddr,<&>
IsAddr = 1
ENDIF
IF IsAddr ne 1
IFIDN instrAddr,<">
IsString = 1
ENDIF
IF IsString ne 1
??oparg = OPATTR(argN)
IF (??oparg eq 024h)
?v = argN
IF ?v eq 0
IsNullv = 1
ENDIF
ENDIF
ENDIF
ENDIF
uIMM = 0
IF ((IsNullv eq 0)  and ( IsString eq 0)and ( IsAddr eq 0) )
??oparg = OPATTR(argN)
IF ((??oparg eq 024h) OR (??oparg eq 026h) OR (??oparg eq 030h) OR (??oparg eq 062h))
uIMM = 1
ENDIF
ENDIF
IF nbProcArg eq 0
IF uRCX eq 1 ;If RCX is used in other arg => push in home
IF nRCX eq 0
.SetMEM 0,argN
ENDIF
ELSE
IF IsNullv eq 1 ; if arg =0
XOR RCX,RCX
ELSEIF IsAddr eq 1
LEA RCX,@SubStr(<argN>,2)
ELSEIF IsString eq 1
% LEA RCX,.Putstr(argN)
ELSE
IF instrRCX eq 0 ; if not rcx in 1st arg
.RCX argN
ENDIF
ENDIF
ENDIF
ELSEIF  nbProcArg eq 1
IF uRDX eq 1
IF nRDX eq 0
.SetMEM 8,argN
ENDIF
ELSE
IF IsNullv eq 1
XOR RDX,RDX
ELSEIF IsAddr eq 1
LEA RDX,@SubStr(<argN>,2)
ELSEIF IsString eq 1
LEA RDX,.Putstr(argN)
ELSE
IF instrRDX eq 0 ; if not rcx in 1st arg
.RDX argN
ENDIF
ENDIF
ENDIF
ELSEIF  nbProcArg eq 2
IF uR8 eq 1
IF nR8 eq 0
.SetMEM 16,argN
ENDIF
ELSE
IF IsNullv eq 1
XOR R8,R8
ELSEIF IsAddr eq 1
LEA R8,@SubStr(<argN>,2)
ELSEIF IsString eq 1
LEA R8,.Putstr(argN)
ELSE
IF instrR8 eq 0
.R8 argN
ENDIF
ENDIF
ENDIF
ELSEIF  nbProcArg eq 3
IF uR9 eq 1
IF nR9 eq 0
.SetMEM 24,argN
ENDIF
ELSE
IF IsNullv eq 1
XOR R9,R9
ELSEIF IsAddr eq 1
LEA R9,@SubStr(<argN>,2)
ELSEIF IsString eq 1
LEA R9,.Putstr(argN)
ELSE
IF instrR9 eq 0
.R9 argN
ENDIF
ENDIF
ENDIF
ELSE
.SetMEM <(nbProcArg*8)>,argN
ENDIF
nbProcArg=nbProcArg+1 
ENDM
IF uRCX eq 1
IF nRCX ne 0
XOR RCX,RCX
ELSE
MOV RCX,QWORD PTR [RSP]
ENDIF
ENDIF
IF uRDX eq 1
IF nRDX ne 0
XOR RDX,RDX
ELSE
MOV RDX,QWORD PTR [RSP+8]
ENDIF
ENDIF
IF uR8 eq 1
IF nR8 ne 0
XOR R8,R8
ELSE
MOV R8,QWORD PTR [RSP+16]
ENDIF
ENDIF
IF uR9 eq 1
IF nR9 ne 0
XOR R9,R9
ELSE
MOV R9,QWORD PTR [RSP+24]
ENDIF
ENDIF
IF instrInterface
sChrs SizeStr <ProcName>
MOV RCX,@SubStr(ProcName,1,instrInterface-1)
MOV RAX,[RCX]
IF sChrs gt instrInterface
fChr SubStr <ProcName>,instrInterface+1,1
instrNum INSTR 1,<0123456789>,fChr
Indexf SubStr <ProcName>,instrInterface+1
IF instrNum
ADD RAX,Indexf * ( SizeOf rWord)
CALL QWORD PTR [RAX]
ELSE
% CALL [RAX]. &Indexf&
ENDIF
ELSE
CALL QWORD PTR [RAX]
ENDIF
ELSE
call ProcName
ENDIF
ADD RSP,alnbProcArg
endm


jj2007

Interesting approach, and it works fine :t

hutch--

Compliments, the output is well done. The next trick is to handle different sized data apart from QWORD sizes. Sad to say the input from some structures is in DWORD rather than QWORD so you need to use OPATTR to determine the data SIZE and write it to the appropriate sized register (al, ax, eax etc ...) then write the full 64 bit register to the memory locations you are using.

I just had a look at the source you have posted and it does the correct sizes. Compliments again.  :t

jj2007

Quote from: hutch-- on August 07, 2016, 08:12:09 PMThe next trick is to handle different sized data apart from QWORD sizes
...
I just had a look at the source you have posted and it does the correct sizes. Compliments again.  :t

Nice indeed:
.RCX macro __val
LOCAL n
n equ type( __val)
IF n eq 1
MOV CL,__val
ELSEIF n eq 2
MOV CX,__val
ELSEIF n eq 4
MOV ECX,__val
ELSE
MOV RCX,__val
ENDIF
endm

hutch--

mabdelouahab,

I have a question for you, have you written your own prologue/epilogue or are you using an existing one, the default or Vasily's pair ? I ask this for a reason as so far I have found this effects "invoke" style macros wityh how accurately the data is passed to the called process.

jj2007

Indeed, prolog macros are fun :t

You can smuggle in things like this check for stack alignment:
  if usedeb
j4Save ; save 4 args in global vars
test sp, 15
je alignOK
Print " Stack misaligned in &procname: "
mov rax, rsp
PrintLine Hex$(rax)
jmp alignBad
alignOK:
if jbVerbosePE
    PrintLine " stack ok"
endif
alignBad:
j4Restore
  endif


Applied to a commented out sub rsp, 8:
j@start
; sub rsp, 8 ; v v v fall through to winmain requires this correction
WinMain proc arg


Results in WndProc:
Message=f
Message=14
Message=7f
Message=7f
Message=7f
Stack misaligned in WndProc: 12fbf8
Message=c0d6
Message=86
Message=d
Message=6


The odd thing here is that over 90% of all messages are aligned. And you can run the proggie without ever seeing a problem - the ideal situation for spending some nights in bug chasing mode :P

mabdelouahab

Quote from: hutch-- on August 08, 2016, 05:16:48 PM
mabdelouahab,

I have a question for you, have you written your own prologue/epilogue or are you using an existing one, the default or Vasily's pair ? I ask this for a reason as so far I have found this effects "invoke" style macros wityh how accurately the data is passed to the called process.
Sir hutch,
I don't use my own prologue/epilogue, I use what you use in masm64rt.inc

sinsi

Off-topic, but jj's "test sp, 15" caught my eye.

An interesting use of a 16+ bit register - accessing the low byte of the stack pointer.

.flat:0000000000401000 48 F7 C4 0F 00 00 00                    test    rsp, 0Fh
.flat:0000000000401007 F7 C4 0F 00 00 00                       test    esp, 0Fh
.flat:000000000040100D 66 F7 C4 0F 00                          test    sp, 0Fh
.flat:0000000000401012 40 F6 C4 0F                             test    spl, 0Fh

There is no corresponding sph because the encoding for the high byte (and AH,BH etc) is used in 64-bit mode as a prefix (usually REX ?).

jj2007

Nice find, John :t
Will test if all assemblers understand spl ;)

mabdelouahab

Now you can call Interface methods:
      .invoke ppvInterface.index   ,.......
ppvInterface:variable = interface pointer
index : is const number 0=first function,1=second function, ...

or      .invoke ppvInterface.IvTable.Method   ,.......

or      .invoke ppvInterface.   ,....... ; to call the first method


Word Application Example:


   STIUnknown STRUCT
      Queryinterface    DQ   0        ; index=0
      AddRef            DQ   0       ; index=1
      Release           DQ   0       ; index=2
   STIUnknown ends
.data
   IID_IUnknown               GUID    <000000000h,00000h,00000h,<0C0h,000h,000h,000h,000h,000h,000h,046h>>
   CLSID_Word_Application     GUID    <0000209FFh,00000h,00000h,<0C0h,000h,000h,000h,000h,000h,000h,046h>>
   IID_Word__Application      GUID    <000020970h,00000h,00000h,<0C0h,000h,000h,000h,000h,000h,000h,046h>>

.code
   .invoke CoInitializeEx,0,0
   .invoke CoCreateInstance,&CLSID_Word_Application,0,CLSCTX_LOCAL_SERVER,&IID_IUnknown,&_ppv_IUnknown
   .if rax == S_OK
      .invoke MessageBoxW,0,"CoCreateInstance Word Application ok",0,0
      .invoke _ppv_IUnknown.STIUnknown.Queryinterface,&IID_Word__Application,&WordApplication
      ; Also you can use:
      ;.invoke _ppv_IUnknown.,&IID_Word__Application,&WordApplication

      .if rax == S_OK
         .invoke MessageBoxW,0,"IUnknown.Queryinterface ok",0,0
         ;------------------------------------ WordApplication.Visible = TRUE
         .invoke WordApplication.34,TRUE
         ;------------------------------- pause
         .invoke MessageBoxW,0,"push OK to quit Winword ",0,0
            .data
               NulloptionalArg      dw   VT_ERROR,0,0,0      ;Null Optional Arg
                                    dq   DISP_E_PARAMNOTFOUND,0
            .code
         ; WordApplication.Quit ([in,optional] SaveChanges:Ptr Variant,[in,optional] OriginalFormat:Ptr Variant,...)            
         .invoke WordApplication.120,&NulloptionalArg,&NulloptionalArg,&NulloptionalArg
         ;WordApplication.Release
         .invoke WordApplication.2

      .endif
   .endif