Here are some examples of simple, yet often very useful API functions.
WinAPI - DebugBreakThis example demonstrates using DebugBreak() to transfer control to a debugger. The example should be executed both, under a debugger and directly.
This example also serves to demonstrate how a program can detect if it is running under a debugger without using the IsDebuggerPresent() API.
Use the "DebugBreak()" menu item to cause the program execution to fall under debugger control. Use F7/F8/F9 to control execution.
DebugBreak() is very useful to create conditional breakpoints with very complex conditions that are beyond the capabilities of
an application debuggers.
Also, DebugBreak() is useful to set breakpoints where it is usually not possible to set them. For instance, during the loading of a program. Windows automatically loads and calls the associated DLLs entry points. A strategically placed DebugBreak instruction allows a debugger to gain control during the DLLs load process. This combined with the use of a system debugger, such as SoftICE, allows debugging almost any code in the executable.
In addition to that, combining the use of DebugBreak() with conditional compilation directives allows having the breakpoints, which in a large program may be numerous, in the program itself. This prevents "losing" breakpoints by exceeding the "breakpoint history" capacity in most debuggers and aids in future maintenance as, at least in theory, calls to DebugBreak() should only appear (or be left) in the most important or complex section of the code.
DebugBreak() can also be useful to make reverse engineering a program more cumbersome. By littering the executable with a fairly large number of calls to it (say about 60 or more) this can make reverse engineering the application very tedious when it is run under a debugger.
WM_COMMAND:
begin
case LOWORD(wParam) of
IDM_INT3:
begin
{ execute an INT 3h instruction - this should be executed }
{ under a debugger. }
try
DebugBreak();
MessageBox(Wnd,
'Running under a debugger - executed DebugBreak() ' +
'- int 3; ret - instructions',
'DebugBreak()',
MB_OK);
except
MessageBox(Wnd,
'This example should be run under a debugger ' +
'(and it isn''t.)',
'DebugBreak()',
MB_OK);
end;
exit;
end;
The above is the "critical" part of the code in the example. By executing DebugBreak() in a try/except, the application can detect whether or not it is running under a debugger. If the code in the exception handler is _not_ executed then, the program is not being debugged because, if it were, the DebugBreak() would have transferred control to the debugger instead of activating the exception handler.
To make reverse engineering more cumbersome, a program could place code that _should be_ executed in the exception handler. If the program is running under a debugger, the piece of code that is actually part of the normal operation of the program is not executed thus causing the program to later fail in some way. Put around fifty (50) of those cases around in the program and, that will try the patience of whoever is trying to find out how the code works.
This works particularly well when present in functions/procedures that are conditionally executed and/or are not executed often (it increases the chances of one or more of them being missed.)
WinAPI - IsDebuggerPresentThis example demonstrates how to use the API IsDebuggerPresent() to determine if a program is running under a debugger or not. If the program is running under a debugger, selecting "Break into the debugger" from the IsDebuggerPresent() menu, will cause a break into the debugger, otherwise the request to break into the debugger will be ignored.
It also demonstrates the difference between using DebugBreak() and an inline int 3. When using DebugBreak(), application debuggers will stop at the DebugBreak() line because the _entire_ DebugBreak() procedure has _not_ yet been executed, specifically, it stops at the "ret" that is inside DebugBreak().
When using an inline "int 3" the debugger will stop at the next source line because the "int 3" has been fully executed unlike the DebugBreak() procedure.
case LOWORD(wParam) of
IDM_ISDEBUGGERPRESENT:
begin
{ if we are running under a debugger, break into it with an INT 3 }
if IsDebuggerPresent() then DebugBreak(); { debugger stop here }
{ the above statement can also be implemented as : }
if IsDebuggerPresent() then
begin
asm int 3 end; { same as DebugBreak() but "inline" }
end;
exit; { debugger will stop here after the int 3 }
end;
IsDebuggerPresent() is particularly useful to guard calls to DebugBreak(). That way, it is not necessary to remove the calls to DebugBreak() in the "release" executable since they are triggered only when the executable is run under a debugger (normal users rarely run a program under a debugger and, many of them don't even know what a debugger is anyway.)
WinAPI - QueryPerformanceCounterWinAPI - QueryPerformanceFrequencyQueryPerformanceCounter along with QueryPerformanceFrequency can be used to measure elapsed time very accurately. That's the combined purpose of these two APIs.
To determine elapsed time in seconds, simply subtract the first query from the second query and divide the result by the frequency.
The attached examples only show how to call each of these functions. They do not include how to calculate elapsed time (it is reasonable to believe that programmers can subtract one number from another and divide the result by yet another number, i.e (b - a) div c)
WinAPI - SetLastErrorSetLastError can be used by an application's procedures to indicate whether or not they successfully performed their task. Note that it is strongly recommended to mark these custom application errors (the example shows how that is done.)
Also, it is important NOT to call any Windows API functions after setting the last error as it is very likely that the API function will overwrite the last error set by the application.
Fun experiment :
1. Start the SetLastError example
2. Select "LoadLibrary failure" from the "SetLastError" menu
3. Notice the value of the error (should be $A1)
4. Select "About SetLastError" from the "Help" menu
5. Click the OK button on the About box
6. Notice the value of the error (should still be $A1
Now repeat step 1 through 4 above
5. Move the About box vertically a couple of pixels up and click the OK button
6. Notice the value of the error (should be $0 instead of $A1)
7. Why did moving the About box cause the value of SetLastError to change ?
WinAPI - Sleep The MSDN documentation states quite a few things about the Sleep API. This test program demonstrates what _actually_ happens when the sleep interval changes.
RECOMMENDATION : _NEVER_ use an interval of zero (0). An interval of zero (0) causes most versions of Windows (maybe all) to consume significantly more CPU than expected. Run the example while watching the CPU usage in Task Manager and selecting various values from the menu.
To eliminate the overhead of repainting the screen, uncomment the NOPAINT define. (painting does not perceptibly alter the cpu consumption, the DEFINE is there to test and prove that is the case.
Enjoy!