flat assembler
Message board for the users of flat assembler.

Index > Macroinstructions > [sug] Allowing edx usage in 'stdcall' when mixed with 'addr'

Author
Thread Post new topic Reply to topic
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20430
Location: In your JS exploiting you and your system
revolution 18 Feb 2008, 16:54
Usually this will not do what you might expect:
Code:
stdcall functionX, edx, ecx, eax, addr esi+8    
The value pushed for edx will be currupted by the "addr esi+8" parameter.

So I fixed it.
Code:
include 'win32ax.inc'

macro callpusher ret_arg_count,[arg] {
    common
   local edx_corrupted_flag, edx_save_flag, temp_edx_save_flag, arg_count, temp_arg_count, ip, opcode
  if defined edx_save_flag
            mov [esp-arg_count*4],edx
   end if
      temp_arg_count=0
    reverse
     match x =edx y,:arg: \{
               if defined edx_corrupted_flag & edx_corrupted_flag=1
                    temp_edx_save_flag=1
                        edx_corrupted_flag=0
                        mov edx,[esp-(arg_count-temp_arg_count)*4]
          end if
      \}
    ip=$
        pushd arg
   load opcode from ip
 if opcode=0x8d
              edx_corrupted_flag=1
        end if
      temp_arg_count=temp_arg_count+1
    common
       if defined temp_edx_save_flag
               edx_save_flag=1
     end if
      arg_count=temp_arg_count
    ret_arg_count=arg_count
}

macro stdcall proc,[arg] {
    common local arg_count
      callpusher arg_count,arg
    call proc
}

macro invoke proc,[arg] {
   common local arg_count
      callpusher arg_count,arg
    call [proc]
}

macro ccall proc,[arg] {
  common local arg_count
      callpusher arg_count,arg
    call proc
   lea esp,[esp+arg_count*4] ;preserves carry
}

macro cinvoke proc,[arg] {
 common local arg_count
      callpusher arg_count,arg
    call [proc]
 lea esp,[esp+arg_count*4] ;preserves carry
}

;A simple test below to exercise the macros

.code

func dd 0
       stdcall func, edx
   ccall   func, addr edx+1234, edx
    invoke  func, edx, addr edx+1234, edx
       cinvoke func, edx, addr edx+1234
    stdcall func, addr edx+1234, edx, addr edx+1234
     ccall   func, edx, addr edx+1234, edx, addr edx+1234
        invoke  func, edx, addr edx+1234, edx, addr edx+1234, edx
   cinvoke func, edx, edx, addr edx+1234, edx, addr edx+1234, edx

.end func    
It is optimal in the sense that it will only generate the extra save and restore code when required.

I would like to encourage the Tomasz to consider including this in the next version.
Post 18 Feb 2008, 16:54
View user's profile Send private message Visit poster's website Reply with quote
wisepenguin



Joined: 30 Mar 2005
Posts: 129
wisepenguin 18 Feb 2008, 17:07
thanks revolution. good contribution.
Post 18 Feb 2008, 17:07
View user's profile Send private message Reply with quote
MHajduk



Joined: 30 Mar 2006
Posts: 6115
Location: Poland
MHajduk 18 Feb 2008, 20:15
Every patch which will make FASM more deterministic is wellcome. Smile
Post 18 Feb 2008, 20:15
View user's profile Send private message Visit poster's website Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid 18 Feb 2008, 22:02
i remember i wanted to do this for FASMLIB, but failed (i instead always preserve registers on "addr" using LEA/PUSH/XCHG). good job!

btw, you should add nested calls support, if you want tomasz to adopt it Razz

Code:
        match x =edx y,:arg: \{     

wow, this works to catch EDX anywhere in expression? It's a new one for me!
Post 18 Feb 2008, 22:02
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid 18 Feb 2008, 22:08
by the way: where do you reserve/allocate/release the place for EDX on stack?
Post 18 Feb 2008, 22:08
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20430
Location: In your JS exploiting you and your system
revolution 19 Feb 2008, 04:00
vid wrote:
... (i instead always preserve registers on "addr" using LEA/PUSH/XCHG)...
Using xchg with memory is a terrible solution. The CPU will automatically make it atomic creating lots of extra bus traffic and cost hundreds of clock cycles.
vid wrote:
btw, you should add nested calls support, if you want tomasz to adopt it Razz
Okay, you may be correct, I will look into it.
vid wrote:
where do you reserve/allocate/release the place for EDX on stack?
If the macro detects that addr is used and edx is needed later then it places edx at the last stack position.
Post 19 Feb 2008, 04:00
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: 20430
Location: In your JS exploiting you and your system
revolution 19 Feb 2008, 10:22
vid wrote:
btw, you should add nested calls support, if you want tomasz to adopt it
I just looked at the allow_nesting macro and it creates a dilemma.

What is the best thing to do with eax,ecx,edx for nested calls? Because of course a nested invoke is possible to trash all three registers and the stack. If we could save them all when required then that would be kinda nice to not have to worry about how and where to you can place the registers, but where to save them? e.g.
Code:
invoke FuncX, ecx, esi, <invoke FuncY, esi, ecx>, edi    

The current macros create this without any warnings:
Code:
push edi
push ecx ;part of the second invoke
push esi ;part of the second invoke
call [FuncY] ;the second invoke
push eax ;save the result from the second invoke
push esi
push ecx ;!!! problem here, what is ecx?
call [FuncX]    

We could quite easily, and naively, make macros to do this:
Code:
mov [esp-4*(4)],ecx
push edi
push ecx ;part of the second invoke
push esi ;part of the second invoke
call [FuncY] ;the second invoke
push eax ;save the result from the second invoke
push esi
mov ecx,[esp-4*(1)] ;!!! problem here, what is on the stack?
push ecx ;garbage
call [FuncX]    

The only reasonable solution is to change the method. Something like this:
Code:
sub esp,4*(4) ;make space for 4 parameters
mov [esp+4*(3)],edi ;the 4th parameter
;mov [esp+4*(2)],??? ;the 3rd parameter - don't do this yet
mov [esp+4*(1)],esi ;the 2nd parameter
mov [esp+4*(0)],ecx ;the 1st parameter

;using the simple invoke method for second level invokes
push ecx ;part of the second invoke
push esi ;part of the second invoke
call [FuncY] ;the second invoke
mov [esp+4*(2)],eax ;the 3rd parameter

;finally call the function
call [FuncX]    

And that will work but it still has a problem:
Code:
invoke FuncX, ecx, esi, <invoke FuncY, esi, ecx>, <invoke FuncZ, ebx, eax>    

When you look at the code created it shows the problem.
Code:
sub esp,4*(4) ;make space for 4 parameters
;mov [esp+4*(3)],??? ;the 4th parameter - don't do this yet
;mov [esp+4*(2)],??? ;the 3rd parameter - don't do this yet
mov [esp+4*(1)],esi ;the 2nd parameter
mov [esp+4*(0)],ecx ;the 1st parameter

;using the simple invoke method for second level invokes

push eax ;part of the third invoke
push ebx ;part of the third invoke
call [FuncZ] ;the third invoke
mov [esp+4*(3)],eax ;the 4th parameter

push ecx ;!!! problem, what is ecx??
push esi ;part of the second invoke
call [FuncY] ;the second invoke
mov [esp+4*(2)],eax ;the 3rd parameter

;finally call the function
call [FuncX]    
So now we are left with not much option, we need to be clever about where to get the contents of a register. With the same triple invoke code above:
Code:
sub esp,4*(4) ;make space for 4 parameters
;mov [esp+4*(3)],??? ;the 4th parameter - don't do this yet
;mov [esp+4*(2)],??? ;the 3rd parameter - don't do this yet
mov [esp+4*(1)],esi ;the 2nd parameter
mov [esp+4*(0)],ecx ;the 1st parameter

;using the simple invoke method for second level invokes

push eax ;part of the third invoke
push ebx ;part of the third invoke
call [FuncZ] ;the third invoke
mov [esp+4*(3)],eax ;the 4th parameter

mov ecx,[esp+4*(0)] ;retrieve ecx, the stack is still valid
push ecx ;okay now.
push esi ;part of the second invoke
call [FuncY] ;the second invoke
mov [esp+4*(2)],eax ;the 3rd parameter

;finally call the function
call [FuncX]    

But it gets quite tricky when we use a register that is not needed further down the stack:
Code:
invoke FuncX, eax, <invoke FuncY, ecx, edx>, <invoke FuncZ>    

Code:
sub esp,4*(3) ;make space for 3 parameters
;mov [esp+4*(2)],??? ;the 3rd parameter - don't do this yet
;mov [esp+4*(1)],??? ;the 2nd parameter - don't do this yet
mov [esp+4*(0)],eax ;the 1st parameter

;using the simple invoke method for second level invokes

call [FuncZ] ;the third invoke
mov [esp+4*(2)],eax ;the 3rd parameter

;now where are the original values of ecx and edx??
push edx ;!!! problem, what is edx??
push ecx ;!!! problem, what is ecx??
call [FuncY] ;the second invoke
mov [esp+4*(1)],eax ;the 2nd parameter

;finally call the function
call [FuncX]    

So a possible robust solution is to simply save the affected registers before calling nested invoke(s).
Code:
sub esp,4*(3) ;make space for 3 parameters
;mov [esp+4*(2)],??? ;the 3rd parameter - don't do this yet
;mov [esp+4*(1)],??? ;the 2nd parameter - don't do this yet
mov [esp+4*(0)],eax ;the 1st parameter

push edx ecx ;save them, we need them later
call [FuncZ] ;the third invoke
mov [esp+4*(2)],eax ;the 3rd parameter
pop ecx edx

push edx ;okay
push ecx ;okay
call [FuncY] ;the second invoke
mov [esp+4*(1)],eax ;the 2nd parameter

;finally call the function
call [FuncX]    

Obviously the pop the push again is unnecessary and wasteful, but since the second level (and deeper) invokes are recursively called then it would be very tricky to catch all cases and eliminate the waste. And would probably open up too much chance of bugs in some weird cases.

So overall there is a way, and it relieves the programmer from unexpected bugs. The macros might get a little bit more complicated though, but it is doable, and I think fasm can handle it without much difficulty.

[edit]I forgot to mention, another option is just leave it as is with the implicit "gotcha" code, but that seems kinda cruel, especially for beginners.[/edit]
Post 19 Feb 2008, 10:22
View user's profile Send private message Visit poster's website Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid 19 Feb 2008, 21:30
i still don't understand where you take place for that EDX variable. If i use your macro inside procedure, doesn't it overwrite my "topmost" data on stack?

by the way, you don't handle "qword" pushes Razz
Post 19 Feb 2008, 21:30
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20430
Location: In your JS exploiting you and your system
revolution 20 Feb 2008, 01:53
vid: Don't worry, the macro doesn't overwrite anything, it uses the stack space of the first parameter (the last value pushed) to save edx.
Post 20 Feb 2008, 01:53
View user's profile Send private message Visit poster's website 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-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.