flat assembler
Message board for the users of flat assembler.

Index > Windows > Win32 programming without includes

Goto page Previous  1, 2
Author
Thread Post new topic Reply to topic
LocoDelAssembly
Your code has a bug


Joined: 06 May 2005
Posts: 4624
Location: Argentina
LocoDelAssembly 07 Sep 2008, 15:22
ESP must be restored to the state right before the call in the case of cdecl calling convention and to the state right before the first "push param" in the case of stdcall calling convention. In the latter case you use "ret 4*args" in your proc (ret size_of_params and no param is less than 4 bytes to be more exact).
Post 07 Sep 2008, 15:22
View user's profile Send private message Reply with quote
Pinecone_



Joined: 28 Apr 2008
Posts: 180
Pinecone_ 05 Oct 2008, 08:08
When the CPU gets to a call instruction, it has two jobs to do: push the address of the next function onto the stack, so it knows where to return to with ret instruction, and jump to the address specified.

When your procedure gets called, if you are expecting to use arguments and the stack within your procedure, you should push ebp onto the stack and mov the value of esp into ebp so you can reference to the arguments passed to your procedure.

The leave command pops ebp back off the stack, and ret jumps back to the next instruction after the call which called your procedure.

The whole process may be explained a little better like this:

Code:
call Test ; push address of next instruction onto stack and jump to test procedure    
Code:
Test:
push ebp
mov ebp, esp
; ... do whatever here, use the stack as normal and use ebp to reference the arguments passed to Test
; say you wanted to get the value passed in argument number two into eax:
; mov eax, [ebp + C]
; it's ebp + c because ebp + 0 is the origonal value of ebp
; ebp + 4 is the address to return to with ret instruction
; from ebp + 8 is your arguments
leave ; basically restores ebp - i think equal to Mov Ebp, [Ebp] and Add Esp, 4
ret NUMBER_OF_ARGS*4; pop arguments off the stack, return back to instruction after call    


I'm probably not the best person at explaining that, please someone correct me if i got something wrong... Razz

good luck with fasm pal.

Edit: sorry for bumping old topic - i forgot to look at the dates Razz


Last edited by Pinecone_ on 17 Oct 2008, 13:44; edited 1 time in total
Post 05 Oct 2008, 08:08
View user's profile Send private message Reply with quote
pal



Joined: 26 Aug 2008
Posts: 227
pal 12 Oct 2008, 21:36
Ahh cheers mate, that actually helped a lot. I understand better now. I knew that it pushed the return address value to the stack, but didn't know much about the leave command. Cheers mate.
Post 12 Oct 2008, 21:36
View user's profile Send private message Reply with quote
bitshifter



Joined: 04 Dec 2007
Posts: 796
Location: Massachusetts, USA
bitshifter 17 Oct 2008, 07:04
One thing i noticed is that you are using DefWindowProc in a dialog procedure.
A WindowProc should be used to compliment RegisterClass and CreateWindow.
A DialogProc should be in any other case. (IE: DialogBox, DialogBoxParam)
A WindowProc should call DefWindowProc for default processing of unhandled messages.
A DialogProc should simply return zero for default processing of unhandled messages.
Also, because both are CALLBACK type functions, we should preserve ebx,esi,edi.
Code:
;------------------------------------------------
; hwnd:   dword[ebp+8]
; umsg:   dword[ebp+12]
; wparam: dword[ebp+16]
; lparam: dword[ebp+20]
;------------------------------------------------
    

Code:
WindowProc:
      push ebp
      mov ebp,esp
      push ebx esi edi
      cmp dword[ebp+12],0x0010
      je .wmclose
      cmp dword[ebp+12],0x0002
      je .wmdestroy
      cmp dword[ebp+12],0x0001
      je .wmcreate
   .defwndproc:
      push dword[ebp+20]
      push dword[ebp+16]
      push dword[ebp+12]
      push dword[ebp+8]
      call [DefWindowProc]
      jmp .finish
   .wmcreate:
      mov eax,1
      jmp .finish
   .wmclose:
      push dword[ebp+8]
      call [DestroyWindow]
      xor eax,eax
      jmp .finish
   .wmdestroy:
      push 0
      call [PostQuitMessage]
      xor eax,eax
   .finish:
      pop edi esi ebx
      mov esp,ebp
      pop ebp
      ret 16
    

Code:
DialogProc:
      push ebp
      mov ebp,esp
      push ebx esi edi
      cmp dword[ebp+12],0x0010
      je .wmclose
      cmp dword[ebp+12],0x0110
      je .wminitdialog:
   .defdlgproc:
      xor eax,eax
      jmp .finish
   .wminitdialog:
      mov eax,1
      jmp .finish
   .wmclose:
      push 0
      push dword[ebp+8]
      call [EndDialog]
      mov eax,1
   .finish:
      pop edi esi ebx
      mov esp,ebp
      pop ebp
      ret 16
    


Last edited by bitshifter on 24 Oct 2008, 02:02; edited 1 time in total
Post 17 Oct 2008, 07:04
View user's profile Send private message Reply with quote
asmcoder



Joined: 02 Jun 2008
Posts: 784
asmcoder 17 Oct 2008, 11:12
[content deleted]


Last edited by asmcoder on 14 Aug 2009, 14:55; edited 1 time in total
Post 17 Oct 2008, 11:12
View user's profile Send private message Reply with quote
Pinecone_



Joined: 28 Apr 2008
Posts: 180
Pinecone_ 17 Oct 2008, 13:10
no idea about enter, but leave does restores ebp. i haven't looked it up in a manual or anything (i should get around to it someday) so i don't know specifics, but if you step through a procedure generated by the proc/endp macros, look at what registers are changed by the leave instruction, ebp and esp. i guessed what i wrote in my above post from my observations of it so i could be off about some of that... but generally i think it's pretty much right

edit: i forgot to mention, i figure leave is equal to something like:
mov ebp, [ebp]
add esp, 4

also added that in the comments in above post


Last edited by Pinecone_ on 17 Oct 2008, 13:52; edited 1 time in total
Post 17 Oct 2008, 13:10
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20298
Location: In your JS exploiting you and your system
revolution 17 Oct 2008, 13:15
I think it would be faster to RTFM than to post a question here.

C'mon people, the manuals are you saviour, don't go coding things based upon myths and guesses.
Post 17 Oct 2008, 13:15
View user's profile Send private message Visit poster's website Reply with quote
Pinecone_



Joined: 28 Apr 2008
Posts: 180
Pinecone_ 17 Oct 2008, 13:50
No myths, and they were educated guesses after analysis. It would've been easier to read the manual, but the question was asked, so i answered.I found out my own way because back when i was trying to figure this stuff out, i didn't have a copy of Intel architecture instruction set manual 'cause i was just starting Razz after that, i have never had a need to look it up in the manual
Post 17 Oct 2008, 13:50
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4016
Location: vpcmpistri
bitRAKE 17 Oct 2008, 14:37
ENTER can be used to copy an array at (E|R)BP to the stack - very handy four byte instruction. It was designed to support multi-level call stacks and back-tracking; but it is good to also understand it at a low level.

Here is my attempt to consolidate the manual:
Code:
ENTER FRAME,NEST {
  push (r|e)bp              ;  BP,   EBP,  RBP
    mov [frametemp],(r|e)sp ;  SP,   ESP,  RSP
    if NEST<>0
      while NEST>1
        sub (r|e)bp,SIZEOF  ;    2,    4,    8
        push SIZE [(r|e)bp] ; word,dword,qword
        dec NEST
      end while
      push SIZE [frametemp] ; word,dword,qword
    end if
  mov (r|e)bp,[frametemp]   ;  BP,   EBP,  RBP
  sub (r|e)sp,FRAME         ;  SP,   ESP,  RSP
}    
...best to try it in a debugger and you'll grasp it very quickly.
The while loop copies an array from (R|E)BP!
(moving backwards in address values Razz)

99.99% of all ENTER use has NEST=0!

_________________
¯\(°_o)/¯ “languages are not safe - uses can be” Bjarne Stroustrup
Post 17 Oct 2008, 14:37
View user's profile Send private message Visit poster's website Reply with quote
LocoDelAssembly
Your code has a bug


Joined: 06 May 2005
Posts: 4624
Location: Argentina
LocoDelAssembly 17 Oct 2008, 14:52
Quote:

edit: i forgot to mention, i figure leave is equal to something like:
mov ebp, [ebp]
add esp, 4

In fact
Code:
mov esp, ebp
pop ebp    
Post 17 Oct 2008, 14:52
View user's profile Send private message Reply with quote
asmcoder



Joined: 02 Jun 2008
Posts: 784
asmcoder 17 Oct 2008, 17:11
[content deleted]


Last edited by asmcoder on 14 Aug 2009, 14:55; edited 1 time in total
Post 17 Oct 2008, 17:11
View user's profile Send private message Reply with quote
LocoDelAssembly
Your code has a bug


Joined: 06 May 2005
Posts: 4624
Location: Argentina
LocoDelAssembly 17 Oct 2008, 21:31
Yes, why do you use it that way? EBP is used as a fixed pointer to access local variables and parameters, not to hold the base address of the local variables. In the positive offsets you have the parameters and in the negative ones the local variables. By doing it in your way you are confusing the debugger that relies on having at [EBP] the old EBP and if the local variables are big enough (like your example), the offset for accessing the parameters won't be encoded as signed imm8 but in full imm32 instead, making the instructions that access parameters larger.
Post 17 Oct 2008, 21:31
View user's profile Send private message Reply with quote
Pinecone_



Joined: 28 Apr 2008
Posts: 180
Pinecone_ 18 Oct 2008, 01:14
ahh so i was wrong Razz that makes much more sense when i think about it Razz Thanks
Post 18 Oct 2008, 01:14
View user's profile Send private message Reply with quote
asmcoder



Joined: 02 Jun 2008
Posts: 784
asmcoder 18 Oct 2008, 13:22
[content deleted]


Last edited by asmcoder on 14 Aug 2009, 14:55; edited 1 time in total
Post 18 Oct 2008, 13:22
View user's profile Send private message Reply with quote
LocoDelAssembly
Your code has a bug


Joined: 06 May 2005
Posts: 4624
Location: Argentina
LocoDelAssembly 18 Oct 2008, 15:45
It is still wrong Confused You must move ESP to EBP before any modification to ESP (besides "push ebp") takes place.

Code:
;proc foo a, b, c
;local buff:BYTE[666], alfa:DWORD
push ebp
mov  ebp, esp
sub  esp, 4+666

mov  eax, [ebp+8]  ; mov eax, [a]
add  eax, [ebp+16] ; add eax, [c]

lea  edx, [ebp-4-666] ; lea edx, [buff] ; char *edx = &buff[0]
mov  ecx, [ebp-4] ; mov ebp, [alfa]

leave ; mov esp, ebp / pop ebp
ret
;endp    


This way, no matter how many times you alter ESP with push, pop, or whatever, EBP will be at a fixed position so your offsets can be constant. You are not obligated to make your procedures EBP-based though, but then, debuggers won't be able to build up the stack trace unless debugging information is present. As another advantage you have the code size, the code above takes 26 bytes but the ESP-based one takes 37.

I have tried to found references but I failed so I have to ask: does the ESP-based frame generates AGI stalls on some processors? (non leaf functions of course)
Post 18 Oct 2008, 15:45
View user's profile Send private message Reply with quote
asmcoder



Joined: 02 Jun 2008
Posts: 784
asmcoder 18 Oct 2008, 17:51
[content deleted]
Post 18 Oct 2008, 17:51
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page Previous  1, 2

< 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.