flat assembler
Message board for the users of flat assembler.

Index > Windows > "Proc...endp" and "stdcall" makes life easier

Author
Thread Post new topic Reply to topic
Mat Quasar



Joined: 15 Dec 2024
Posts: 87
Mat Quasar 25 Jan 2025, 11:40
Out of curiousity, I use disassembler to see how "proc...endp" and "stdcall" translated in raw Assembly. And actually I think "proc...endp" makes maintaining stack frame easier.

This is my finding:

Code:
proc loc_abc uses ebx esi edi, param
  locals
        variable db 1024 dup (?)
  endl
   mov eax, [param]
   mov ebx. [variable]
    ret
endp    


...is the same as...

Code:
loc_abc:
    push ebp          ;--+
    mov  ebp, esp     ;   |
    sub  esp, 1024    ;   +--->   or "enter 1024,0"  -- but slow
    push ebx
    push esi
    push edi

    mov  eax, [ebp+8]    ;+8 because 4 bytes is return address?
    mov  ebx, [ebp-1024]    
 
    pop edi
    pop esi
    pop ebx
    leave             ;or "mov esp,ebp" & "pop ebp" 
    retn 4            ;clear the 4-byte "param" off stack
    


And some more:

Code:
    stdcall loc_abc, ebx
    


...is equivalent to...

Code:
    push ebx
    call loc_abc    
Post 25 Jan 2025, 11:40
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20669
Location: In your JS exploiting you and your system
revolution 25 Jan 2025, 12:25
Mat Quasar wrote:
Out of curiousity, I use disassembler to see ...
They are macros in fasm, and native in MASM.

So for fasm you can also examine the macros to discover the internal details of everything they can do and how they work.
Post 25 Jan 2025, 12:25
View user's profile Send private message Visit poster's website Reply with quote
Mat Quasar



Joined: 15 Dec 2024
Posts: 87
Mat Quasar 25 Jan 2025, 12:55
Thanks @revolution. So far I have examined "invoke", "cinvoke", "fastcall", "stdcall", "locals" and "proc...endp".

Even the "library" and "import" keyword in PE ".idata" section are also macro, I think.

Once I know how it is translated to raw Assembly, I am more happier to use the macros.
Post 25 Jan 2025, 12:55
View user's profile Send private message Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1717
Location: Toronto, Canada
AsmGuru62 25 Jan 2025, 13:53
These macro also have an awesome feature.
If you have the proc/endp declared in the source, but the name of the 'proc' is not mentioned anywhere else,
then the EXE file FASM produces will not have the code between proc/endp included!
It is like HLL Linker option: "Remove unreferenced code". Very cool!
Say, you have a file with 50 functions and you include it and call only a couple of functions.
Only their code will be inside the EXE file.
In x64 code proc/endp also balance the stack on 16 byte frame automatically, even if you declare locals.
Post 25 Jan 2025, 13:53
View user's profile Send private message Send e-mail Reply with quote
Mat Quasar



Joined: 15 Dec 2024
Posts: 87
Mat Quasar 25 Jan 2025, 14:43
Good to know, @AsmGuru62.
Post 25 Jan 2025, 14:43
View user's profile Send private message Reply with quote
Mat Qua sar



Joined: 13 Jun 2025
Posts: 18
Mat Qua sar 18 Jun 2025, 16:00
I created an example that replace PROC/ENDP with code like I shown in post #1:

It is probably my preliminary Win32 dialog/form for my simple database program (that I planned to do):

Code:
format PE GUI 4.0
include 'win32a.inc'

IDD_MAIN = 100
IDC_BOX = 101
IDC_ADD = 102
IDC_UPDATE = 103
IDC_REMOVE = 104

section '.code' code readable executable

entry $

    invoke  GetModuleHandle,0
    invoke  DialogBoxParam,eax,IDD_MAIN,HWND_DESKTOP,DialogProc,0
    invoke  ExitProcess,0

DialogProc:
    push ebp
    mov  ebp, esp
    push ebx
    push esi
    push edi

    cmp     dword [ebp+12],WM_INITDIALOG
    je      .wminitdialog
    cmp     dword [ebp+12],WM_COMMAND
    je      .wmcommand
    cmp     dword [ebp+12],WM_CLOSE
    je      .wmclose
    xor     eax,eax
    jmp     .finish
.wminitdialog:
    jmp     .processed
.wmcommand:
    cmp     dword [ebp+16],BN_CLICKED shl 16 + IDC_ADD
    jne     .processed
    invoke  MessageBoxA, HWND_DESKTOP, _caption, _title, 0x40
    jmp     .processed
.wmclose:
    invoke  EndDialog,[ebp+8],0
.processed:
    mov     eax,1
.finish:
    pop edi
    pop esi
    pop ebx
    leave
    retn 16

_caption    db    'Yay! ADD Button clicked',0
_title      db    'dbform',0

section '.idata' import readable

    library   kernel32,'kernel32.DLL', user32,'user32.DLL'
    include  'api\kernel32.inc'
    include  'api\user32.inc'

section '.rsrc' resource data readable

     directory  RT_DIALOG, dialogs

     resource   dialogs, IDD_MAIN, LANG_ENGLISH + SUBLANG_DEFAULT, mainform

     dialog  mainform,'dbform',0,0,300,200, DS_CENTER + WS_CAPTION + WS_SYSMENU
     dialogitem 'Button','Main',-1,10,10,280,180,WS_VISIBLE + BS_GROUPBOX
     dialogitem 'Button','Add',IDC_ADD,150,30,70,15,WS_VISIBLE + BS_DEFPUSHBUTTON
     dialogitem 'Button','Update',IDC_UPDATE,150,50,70,15,WS_VISIBLE
     dialogitem 'Button','Remove',IDC_REMOVE,150,70,70,15,WS_VISIBLE

enddialog    


Description: Demo screen
Filesize: 3.65 KB
Viewed: 131 Time(s)

abc.PNG


Post 18 Jun 2025, 16:00
View user's profile Send private message Reply with quote
Core i7



Joined: 14 Nov 2024
Posts: 104
Location: Socket on motherboard
Core i7 19 Jun 2025, 04:29
Mat Qua sar wrote:
I created an example that replace PROC/ENDP with code like I shown in post #1:

I understand that this is an experiment,
but you must agree that it is not convenient to control arguments this way (especially when there are many of them). For example, you need to remember that [ebp+24] is the fifth argument from the beginning, etc. It is much more convenient to access arguments by their aliases in the form of text strings, which is implemented in proc/endp macros. This also applies to the prologue/epilogue of a procedure.
Post 19 Jun 2025, 04:29
View user's profile Send private message Reply with quote
Mat Qua sar



Joined: 13 Jun 2025
Posts: 18
Mat Qua sar 19 Jun 2025, 08:16
Core i7 wrote:
...
but you must agree that it is not convenient to control arguments this way (especially when there are many of them). ...This also applies to the prologue/epilogue of a procedure.


Agreed. It was just fun to use primitive way to make it work like PROC/ENDP macro. Smile
Post 19 Jun 2025, 08:16
View user's profile Send private message Reply with quote
Core i7



Joined: 14 Nov 2024
Posts: 104
Location: Socket on motherboard
Core i7 19 Jun 2025, 08:29
yes, this is exactly how any debugger displays the entry into a procedure


Description:
Filesize: 11.59 KB
Viewed: 52 Time(s)

proc.png


Post 19 Jun 2025, 08:29
View user's profile Send private message Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1717
Location: Toronto, Canada
AsmGuru62 19 Jun 2025, 16:24
I once tried to 'relieve' EBP of its duties in PROC/ENDP principle.
So both the parameters and locals would be accessed by ESP.
It was a nightmare stuff!
Every function had a matching structure for locals, and parameters, and preserving registers.
Like this one:
Code:
struct LocVarsProc1
    ;
    ; the area to preserve used registers
    ;
    used_ebp    dd ?
    used_esi    dd ?
    used_edi    dd ?
    ;
    ; the area for locals (short fields first to save encoding length)
    ;
    handle      dd ?
    path        rb 256
    ;
    ; Return Address Location
    ;
    RetAddr     dd ?
    ;
    ; Parameters (named properly for each function)
    ;
    Param1      dd ?
    Param2      dd ?
    Param3      dd ?
    Param4      dd ?
ends
    

Function itself:
Code:
align 16
MyInsaneProcedure:
    sub     esp, LocVarsProc1.RetAddr
    ;
    ; preserve registers
    ;
    mov     [esp + LocVarsProc1.used_ebp], ebp
    mov     [esp + LocVarsProc1.used_esi], esi
    mov     [esp + LocVarsProc1.used_edi], edi
    ;
    ; use EBP, ESI, EDI as you wish
    ;
    lea     esi, [esp + LocVarsProc1.path]
    mov     ebp, [esp + LocVarsProc1.Param4]
    ;
    ; etc.
    ;
        ; if you use PUSH here, do not access locals until POP is done.
    ;
    ; restore registers
    ;
    mov     edi, [esp + LocVarsProc1.used_edi]
    mov     esi, [esp + LocVarsProc1.used_esi]
    mov     ebp, [esp + LocVarsProc1.used_ebp]
    ;
    ; cleanup and return
    ;
    add     esp, LocVarsProc1.RetAddr
    ret     4*4
    

And the call:
Code:
push    4 3 2 1
call    MyInsaneProcedure
    

Neat!
Of course, using PROC/ENDP is a small price to pay to just leave EBP alone.
The bloat of code is obvious.
To save registers using PUSH/POP takes one byte only vs the few bytes for each MOV into/from structure.
I dropped that horrible idea soon enough.
Post 19 Jun 2025, 16:24
View user's profile Send private message Send e-mail 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-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.