flat assembler
Message board for the users of flat assembler.

Index > Windows > The best start to a Windows program

Goto page 1, 2  Next
Author
Thread Post new topic Reply to topic
Mino



Joined: 14 Jan 2018
Posts: 163
Mino 30 Apr 2018, 15:24
(Re)Hello!
I was wondering what was the best way to write the beginning of a program. Let me explain,
in the assembler code given by my compiler, the main function (and any other function for that matter) gives this :
Code:
main:
        push    rbp
        mov     rbp, rsp
        mov     eax, 0
        pop     rbp
        ret
    

Except that in the examples given in the FASM file, the entry point does not define any "good manners", and goes directly into the heart of the program. Example from "DIALOG.ASM" :
Code:
start:

        invoke  GetModuleHandle,0
        invoke  DialogBoxParam, eax, 37, HWND_DESKTOP, DialogProc,0
        or      eax,eax
        jz      exit
        invoke  MessageBox, HWND_DESKTOP, message, caption, [flags]
        ...
    

I would also like to know two other things about registers:
1) What is the main difference between relative and absolute addressing?
2) What registers should I use as a stack pointer? I always used the general registers (eax, ecx, ...), except one person told me they weren't made for it (he doesn't know FASM), your opinion?

Thank you so much Very Happy

Edit by revolution: Changed the title to reflect the intent

_________________
The best way to predict the future is to invent it.
Post 30 Apr 2018, 15:24
View user's profile Send private message Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1228
Location: Belarus
DimonSoft 30 Apr 2018, 22:47
Mino wrote:
(Re)Hello!
I was wondering what was the best way to write the beginning of a program. Let me explain,
in the assembler code given by my compiler, the main function (and any other function for that matter) gives this :
Code:
main:
        push    rbp
        mov     rbp, rsp
        mov     eax, 0
        pop     rbp
        ret
    

Except that in the examples given in the FASM file, the entry point does not define any "good manners", and goes directly into the heart of the program. Example from "DIALOG.ASM" :
Code:
start:

        invoke  GetModuleHandle,0
        invoke  DialogBoxParam, eax, 37, HWND_DESKTOP, DialogProc,0
        or      eax,eax
        jz      exit
        invoke  MessageBox, HWND_DESKTOP, message, caption, [flags]
        ...
    

If we’re talking about HLL compiler (C/C++, I guess?), its main function is not the real entrypoint. In fact, Windows application entry point is generally hidden inside the library and what a programmer sses as main() function is in fact a function called by the library after a lot of initialization and simplification is done. In particular, this function gets several parameters on the stack and thus has to build a complete stack frame.

The real entry point (which you can see in FASM example) is just a function with no parameters and thus with no particular calling convention. In fact, you don’t even return from it since this might cause the process hang on certain Windows versions.

Mino wrote:
1) What is the main difference between relative and absolute addressing?

Well, in general relative addressing is… um… relative. It implies specifying an address relative to some place (say, current value of (E)IP if we talk about jumps). Absolute addressing means that the effective address is directly specified.

Mino wrote:
2) What registers should I use as a stack pointer? I always used the general registers (eax, ecx, ...), except one person told me they weren't made for it (he doesn't know FASM), your opinion?

ESP is used as a pointer to the top of the stack by all the stack instructions in x86. EBP is generally used as a pointer to a stack frame. I guess, the reason is that it used to imply stack segment when used for addressing. These days the rules are somewhat simpler: most OSes seem to setup all segments the same, SIB addressing may have addressing with EBP go to data segment in some cases.

Nothing prevents you from using any general-purpose register to store an address to a place in the stack. Although the two registers mentions above are the most suitable.
Post 30 Apr 2018, 22:47
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: 20339
Location: In your JS exploiting you and your system
revolution 01 May 2018, 02:32
Moving to Windows forum.
Post 01 May 2018, 02:32
View user's profile Send private message Visit poster's website Reply with quote
Mino



Joined: 14 Jan 2018
Posts: 163
Mino 01 May 2018, 12:00
Ok, thanks for your explanations:)
Actually, I'm writing a compiler (in C++, C and F#) that will use FASM as the target assembler language. I wanted to know what was the best way to compile the high level down, and see what was the best way to start a program, about registries and the entry point. So if you have anything to add about that, I'm listening Smile
I don't think I have anything else to add, and sorry for the forum Very Happy

_________________
The best way to predict the future is to invent it.
Post 01 May 2018, 12:00
View user's profile Send private message Reply with quote
vivik



Joined: 29 Oct 2016
Posts: 671
vivik 02 May 2018, 16:40
GetModuleHandle always returns 0x400000. Actually it returns the address of where the exe is loaded to, and I guess it's somewhere in the pe header. There is a neat pe format tutorial here http://win32assembly.programminghorizon.com/tutorials.html . I also have an exe with notes on what is important and what isn't somewhere, can try finding it if you need. Try opening your exe in ollydbg and opening "memory map". Your exe will be on 0x400000 address. Oh, it may be somewhere else if you are on 64bit windows.

DialogBoxParam, i don't know what it is, never seen it before. You probably don't need that.

MessageBox is actually either MessageBoxA or MessageBoxW. First is ascii text, second is widechar text. In ascii, one char takes 1 byte, in widechar - 2. Widechar is something like unicode.

MessageBoxA 0,"text1","text2",0 usually works fine.

You don't need ExitProcess at the end if you have only one thread, you can just use "ret". My program stopped exiting properly once I added directdraw in it, so there were 2 threads. Better have it just in case.

I have no idea how to make gui on windows, I'm trying to use directdraw and direct3d directly instead.
Post 02 May 2018, 16:40
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20339
Location: In your JS exploiting you and your system
revolution 02 May 2018, 16:48
vivik wrote:
GetModuleHandle always returns 0x400000. Actually it returns the address of where the exe is loaded to, and I guess it's somewhere in the pe header.
This is not guaranteed by the OS docs. It costs practically nothing to call the proper function.
vivik wrote:

You don't need ExitProcess at the end if you have only one thread, you can just use "ret".
This is also not guaranteed to work. It costs practically nothing to call the proper function.

While those two things above might be true for the current versions of Windows, there is nothing that says MS won't change the implementation in a future version.
Post 02 May 2018, 16:48
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8353
Location: Kraków, Poland
Tomasz Grysztar 02 May 2018, 17:15
revolution wrote:
vivik wrote:
You don't need ExitProcess at the end if you have only one thread, you can just use "ret".
This is also not guaranteed to work. It costs practically nothing to call the proper function.
While you are generally right here, I would still argue (as I did in the past) that since all threads are equal with regards to Windows API, and since it is documented that returning from a thread "function" is a valid way of terminating it, the return from the "main function" (that is the main thread) may be considered an officially documented and therefore reliable method.

Still, in case of any doubt it is better to play it safe. As it was rightly pointed out above - it costs practically nothing to call the appropriate function. And the source becomes more readable - a RET instruction may require, unnecessarily in this case, a little bit of mental effort to find out if it is intended to end the thread or just a subroutine.
Post 02 May 2018, 17:15
View user's profile Send private message Visit poster's website Reply with quote
vivik



Joined: 29 Oct 2016
Posts: 671
vivik 02 May 2018, 17:28
you guys are boring

By the way, Raymond Chen isn't THAT against using hardcoded module handle: https://blogs.msdn.microsoft.com/oldnewthing/20041025-00/?p=37483
Post 02 May 2018, 17:28
View user's profile Send private message Reply with quote
rugxulo



Joined: 09 Aug 2005
Posts: 2341
Location: Usono (aka, USA)
rugxulo 02 May 2018, 17:51
vivik wrote:

MessageBox is actually either MessageBoxA or MessageBoxW. First is ascii text, second is widechar text. In ascii, one char takes 1 byte, in widechar - 2. Widechar is something like unicode.


I really don't understand all the details here, but I think NT originally was UCS-2. So it wasn't really supporting full Unicode from the start, only a subset of it. I had thought I also heard that modern NT is UTF-16 (same as Java??), but (again) there are so many variations, complications, etc. that I don't honestly know. I haven't done any direct work or learning in that area, so I'm fairly clueless.
Post 02 May 2018, 17:51
View user's profile Send private message Visit poster's website Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1228
Location: Belarus
DimonSoft 02 May 2018, 22:17
revolution wrote:
While those two things above might be true for the current versions of Windows, there is nothing that says MS won't change the implementation in a future version.

Tomasz Grysztar wrote:
While you are generally right here, I would still argue (as I did in the past) that since all threads are equal with regards to Windows API, and since it is documented that returning from a thread "function" is a valid way of terminating it, the return from the "main function" (that is the main thread) may be considered an officially documented and therefore reliable method.

But the “main” thread might not be the only one in the process. I remember having problems with RET from WinMain even on WinXP for programs that called GetOpenFileName() function. In Win10 even a simple two-liner fails to terminate this way. Antivirus software, UI customizers and whatever the hell they put into the new Windows release might and does create threads in pretty much every process out there.
Post 02 May 2018, 22:17
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: 20339
Location: In your JS exploiting you and your system
revolution 03 May 2018, 00:21
DimonSoft wrote:
But the “main” thread might not be the only one in the process. I remember having problems with RET from WinMain even on WinXP for programs that called GetOpenFileName() function. In Win10 even a simple two-liner fails to terminate this way. Antivirus software, UI customizers and whatever the hell they put into the new Windows release might and does create threads in pretty much every process out there.
Yes. Windows is a multi-tasking OS. One can't know what all those other tasks are doing. Don't simply assume your task is all that is happening. It costs practically nothing to call the proper function. If someone finds that boring, well so be it. But having incorrect operation is also not fun when everyone complains your code is broken.
Post 03 May 2018, 00:21
View user's profile Send private message Visit poster's website Reply with quote
vivik



Joined: 29 Oct 2016
Posts: 671
vivik 03 May 2018, 04:37
revolution, you have a tendency of warning about "oh no it's scary" without bringing any facts about it.

Though those practices already byte me, most of pouet demos don't work without any kind of messagebox even.
Post 03 May 2018, 04:37
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20339
Location: In your JS exploiting you and your system
revolution 03 May 2018, 05:32
vivik wrote:
revolution, you have a tendency of warning about "oh no it's scary" without bringing any facts about it.
You can still take the "risky" route if you wish. But the docs don't support that usage, that is all. It might work for you. Just that you can't predict how long it will work for. Maybe forever, maybe until the next update. The "facts" here are the lack of official documentation to say it is no problem.
Post 03 May 2018, 05:32
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8353
Location: Kraków, Poland
Tomasz Grysztar 03 May 2018, 07:47
DimonSoft wrote:
But the “main” thread might not be the only one in the process. I remember having problems with RET from WinMain even on WinXP for programs that called GetOpenFileName() function. In Win10 even a simple two-liner fails to terminate this way. Antivirus software, UI customizers and whatever the hell they put into the new Windows release might and does create threads in pretty much every process out there.
Well, if there is another thread that still has some work to do, it would be nicer to let it finish first, wouldn't it? Wink And if the other thread misbehaves you end up exposing the problems/bugs there, which may be considered a good thing. Wink

Anyway, my point here was that RET is a valid and documented method of exiting a thread. The differences between ending a thread and ending a process are a different story altogether - you would end up with exactly the same problem if you used ExitThread instead of RET.
Post 03 May 2018, 07:47
View user's profile Send private message Visit poster's website Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4042
Location: vpcmpistri
bitRAKE 03 May 2018, 08:20
When exiting with RETN, I've had applications hang because of other threads - I think related to language features or accessibility? A bunch of extra DLLs were loaded into the process. Never did run it down because ExitProcess fixed it.
Post 03 May 2018, 08:20
View user's profile Send private message Visit poster's website Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1228
Location: Belarus
DimonSoft 03 May 2018, 11:23
Tomasz Grysztar wrote:
Well, if there is another thread that still has some work to do, it would be nicer to let it finish first, wouldn't it? Wink

Well, IMHO, the fact that such a thread is created by some third-party software inside my process and is left there behind my back is bad, no matter how useful the thread is for stuff I’m doing.

Anyway, I just tried to give another real-life example when the <perfectly valid main function termination since MS-DOS>™ doesn’t do what is generally expected, since vivik started discussing such stuff.
vivik wrote:
You don't need ExitProcess at the end if you have only one thread, you can just use "ret". My program stopped exiting properly once I added directdraw in it, so there were 2 threads. Better have it just in case.
Post 03 May 2018, 11:23
View user's profile Send private message Visit poster's website Reply with quote
fasmnewbie



Joined: 01 Mar 2011
Posts: 555
fasmnewbie 07 May 2018, 23:30
Mino wrote:
(Re)Hello!
I was wondering what was the best way to write the beginning of a program. Let me explain,
in the assembler code given by my compiler, the main function (and any other function for that matter) gives this :
Code:
main:
        push    rbp
        mov     rbp, rsp
        mov     eax, 0
        pop     rbp
        ret
    

Except that in the examples given in the FASM file, the entry point does not define any "good manners", and goes directly into the heart of the program. Example from "DIALOG.ASM" :
Code:
start:

        invoke  GetModuleHandle,0
        invoke  DialogBoxParam, eax, 37, HWND_DESKTOP, DialogProc,0
        or      eax,eax
        jz      exit
        invoke  MessageBox, HWND_DESKTOP, message, caption, [flags]
        ...
    


The first program is adhering to standard C structure. In C, almost everything is a function, even main function itself. The basic idea behind it -- a function, whatever that is, returns with a RET instruction. This is guaranteed by C, to ensure portability of C's binaries across multiple platforms. C has its own way dealing with program's exit. The return address found at C's TOS points to an area where C hosts its own exit sequence, at least along the MingW toolchain.

If you debugged a C program both on Win64 and Linux64, you'll see that they both would produce similar "main" structure at the binary level. You won't see ExitProcess or syscall coming out from main because according to C's design philosophy, main is a function, and a function should use RET, just like any other functions.
Post 07 May 2018, 23:30
View user's profile Send private message Visit poster's website Reply with quote
Furs



Joined: 04 Mar 2016
Posts: 2503
Furs 07 May 2018, 23:38
fasmnewbie wrote:
The first program is adhering to standard C structure. In C, almost everything is a function, even main function itself. The basic idea behind it -- a function, whatever that is, returns with a RET instruction. This is guaranteed by C, to ensure portability of C's binaries across multiple platforms. C has its own way dealing with program's exit. The return address found at C's TOS points to an area where C hosts its own exit sequence, at least along the MingW toolchain.

If you debugged a C program both on Win64 and Linux64, you'll see that they both would produce similar "main" structure at the binary level. You won't see ExitProcess or syscall coming out from main because according to C's design philosophy, main is a function, and a function should use RET, just like any other functions.
main is also not the "start" of a Windows program. Not even close. In fact, unless you link with the C runtime and specifically export the main function, your code has nothing to do with C's main.

But the C runtime eventually has a "start" (hijacks the entry point) and that's not main.

You can think of main as a callback that the C runtime expects you to export. A callback that's just like any other function, yes, but it's definitely not the start of a Windows program.
Post 07 May 2018, 23:38
View user's profile Send private message Reply with quote
fasmnewbie



Joined: 01 Mar 2011
Posts: 555
fasmnewbie 07 May 2018, 23:46
C is not a "Windows" program. It's a high-level language that should appear / behave the same across platforms. Whatever happens behind the scene should not be of concern to users / programmers because they are platform-specific by then. It's the same with C's exit sequence. Whatever happens past the RET is ignorable.
Post 07 May 2018, 23:46
View user's profile Send private message Visit poster's website Reply with quote
fasmnewbie



Joined: 01 Mar 2011
Posts: 555
fasmnewbie 08 May 2018, 00:05
With BASELIB, one can check what happens behind the scene of C's return sequence. Pay particular attention to the return address. And using "memview" we get to see what exactly C's return sequence is...
Code:
;-----------------------------------
; fasm this.asm
; gcc -m32 this.obj sbase3.dll -o this.exe
;-----------------------------------
        format MS COFF
        public _WinMain@16
        extrn _stackview
        extrn _memview

        section '.data' writeable
msg     db 'Hello World',0ah,0

        section '.text' executable
_WinMain@16:
        push    ebp             ;C standard prologue
        mov     ebp,esp

        push    5               ;view main's stack
        call    _stackview

        mov     eax,[esp+4]
        push    eax             
        push    100
        call    _memview        ;what C is hiding at that return address

        pop     ebp
        ret                     ; C's standard return    


Output

Code:
00562954 |0022FEA8
00000000 |0022FEA4
00400000 |0022FEA0
00401D6B |0022FE9C  ==> C's return address
0022FF18 |0022FE98
5B59F4658D10EC83 |00401D6B| 0|0  ==> C's return sequence at that address
B48DC3FC618D5D5E |00401D73| 8|8
74223C0000000026 |00401D7B| 10|16
8403B60F01C3830A |00401D83| 18|24
C0940F223CF275C0 |00401D8B| 20|32
3C86EBC301C0B60F |00401D93| 28|40
E5895595EB897409 |00401D9B| 30|48
FFFFF535E818EC83 |00401DA3| 38|56
E8004013702404C7 |00401DAB| 40|64
9090C3C9FFFFF509 |00401DB3| 48|72
FFFFFF9090909090 |00401DBB| 50|80
00000000401DA0FF |00401DC3| 58|88
        FFFFFF00 |00401DCB| 60|96    


So the question, is main's RET safe? Yes it is. It is the standard way for C to return control to the OS because it has it's own well-defined return sequence. The examples above is for 32-bit C, using GCC as the linker.
Post 08 May 2018, 00:05
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:  
Goto page 1, 2  Next

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