Statistics

Members: 1927
News: 293
Web Links: 1
Visitors: 3932811

Who's Online

We have 2 guests online
Damn Vulnerable LinuxDamn Vulnerable Linux (DVL) is a Linux-based (modified Damn Small Linux) tool for IT-Security & IT-Anti- Security and Attack & Defense. [CLICK HERE FOR MORE INFOS! ]

Featured Conference Video

T16-Recon2006-Joe_Stewart-OllyBonE.gif OllyBone - Semi-Automatic Unpacking on IA-32. View the conference video here!
Home arrow Articles - Black Hat Methods arrow Assembly arrow Standardizing Win32 Callback Procedures
Standardizing Win32 Callback Procedures
User Rating: / 1
PoorBest 
Written by Jeremy Gordon   


This short article describes my preferred method for coding CALLBACK procedures in a large assembler program for Windows 32.  First I describe what Win32 callback procedures are, and then get down to some code.

 

 

At run time the Win32 system will call your program on a regular and frequent basis. The procedures you supply for the system to call are called CALLBACK procedures. Here are examples of when these are used:- 1. To manage a window you created. In this case the system will send many

messages to the Window Procedure for the window. The Window Procedure is the code label you provide when you register your window class (by calling RegisterClass). For example the message WM_SIZE is sent by the system when the window is resized.
2. To inform the owner of a child window of events in the child window. For

example WM_PARENTNOTIFY (with a notify code) is sent to the Window Procedure of the owner of a window when the child window is being created or destroyed, or if the user clicks a mouse button while the cursor is over the child window.
3. To inform the owner of a common control of events in the control. For

example if you create a button owned by your window the Window Procedure for that window receives BN_CLICKED messages if the button is clicked. 4. Messages sent to a dialog you have created. These are messages relating

to the creation of the dialog and of the various controls. The dialog procedure is informed of events in the controls. 5. If you "Superclass" or "Subclass" a common control, you receive messages

for that common control like a hook procedure but your window procedure has the responsibility of passing them on to the control. 6. If you create "Hook" procedures you can intercept messages about to be sent

to other windows. The system will call your hook procedure and will pass the message on only when your hook procedure returns. 7. You can ask the system to provide your program with information to be sent

to a CALLBACK procedure. Examples are EnumWindows (enumerate all top-level windows) or EnumFonts (enumerate all available fonts).

In cases 1 to 5 above, just before the system calls the CALLBACK procedure, it PUSHES 4 dwords on the stack (ie. 4 "parameters"). Traditionally the names given to these parameters are:-

       hWnd = handle of window being called
uMsg = message number
wParam = a parameter sent with the message
lParam = another parameter sent with the message.

The number of parameters sent to hook procedures and emumeration callbacks varies - see the Window SDK.

Since your Window (or Dialog) procedure will need to react in a certain way depending on the message being sent, your code will need to divert execution to the correct place for a particular message.

"C" programmers have the advantage of being able to code this simply, using "switch" and "case".

Assembler programmers use various techniques. Perhaps the worst if there are a lot of messages to handle is the chain of compares, eg. (in A386 format):-

       MOV EAX,[EBP+0Ch]        ;get message number
CMP EAX,1h               ;see if WM_CREATE
JNZ >L2                  ;no
XOR EAX,EAX              ;ensure eax is zero on exit
JMP >L32                 ;finish
L2:
CMP EAX,116h             ;see if WM_INITMENU
JNZ >L4                  ;no
CALL INITIALISE_MENU
JMP >L30                 ;correct exit code
L4:
CMP EAX,47h              ;see if WM_WINDOWPOSCHANGED
JNZ >L8
and so on ........

To avoid these long chains, assembler programmers have developed various techniques. You will have seen many of these in sample code around Win32 assembler web sites and in the asm journal, using conditional jumps, macros or table scans. I do not wish to compare these various methods, merely to put forward my own current favourite, which I believe has these advantages:- 1. It works on all assemblers
2. It is modular, ie. the code for each window can be concentrated in a

particular part of your source code
3. It is easy to follow from the source code what message causes what result 4. The same function can easily be called from within different window

procedures

My method results in a very simple Window Procedure as follows (A386 format):-

WndProc
MOV EDX,OFFSET MAINMESSAGES CALL GENERAL_WNDPROC RET 10h

where the messages and functions (specific to this particular window procedure) are set out in a table such as this:-

;---------------------------------------------------------- DATA SEGMENT FLAT ;assembler to put following in data section ;--------------------------- WNDPROC message functions

MAINMESSAGES DD ENDOF_MAINMESSAGES-$                ;=number to be done
DD  312h,HOTKEY,116h,INITMENU,117h,INITMENUPOPUP,11Fh,MENUSELECT
DD  1h,CREATE,2h,DESTROY, 410h,OWN410,411h,OWN411
DD  231h,ENTERSIZEMOVE,47h,WINDOWPOSCHANGED,24h,GETMINMAXINFO
DD  1Ah,SETTINGCHANGE,214h,SIZING,46h,WINDOWPOSCHANGING
DD  2Bh,DRAWITEM,0Fh,PAINT,113h,TIMER,111h,COMMAND
DD  104h,SYSKEYDOWN,100h,KEYDOWN,112h,SYSCOMMAND
DD  201h,LBUTTONDOWN,202h,LBUTTONUP,115h,SCROLLMESS
DD  204h,RBUTTONDOWNUP,205h,RBUTTONDOWNUP
DD  200h,MOUSEMOVE,0A0h,NCMOUSEMOVE,20h,SETCURSORM
DD  4Eh,NOTIFY,210h,PARENTNOTIFY,86h,NCACTIVATE,6h,ACTIVATE
DD  1Ch,ACTIVATEAPP
ENDOF_MAINMESSAGES:           ;label used to work out how many messages
;----------------------------------------------------------
_TEXT SEGMENT FLAT            ;assembler to put following in code section

;----------------------------------------------------------

and where each of the functions here are procedures, for example:-

CREATE
XOR EAX,EAX ;ensure zero and nc return RET

and where GENERAL_WINDPROC is as follows:-

GENERAL WNDPROC
PUSH EBP MOV EBP,[ESP+10h] ;get uMsg in ebp MOV ECX,[EDX] ;get number of messages to do 8 (+4) SHR ECX,3 ;get number of messages to do ADD EDX,4 ;jump over size dword
L33
DEC ECX JS >L46 ;s=message not found CMP [EDX+ECX8],EBP ;see if its the correct message JNZ L33 ;no MOV EBP,ESP PUSH ESP,EBX,EDI,ESI ;save registers as required by Windows ADD EBP,4 ;allow for the extra call to here ;now [EBP+8]=hWnd,[EBP+0Ch]=uMsg,[EBP+10h]=wParam,[EBP+14h]=lParam CALL [EDX+ECX*8+4] ;call correct procedure for the message POP ESI,EDI,EBX,ESP JNC >L48 ;nc=don't call DefWindowProc eax=exit code
L46
PUSH [ESP+18h],[ESP+18h],[ESP+18h],[ESP+18h] ;ESP changes on push CALL DefWindowProcA
L48
POP EBP RET
NOTES

1. Instead of giving the actual message value, you can, of course, give

the name of an EQUATE. For example

WM_CREATE EQU 1h enables you to use WM_CREATE,CREATE instead of 1h,CREATE if you wish. 2. It is tempting to keep the message table in the CODE SECTION. This is

perfectly possible because the only difference to the Win32 system between the code section and the data section is that the code section area of memory is marked read only, whereas the data section is read/write. However, you may well get some loss of performance if you do this because most processors will read data more quickly from the data section. I performed some tests on this and found that having the table in the code section rather than the data section could slow the code considerably:-

486 processor - 22% to 36% slower Pentium processor - 94% to 161% slower AMD-K6-3D processor - 78% to 193% slower (but Pentium Pro - from 7% faster to 9% slower) (and Pentium II - from 29% faster to 5% slower) These tests were carried out on a table of 60 messages and the range of results is because tests were carried out varying the number of scans required before a find and also testing a no-find. 3. The procedure names must not be the names of API imports to avoid

confusion! For example change SETCURSOR slightly to avoid confusion with the API SetCursor. 4. If a function returns c (carry flag set) the window procedure will call

DefWindowProc. An nc return (carry flag not set) will merely return to the system with the return code in eax. (Some messages must be dealt with in this way). 5. You can send a parameter of your own to GENERAL_WNDPROC using EAX.

This is useful if you wish to identify a particular window. For example:-

SpecialWndProc
MOV EAX,OFFSET hSpecialWnd MOV EDX,OFFSET SPECIALWND_MESSAGES CALL GENERAL_WNDPROC RET 10h 6. The ADD EBP,4 just before the call to the function is to ensure that

EBP points to the parameters the stack in the same way as if the window procedure had been entered normally. This is intended to ensure that the function will be compatible if called by an ordinary window procedure written in assembler, for example:-

WndProc
PUSH EBP MOV EBP,ESP ;now [EBP+8]=hWnd,[EBP+0Ch]=uMsg,[EBP+10h]=wParam,[EBP+14h]=lParam 7. A standardized procedure for dealing with messages to a dialog procedure

can also be created in the same way, except that it should return TRUE (eax=1) if the message is processed and FALSE (eax=0) if it is not, without calling DefWindowProc. The same coding method can be applied to hooks and to enumerator CALLBACKS although these will vary.

                             {
 This e-mail address is being protected from spam bots, you need JavaScript enabled to view it
 }
{http://ourworld.compuserve.com/homepages/jorgon}