----------------------------- Win32 Programming in Assembly ----------------------------- by: device ------------ Introduction ------------ Welcome to windows 32 bit assembly programming. If you want to take part in the new trend of win16/32 viruses currently being made, but don't have a clue about how to program windows at the assembly level, than this is the article for you. This article will assume that you know pretty much nothing about windows at this level. If you've ever programmed a windows program in C(not C++), than this will be a lot easier for you, as the structural as- pects of windows programs are already familiar to you. If you've programmed with C++(MFC), than you're better off just forgetting everything you've done with that, because it will just hurt you in the end, as it shields you from the way windows applications really work. As a conclusion, although there are no prerequisites for this article, it would greatly help if you've made windows programs in languages like C, and it is pretty much mandatory that you be familiar with assembly language itself, because it won't be covered at all, unless there is something done that is not usually seen in common programming. Although the structures of win16 and win32 programs are similar, this article will focus on win32. As an assembler, I use TASM 5.0. That's what the source code to this article will assemble with. I don't know if it'll assemble with anything else, so you may need to make some modifications before trying to assemble it with another assembler(like MASM). Of course you don't need to know how to build a full win32 program in assembly to make a virus, but, it does help to know how to do it. To demonstrate the basic structure of a win32 program, I've made a small program called win32app. It just displays a window, and when you click the mouse on the window, it gives you a message. It's nothing amazing, but it's simple enough to demonstrate windows 32 bit programming in assembly. The bad thing that's happening is that the high level application development systems(like VC++,MFC,VB) are giving windows a bad name, because of the big, bloated, slow, and dll hungry programs that they create. But, once you make your first windows program completely in assembly(with notepad!) your view about windows programming in general may change. You'll see that windows programming is not as hard as you may have thought it was. And, there are benefits too. You have access to all of the memory on the computer (plus more!), and everything is 32 bit in a flat memory model, so you'll never have to worry about segments! You'll also see that windows programs can be just as fast as dos programs(graphics are another story, though), when written in assembly. This article only goes over the basics, so if you decide that you like win32 programming in assembly, you can move on to the next article in this series which shows you how to write and explains the source code to a win32 file encrypter program written in assembly. This tutorial will demonstrate some of the more advanced features of windows programming, such as file input/output, using dialog boxes, using common controls, and how to structure your program to allow background tasks to run. If, when finished reading this, you become interested in making regular windows applications in assembly(or any language, for that matter), you should get a copy of the win32 SDK help files(a one of those 2000 page books), because there are hundreds and hundreds of API calls available. But for now, lets start with the basics. ------------------------ Windows message handling ------------------------ OK, this is the part of windows programs that most separates them from dos programs. First, let's review some of the basic differences between how dos and windows programs are run. When a dos program is run(in true real mode), the operating system passes control to the program, and program runs from top to bottom. The dos program can do whatever it wants, and can keep control of the computer for as long as it wants. It has complete control over the processor and the resources of the computer. This is good for some programs(like games), but when it comes to running more than one program at a time, giving this kind of freedom to a program wouldn't work out too well. The general idea behind a multitasking OS is that the programs should not be able to interfere with any other running programs in any way(unless the programs are specifically made to share data). They should also not have the freedom to communicate directly with hardware, as it needs to be shared with the other applications. The Intel processors provide hardware to implement some of these things, and windows 9x/2K/NT take advantage of it. The way that windows multitasks is by preemptive time slicing. Every few milliseconds, control is taken away from one task and given to another. There are many things that can effect this and throw the timer off slightly, but generally it is done every few milliseconds. Windows also provides an API for programs to use. This API is used to do pretty much anything that a windows program could ever want to do, including displaying windows, drawing on the screen, communicating with hardware, and file input/output. In fact, windows applications are forced to use this API for the most part. This ensures that most low level operations are performed by trusted software, so a crashed program is handled easily as it doesn't usually leave the hardware in an unrecoverable state. Technically, programs do not have to use the API(there are ways around it), but most do. Another major difference between windows programs and dos programs is that windows forces its programs to use what's called a message loop. A message loop is a software loop coded in each windows program that continuously polls windows for new messages. A message in windows is something that the operating system sends to a program to notify it of an event that occurred that may have an affect on the program. Examples of windows messages are mouse clicks, window changing position, timer ticks, etc. This works out well, because due to the nature of windows programs(graphical), it is more natural to have the operating system tell the program what the user does, instead of forcing the program to check manually for such events as button presses, or force the program to continually poll the mouse. Here's what actually happens with the message loop: When windows receives messages, it puts them in a message queue. A queue is a form of a linked list in which new links are added to the end, and old links are popped off the top. The program then checks(via the API) that queue to see if any new messages have been put in. When a message is retrieved, it is removed off of the queue, some intermediate operations may be performed on it, and it is sent back to windows. Windows will then take that message and call one of the program's message handling functions with the message and any extra data as parameters to that function. If all that sounds confusing, don't worry about it. You don't have to understand the way windows processes messages internally. The important thing to know about retrieving messages, however, is that in your program you must have a special function called a CALL BACK FUNCTION. A call back function is a function that you do not call directly in your program, but instead gets called by windows. When your program receives a message, windows will eventually call your call back function with a description of the message so that the program can handle it. For example, you might want to have the computer make a beep sound every time the mouse button is pressed. The way you would do that is by putting the code that beeps in the call back function. That way, when the mouse is clicked, windows will notify your program via the call back function. The call back function will then check to see what kind of message it is, and act accordingly(in this case, sound a beep if it's the mouse click message). Most programs have no need for most of the messages that windows can give. As you'll see, the win32app program in this article looks only for 2 messages: one to indicate a mouse click in the window(which will then signal the displaying of a window), and a message that indicates that the user wishes to quit the application. With all that said, let's discuss how these message loops work at a more technical level and see where and how they fit into a program. Keep in mind that large programs may have many message loops (usually one per window), but, the example program uses only one. First off, when the program is first run, it does some initialization, which we'll get to later. For now just know that the initialization may include initializing data, reading disk files, and almost always displaying one or more windows. Once the initialization is complete, the program has usually displayed a window, and ends up in a message loop waiting for messages. This is where the program will stay(in the message loop) until it terminates. This is what most message loops look like(in pseudocode): 01: Get_Message(msg) 02: If (Message == QUIT) 03: goto 07 04: Translate_Message(msg) 05: Dispatch_Message(msg) 06: goto 1 07: quit Let's go over these steps in detail: 1. Line 1 is a call to the function that retrieves the messages off of the message queue. When the program calls this function, it will not return until there is a message(it doesn't have to be this way, but many times it is). When there is a message, the function returns with the message in the MSG variable. 4. After the message is received, we check to see if the message is a quit message(meaning that the user wishes to exit the program). If it is a quit message, we exit the loop. 2. If the message is not a quit message, we move to line 4, where the message gets sent to the Translate_Message function. This function performs some intermediate operations on the message, but this isn't very important right now for out purposes, so don't worry about this. 3. The message then comes to line 5, where the Dispatch_Message function is called. By calling this function, the program is sending the new message back to windows. Windows will then take this message and send it back to the program again! But this time, when windows sends the message back, it will pass it as a parameter to the windows' call back function, which is used to handle the messages. Some examples of messages are mouse clicks, button presses, and close window. 4. We then repeat the process over again Before we move on, let's step through a typical message. Let's say it's a mouse click message, meaning that the user has clicked the mouse button on the program's window. First, the user clicks the mouse. Windows realizes this and put a message onto the program's message queue a long with some other data such as the mouse position at the time of the click. The program then calls Get_Message(if it didn't already), and retrieves the message that the user has clicked the mouse button on the window. The message then gets passed through the Translate_Message function, then gets send back to windows via Dispatch_Message. Windows then takes that message, and sends it(as a parameter) to the program's message handling function where it is processed. By processed, I mean that the program can do whatever it needs to do. The program in this article will display a message box when it receives this particular message, but it's not required that you do anything. As a matter of fact, if the program doesn't want or need to handle a certain message, it can send it back to windows and windows will handle it. If all this sending of messages back and forth is confusing, don't worry. It takes some time to get used to. Plus, it's not very critical that you know this procedure inside and out to make a basic windows program. ------------------------------- Creating and displaying windows ------------------------------- Since interacting with the user is the main purpose of windows programs, and windows are pretty much the only way to interact with the user, you'll want to know how to display windows. Here's what we have to do to display a window: 1. Register our Window class 2. Create the window from the window class 3. Show the window 4. Enter in the message loop, and stay here until the user wants to exit the program. 5. exit The first step is called registering your window class. What this is, is that your telling windows to create a new CLASS of window(or new type). Along with that you give windows a description of the properties of that window, as well as a name for it. That way, when ever you want to use that type of window in the future, you need only refer to it by name and windows will automatically recall all of it's properties(such as its icon, background color, and message handler), and create it. The second step is to create the window. This step is the easiest, because all you do is tell windows to create a window of the type that you just described when you registered the class in the previous step. And the way you do that is just by giving windows the name of the class. Along with this also comes properties that you would like the window to have, such as whether you want it to have a system menu, title bar, how big it should be, etc. The third step is optional, but is usually done. It is to show the window. This is even easier, because you just tell windows to make the window appear, and it'll take care of the rest for you. The fourth step is the message loop. Here, the program will stay until the user wishes to exit. As you read above, a message loop is just a software loop that continually polls windows for new messages, or events that happen that the program may be interested in handling. If we get to the fifth step, that means that the user wishes to quit the program, and the message loop has received a quit message. So, we quit. The next few sections will go into these steps in more detail and show you how to implement them. --------------- Technical Stuff --------------- Before we dive into writing assembly code implementations of what we've been talking about, let's get acquainted with some of the details of assembling a windows program using tasm 5.0. The first things that you should put at the top of your assembly file are these lines: .386 locals .model flat,stdcall The .386 tells tasm that we'll be using 32 bit code. We need this because all win32 programs are 32 bit. The LOCALS directive tells tasm that we want to enable local symbols. Local symbols in tasm are variables or labels that are inside procedures that begin with '@@'. That way, by making them local, you can use the save label/variable names in more than one function, which is useful. The ".model flat,stdcall" tells turbo assembler to do two things: first, the program will be assembled using a flat memory model, which all win32 programs run in. The second tells turbo assembler to use the standard calling convention when calling functions. Normally this wouldn't be needed in assembly programs, because all parameters to functions are usually pushed manually. But, tasm has some high level features that allow you to call functions much like you would call them in a language like C, which makes code about 50 times easier to read. The STDCALL directive tells tasm that parameters to functions should be pushed from right to left, and that the functions themselves will be cleaning up the stack afterwards. Also at the top of the program, you'll need to declarations to all the API functions that you'll be using. You must declare them with the EXTRN keyword. See the example program to see this. Now, we'll make sample function to show you tasm's high level features: #1 Foo proc stdcall, @@param1:dword, @@param2:dword #2 ... #3 Foo endp In line one, we first have the function name(Foo), followed by PROC, which tells tasm that it's a function. Then we have STDCALL, which means that this function will be called using the standard calling convention. After that, comes the parameters to the function. Each parameter is in two parts: @@name:size The NAME is the name of that variable, and the SIZE is the size of that variable. This could be BYTE, WORD, DWORD, etc., but the most common in win32 assembly is DWORD. Also notice the @@ in front of the names. This tells tasm to make these symbols local to only this function, so that if we wanted to we could make another function with the same parameters names. Now, let's say that we wanted to call the function Foo from our code. Using the tasm high level features, it's as easy as this: call Foo, eax, ebx It's divided into 3 parts: the first part is the instruction(call). The second part is the function name followed by a comma, and the 3rd section contains the parameters, each separated by a comma. Note that the parameters can be registers, immediate values, or even memory references. This saves a lot of time! One other thing that you need at the top of ever win32 program is some kind of header files that contains all of the constants used by windows. There are thousands, so the bigger the header file, the better. The header file that I'll be using for this tutorial is WIN32.INC, which is an include file that comes with tasm. It doesn't have all of the constants, but it has enough. To include a header file, do this: include win32.inc Another small detail, is that before you can make your final executable, you have to link it with a windows linker. A windows linker requires something called a DEFINITION file(*.def), which gives it some information about the program to be linked. You DEF files will look pretty much the same for all of your windows programs. Here is the one that I use for this tutorial: NAME W32SHELL DESCRIPTION 'Win32 Shell' EXETYPE WINDOWS STUB 'WINSTUB.EXE' CODE PRELOAD MOVEABLE DATA PRELOAD MOVEABLE MULTIPLE HEAPSIZE 65536 STACKSIZE 65536 EXPORTS WindowProc There are only a few things that you may want to look at here. First, the NAME field is the name of the module. This can be whatever you want. Second, the DESCRIPTION field is a one sentence description of your program. Something else that's interesting is the STUB field, which defines the MS-DOS program that will be linked in to the top of your windows program, which will be executed if the user tries to run your windows program in DOS. The stub is the program that displays the message "This program must be run in Microsoft Windows". But, you can replace this(ordinary dos program) with anything that you want. The EXPORTS field is another field that you'll have to worry about. This is the field that will define all of the functions that windows will call. Among these are you call back functions(or message handling functions). As you can see, I have only one call back function here, because I have only to handle messages from the 1 single window in the program. But, if you had more than one window, and therefore more than one callback function, you would have too add it to the EXPORTS field of the definition file. There are other files that are commonly linked into windows programs. These are called RESOURCE SCRIPTS. A resource script is a text file(made by you or by a resource generator program), that describes all of the programs resources. The resources of a program include: dialog boxes, buttons, menus, images, icons, cursors, and other common controls(like edit boxes). The resource file pretty much describes everything that will be interacting with the user. But, in this tutorial we won't be making a resource file, because we don't have any dialog boxes or common controls to deal with. Another one of tasm's high level features is the way it handles structures. Let's say you've defined a structure like this: test struc field1 dd ? field2 dd ? field3 dd ? test ends If you have this structure, you can create a instance of that structure like this: my_struc test <> That will create a variable(my_struc) that will be a structure of type TEST. You can access members of the structure like this: mov [my_struc.field1],eax The last thing that we have to worry about is how to assemble/link the program after all of the code is finished. Here is how you do that, assuming that the assembly file is called TEST32.ASM: tasm32 /ml /m test32 tlink32 /c /Tpe /aa test32,,,import32,test32 The first line assembles the test32.asm file into an object file. the /ml switch tells tasm to turn on case sensitivity for all symbols. The /m switch tells tasm to allow multiple passes to resolve forward references. The next line will link the object file, the import library, and the definition file into an executable. The /c tells tlink to use case sensitivity, and the /Tpe and /aa switches tell tlink what type of application will be generated. The first test32 is the object file. The import32 is an import library that must be linked to win32 programs to allow access to the API functions. I use the one that comes with tasm 5.0, but you may use which ever you would like. The last test32 is the definition file. Out of all this will come the final executable. Now that we understand those details, let's start coding the windows app. ------------------------ Getting the model handle ------------------------ The first step that must be performed in a win32 application is the retrieval(via an API function) of the model handle. This handle is used in various other functions(such as creating windows). Luckily, it's very easy. All we do is this: call GetModuleHandleA, NULL ; return the model handle in EAX mov [mod_handle],eax ; store it in a variable The first line calls the GetModuleHandle API function to retrieve the model handle, which will be returned in EAX(like always). Then, it is simply stored in a global variable for later use. ---------------------- Registering the Window ---------------------- Before we show or even create our window, we have to register its window class. This simply describes the window to windows, giving it some of its properties(including icon, background color, and NAME). Then, to refer to or to create that window in the future, all we need to do is refer to the name of the class, and windows will automatically call up all of it's properties. So how do we register a windows class? First we have to create a window class structure to fill out. We will fill out this window class structure with the information about our window, then pass it to windows. (NOTE: the following symbols may be different, depending on which header file you chose): Anyway, the window class structure looks like this: WNDCLASS struc clsStyle dd ? ; class style clsLpfnWndProc dd ? ; pointer to message handling function clsCbClsExtra dd ? ; set to zero clsCbWndExtra dd ? ; set to zero clsHInstance dd ? ; instance handle of application clsHIcon dd ? ; class icon handle(icon to be used for window) clsHCursor dd ? ; class cursor handle(cursor to be used for window) clsHbrBackground dd ? ; class background brush(background color) clsLpszMenuName dd ? ; menu name(if any) clsLpszClassName dd ? ; pointer to class name WNDCLASS ends The clsStyle field holds various properties and styles about the window. The clsLpfnWndProc field is a pointer to a message handler function for this window, which we'll create later. The clsHInstance is the module handle that we retrieved earlier. The clsHbrBackground field is a BRUSH that tells windows the background color of the window. Brushes are very powerful in windows. They allow you to specify colors or patterns to fill a surface with, but we're not going to be covering them here, just to keep it simple. The clsLpszMenuname is the name of the menu that will be shown at the top of the window. Our window will not have a menu, though. The clsLpszClassName is the name of the class that we would like this window to have, and with which we will refer to this window in the future. Next, we create a instance of the WNDCLASS structure. This would normally be a global variable put in the data segment. The following will create a variable named 'wclss' of type WNDCLASS: wclss WNDCLASS <> Now we have to fill out that structure with the details of our window. That is done like this: #1 mov wclss.clsStyle,CS_HREDRAW or CS_VREDRAW #2 mov wclss.clsLpfnWndProc,offset WindowProc #3 mov wclss.clsCbClsExtra,0 #4 mov wclss.clsCbWndExtra,0 #5 mov eax,[inst] #6 mov wclss.clsHInstance,eax #7 call LoadIconA,NULL,IDI_APPLICATION #8 mov wclss.clsHIcon,eax #9 call LoadCursorA,NULL,IDC_ARROW #10 mov wclss.clsHCursor,eax #11 mov wclss.clsHbrBackground,COLOR_BACKGROUND #12 mov wclss.clsLpszMenuName,NULL #13 mov wclss.clsLpszClassName,offset classname #14 call RegisterClassA,offset wclss Once again, for a complete and detailed listing of all of these functions, get the SDK. I'll discuss some here, but you may want more information if you plan to make more advanced windows programs. Line 1 sets the style of the window class. CS_HREDRAW and CS_VREDRAW tell windows to redraw the window when it is resized horizontally or vertically. You don't have to worry about that, just put it there. Line 2 is important. It gives windows the address of our message handler. Again, the message handler is nothing more than a function that windows will call to notify your program of any messages that might affect it. Lines 5 and 6 take the module handle of the program and store it in the WNDCLASS structure. This is just something that windows needs. Line 7 loads the standard application icon to use with this window, then on line 8 it is stored in the structure. Line 9 loads the standard mouse cursor for use with this window and line 10 stores it in the structure. As I said above, line 11 uses a brush to set the background color, but we're not going to cover brushes here. In line 13 we give windows the window class name that we would like the window to have. And finally, in line 14, we register the class, passing the address of out WNDCLASS structure to the RegisterClass function. ------------------- Creating the Window ------------------- After we've registered the window class, we must create the window. Luckily, this is very easy. We use the CreateWindowEx function, as follows: call CreateWindowExA, 0, ; extended window style(none) offset classname, ; class name of window to create offset windowtitle, ; title of window WS_BORDER or WS_SYSMENU; style 200, ; x position of window on screen 200, ; y position of window on screen 640, ; width in pixels of window 480, ; height in pixels of window 0, ; handle to parent window(none) 0, ; menu handle(none) instance, ; handle of application(module handle) 0 ; pointer to window creation data(none) mov hmainwnd,eax ; store returned window handle The parameters of this function are pretty much self explanatory. The class name is the name of the class that we have just registered. The window title is just an ASCIIZ string that will be the title of the window. The style field describes the way that a window will look. Again, I highly recommend getting the win32 SDK or help files, because there are many styles that window can have, and I can't list everything here. After we've registered the class, the window handle is returned in EAX, which is stored in a global variable for later reference. ------------------ Showing the Window ------------------ After we've created the window, we have to display it so that the user can see it. This is very easy: call ShowWindow,hmainwnd,SW_SHOWNORMAL The function is ShowWindow, which can actually either show or hide a window. In this case, we're showing it. The first parameter is the handle to the window that was returned by CreateWindowEx. The 2nd parameter is a constant that tells windows how to show it(i.e. minimized, maximized, normal). When this function returns, however, the window will not yet be displayed. There is one more step before out window is displayed: The Message Loop. ---------------- The Message Loop ---------------- Before we enter into the message loop, we have to create the message structure. This message structure will hold a single message that is received from windows. To do that, we do this: message MSGSTRUCT <> This creates a variable called 'message' that is of type MSGSTRUCT. This structure will hold a single message received from windows. This would usually be stored as a global variable. Once we've created the message structure, we're ready to enter into the loop. If you're familiar with the psuedocode of the message loop presented earlier, the code will look pretty straight forward. Here it is: #1 msg_loop: #2 call GetMessageA,offset message,0,0,0 #3 cmp ax,0 #4 je end_loop #5 call TranslateMessage,offset message #6 call DispatchMessageA,offset message #7 jmp msg_loop #8 end_loop: #9 call ExitProcess,message.msWPARAM In line two, we call the GetMessage function will wait for a message to be sent from windows. When a message comes, it will return with the message in the MESSAGE structure. In lines 3 and 4 we check to see if the value returned from the GetMessage function is 0. If it is, that means that the user wants to quit, so we should exit the loop. We exit the loop at line 8, and on line 9 we call the ExitProcess API function that will terminate our program. Lines 5 calls the TranslateMessage function to perform some intermediate operations on the message, and then line 6 sends it back to windows so that windows can call our message handling function. But how do you create the message handling function that we've been talking about for all this time? ------------------- The Message Handler ------------------- The message handler for a window is nothing more than a normal function that is called by windows. It takes special parameters that are given to it by windows to describe the message. Here is the skeleton for the function: WindowProc proc stdcall, @@hwnd:dword,@@wmsg:dword,@@wparam:dword,@@lparam:dword ... WindowProc endp The first parameter to this function is the handle to the window that generated this message. This is used for various purposes, like drawing to the window. The next parameter is the actual message itself, so that we can figure out what the message is, so that we can act accordingly. Note that the message parameter is not a message structure, but instead just a dword. The next two parameters carry extra data that helps to describe the message. For instance, on a mouse click message, they hold the position and button status of the mouse. Now what are we going to put in the message handling function? Well, the message handling function has to do a few things: first, it has to figure out which message was sent. Then, it will decide whether that message is important. If it isn't, it tell windows to handle it and then exit, but if it is important, it will just to the code that will handle that event. Here's some psuedocode to illustrate this: if (message == MOUSE_LEFT_BUTTON_DOWN) ; is the message a mouse click message? DisplayMessageBox(...) ; if so, display a message box if (message == DESTROY_WINDOW) ; is it a message where the user is trying PostQuitMessage() ; to close the window? If so, they want ; to quit, so add a quit message onto the ; message queue to quit the program. else Tell_Window_To_Handle_It() ; we don't need this message, so let ; windows handle it. The first line tests to see if it's a mouse event, and if it is, it will display a little message. That's pretty straight forward. The 3rd line tests to see if the user is trying to close the window. The reason why we do this is to allow the user to exit the program. You see the user does not have the ability to actually exit the application themselves with the mouse. All the user can do is close the window(i.e. clicking the X in the top right hand corner). So, we assume that when the user is closing the window, they want to exit the program. That's why we test to see if the user is trying to DESTROY the window(close it). Actually, when the user closes the main window, the application doesn't have to exit. An application can exist just fine with no windows at all. But, when people close the main window of the program, that usually means that they want to exit the entire program. So therefore, to tell when the user wants to quit, we just test to see if the user is trying to close the main window. When the program has determined that the user wants to quit, it will call a function that will manually add a message to the message queue(the linked list where pending messages are kept). That way, the message loop that coded earlier will receive that message and exit. The last two lines will handle the message if we don't want it. Most of the messages that we'll receive we don't even need or want. But, all messages have to be handled, to any messages that we don't need, we give to windows to handle for us and that's it. Once again, I suggest you get the help files from the win32 SDK, because there are hundreds of messages that you might want to handle. The following is the assembly implementation of what was just discussed. There is nothing tricky with this code. If you understand the psuedocode presented above, you won't have a problem with this. Once again, this is what would go inside the message handling function(for our program): ;----------------------------------------------------------------------------- mov eax,@@wmsg ; put message in eax cmp eax,WM_LBUTTONDOWN ; is it mouse press? je button_down ; if so, handle it. cmp eax,WM_DESTROY ; user trying to exit? je destroy_window ; if so, post quit message call DefWindowProcA, @@hwnd,@@wmsg,@@wparam,@@lparam ; let windows ; handle the messages we don't ; want. ret ; return. button_down: ; here we'll handle the mouse ; event--display a message. call MessageBoxA,@@hwnd, ; handle of parent window. offset string1, ; the string to display. offset title1, ; title of message box MB_OK ; type of message box xor eax,eax ; return 0 ret ; return destroy_window: ; here we handle the user quitting call PostQuitMessage,0 ; manually add quit message to queue xor eax,eax ; return 0 ret ; return WindowProc endp ;----------------------------------------------------------------------------- This is pretty straight forward. Some parts to notice are: WM_LBUTTONDOWN and WM_DESTROY are constants defined in the header files. The DefWindowProc function is used to tell windows to handle messages that we don't want. The MessageBox function is used to display a message on screen. The first parameter is the parent window(which is our main window). The parent window is a window that "owns" the window. The next two parameters are ASCIIZ strings that are the string to display and the title of the message box. The last parameter is the type of message box to display. MB_OK will display a box with no icon, and just an OK button. The PostQuitMessage function is used to manually put a quit message on the message queue, so that the message loop will eventually receive it and exit the application. --------------------------- The final Program--Win32App --------------------------- OK, here it is. The full win32App source code. Its code is made up of all of the bits and pieces of code that were presented in this text exactly as you saw them. So basically it's just everything you've learned put together into one final program. It will display a window, and display a message box every time you click the mouse on the window. You may be surprised at how short it is(or maybe not). NOTE: this program is not commented, because there is really nothing new in it. It's just a collection of all the pieces of code (which are explained in detail) in this text. ;============================================================================= ;= WIN32APP.ASM ;============================================================================= .386 locals .model flat,stdcall include win32.inc NULL equ 0 extrn GetModuleHandleA : proc extrn LoadIconA : proc extrn LoadCursorA : proc extrn RegisterClassA : proc extrn CreateWindowExA : proc extrn GetMessageA : proc extrn TranslateMessage : proc extrn DispatchMessageA : proc extrn ExitProcess : proc extrn DefWindowProcA : proc extrn PostQuitMessage : proc extrn MessageBoxA : proc extrn ShowWindow : proc .data inst dd 0 hmainwnd dd 0 wclss WNDCLASS <> message MSGSTRUCT <> windowtitle db 'Win32 Shell',0 classname db 'win32win',0 string db 'Left Mouse button pressed',0 title db 'Note:',0 public WindowProc .code start: call GetModuleHandleA,NULL mov [inst],eax mov wclss.clsStyle,CS_HREDRAW or CS_VREDRAW mov wclss.clsLpfnWndProc,offset WindowProc mov wclss.clsCbClsExtra,0 mov wclss.clsCbWndExtra,0 mov eax,[inst] mov wclss.clsHInstance,eax call LoadIconA,NULL,IDI_APPLICATION mov wclss.clsHIcon,eax call LoadCursorA,NULL,IDC_ARROW mov wclss.clsHCursor,eax mov wclss.clsHbrBackground,COLOR_BACKGROUND mov wclss.clsLpszMenuName,NULL mov wclss.clsLpszClassName,offset classname call RegisterClassA,offset wclss call CreateWindowExA,0,offset classname,offset windowtitle, WS_BORDER or WS_SYSMENU or WS_MINIMIZEBOX, 200,200,640,480,0,0,inst,0 mov [hmainwnd],eax call ShowWindow,hmainwnd,SW_SHOWNORMAL msg_loop: call GetMessageA,offset message,0,0,0 cmp ax,0 je end_loop call TranslateMessage,offset message call DispatchMessageA,offset message jmp msg_loop end_loop: call ExitProcess,message.msWPARAM WindowProc proc stdcall, @@hwnd:dword,@@wmsg:dword,@@wparam:dword,@@lparam:dword mov eax,@@wmsg cmp eax,WM_LBUTTONDOWN je button_down cmp eax,WM_DESTROY je destroy_window call DefWindowProcA, @@hwnd,@@wmsg,@@wparam,@@lparam ret button_down: call MessageBoxA,@@hwnd,offset string,offset title,MB_OK xor eax,eax ret destroy_window: call PostQuitMessage,0 xor eax,eax ret WindowProc endp end start end ;============================================================================= ;= WIN32APP.ASM ;============================================================================= -device