Recent

Author Topic: WinAPI - examples - post 3  (Read 1219 times)

440bx

  • Hero Member
  • *****
  • Posts: 2280
WinAPI - examples - post 3
« on: January 21, 2021, 05:56:16 pm »
Here are some examples of simple, yet often very useful API functions.

WinAPI - DebugBreak

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

Code: Pascal  [Select][+][-]
  1.     WM_COMMAND:
  2.     begin
  3.       case LOWORD(wParam) of
  4.         IDM_INT3:
  5.         begin
  6.           { execute an INT 3h instruction - this should be executed           }
  7.           { under a debugger.                                                 }
  8.  
  9.           try
  10.             DebugBreak();
  11.             MessageBox(Wnd,
  12.                        'Running under a debugger - executed DebugBreak() ' +
  13.                        '- int 3; ret - instructions',
  14.                        'DebugBreak()',
  15.                        MB_OK);
  16.           except
  17.             MessageBox(Wnd,
  18.                        'This example should be run under a debugger '      +
  19.                        '(and it isn''t.)',
  20.                        'DebugBreak()',
  21.                        MB_OK);
  22.           end;
  23.  
  24.           exit;
  25.         end;
  26.  
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 - IsDebuggerPresent

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

Code: Pascal  [Select][+][-]
  1.       case LOWORD(wParam) of
  2.         IDM_ISDEBUGGERPRESENT:
  3.         begin
  4.           { if we are running under a debugger, break into it with an INT 3   }
  5.  
  6.           if IsDebuggerPresent() then DebugBreak();   { debugger stop here    }
  7.  
  8.           { the above statement can also be implemented as :                  }
  9.  
  10.           if IsDebuggerPresent() then
  11.           begin
  12.             asm int 3 end;       { same as DebugBreak() but "inline"          }
  13.           end;
  14.  
  15.           exit;                  { debugger will stop here after the int 3    }
  16.         end;
  17.  
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 - QueryPerformanceCounter
WinAPI - QueryPerformanceFrequency

QueryPerformanceCounter 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 - SetLastError

SetLastError 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!
« Last Edit: January 21, 2021, 05:58:15 pm by 440bx »
FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

lucamar

  • Hero Member
  • *****
  • Posts: 3788
Re: WinAPI - examples - post 3
« Reply #1 on: January 21, 2021, 11:27:04 pm »
Just a tiny observation: wouldn't it be better if each thread had kind of an "index" pointing to the other threads? That way one could look over all your contributions without having to search the forum. Though it's not very important :-[
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 7052
  • Debugger - SynEdit - and more
    • wiki
Re: WinAPI - examples - post 3
« Reply #2 on: January 22, 2021, 12:52:26 am »
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.
Windows actually transfers control to the debugger for each loaded dll. It is of course up to the debuggers capabilities what should happen on such events.

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.
Of course one can easily modify a debugger not to stop at a DebugBreak (or int3, when it is original code).
And one can also modify a debugger to let the application have the exception.
I just tested this myself, with a modified fpdebug.


440bx

  • Hero Member
  • *****
  • Posts: 2280
Re: WinAPI - examples - post 3
« Reply #3 on: January 22, 2021, 01:24:55 am »
Just a tiny observation: wouldn't it be better if each thread had kind of an "index" pointing to the other threads? That way one could look over all your contributions without having to search the forum. Though it's not very important :-[
I will create an index.  Like you I believe that would be useful in the long run.

Thank you for the suggestion.



Windows actually transfers control to the debugger for each loaded dll. It is of course up to the debuggers capabilities what should happen on such events.
Maybe the dll case I mentioned wasn't the best example but, I remember having to manually place an int 3 directly in the code to get the debugger to break at a specific time.  Unfortunately, I don't remember the details of the case.

Of course one can easily modify a debugger not to stop at a DebugBreak (or int3, when it is original code).
And one can also modify a debugger to let the application have the exception.
I just tested this myself, with a modified fpdebug.
When the programmer has the luxury and expertise to modify the debugger then what you mentioned is definitely applicable.  I believe, for most programmers, placing an int 3 in a strategic place will likely be the preferred method.
FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 7052
  • Debugger - SynEdit - and more
    • wiki
Re: WinAPI - examples - post 3
« Reply #4 on: January 22, 2021, 02:47:27 am »
Windows actually transfers control to the debugger for each loaded dll. It is of course up to the debuggers capabilities what should happen on such events.
Maybe the dll case I mentioned wasn't the best example but, I remember having to manually place an int 3 directly in the code to get the debugger to break at a specific time.  Unfortunately, I don't remember the details of the case.
The example is not invalidated by my statement. However, the need for int3/debugbreak depends not just on what you try to debug, but also what the debugger offers. In theory a debugger could offer a solution for 99.99% of int3 usage. However, no debugger really does.

Of course one can easily modify a debugger not to stop at a DebugBreak (or int3, when it is original code).
And one can also modify a debugger to let the application have the exception.
I just tested this myself, with a modified fpdebug.
When the programmer has the luxury and expertise to modify the debugger then what you mentioned is definitely applicable.  I believe, for most programmers, placing an int 3 in a strategic place will likely be the preferred method.
This 2nd part was about int3 as a "anti hacker" protection.
And yes, like the above it depends. Only now it depends what hacker you try to defeat.
An inexperienced hacker may indeed be bothered like hell.
An experienced one, might have a debugger that will not be fooled.
There would still be subtle differences, an int3 in such a special debugger would still have different timings. If it enters the debugger other threads will be paused too, for just a moment. I have not explored those effects in detail, but they probably can be used to make a hackers plans go south.

Peter H

  • Full Member
  • ***
  • Posts: 197
Re: WinAPI - examples - post 3
« Reply #5 on: January 22, 2021, 03:32:31 am »
It should be added, in windows it is possible to make an entry to the registry for a specific exe, so this exe, when invoked, is automatically attached to the debugger and stopped.
This permits to debug the startup code.
I dont have a link, this is somewhere described in the M$ debugger documentation.
I dont know, if this works with non-MS debuggers.

I think your postings are useful - go on!

However distributed calls to debugbreak for hacker protection is risky.
It is then very easy to search for the adress and calling instruction of debugbreak and patch it, once the trick is known.  ;D
« Last Edit: January 22, 2021, 03:40:38 am by Peter H »

440bx

  • Hero Member
  • *****
  • Posts: 2280
Re: WinAPI - examples - post 3
« Reply #6 on: January 22, 2021, 06:23:15 am »
However distributed calls to debugbreak for hacker protection is risky.
It is then very easy to search for the adress and calling instruction of debugbreak and patch it, once the trick is known.  ;D
The goal isn't to outsmart a good reverse engineer, the goal is to try his/her patience.  The tricks don't have to be elaborate, what's needed is to force the individual to manually inspect every place where there is int 3 and other simple tricks to determine what should be done in each specific case.

Combine int 3 tricks, int 1 tricks, calls to DebugBreak(), required code that executes in the exception handlers instead of outside of them and, a few other tricks I won't mention and, that can make the life of the reverse engineer totally miserable, not because the tricks are difficult to understand, they aren't, but because now the reverser has to manually inspect as many as 80 different places and determine what the proper corrective action is and, that will test their patience, which is the goal.  Combining those tricks can make running the program under a debugger a completely miserable experience.

It is extremely difficult to outsmart a talented and experienced reverse engineer (Adobe and Autodesk are constantly trying and failing at it.)  Additionally, the trickier and more complex the code is, the greater the motivation becomes to figure out how to neutralize it.   Instead, make it easy and boring and, force the process to be fully manual, i.e, create a disassembly and visually inspect all the code and determine where a "fix" is necessary and what the correct fix is in every case.  Reversers very rarely run out of smart but, they do run out of patience (and coffee.) :)

Don't try to outsmart them, just wear them out.

FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

PascalDragon

  • Hero Member
  • *****
  • Posts: 2918
  • Compiler Developer
Re: WinAPI - examples - post 3
« Reply #7 on: January 22, 2021, 10:27:58 am »
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.

As Martin_fr said, it's easily possible to avoid this. For example in WinDbg you can simply use gn after an exception to continue it as unhandled thus invoking the application's exception handler (if any).

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

With Windows nowadays supporting non-x86 architectures you should mention that using int 3 directly only works on x86 systems. For e.g. Windows on ARM64 you'd need to use brk #0xf000 (yes, exactly like that). I have used that quite often when porting FPC for aarch64-win64. ;)

Don't try to outsmart them, just wear them out.

We use a similar philosophy with the copy protection of our software at work. Make it too annoying for the crackers to work with and they'll look for more worthwhile targets ;)

440bx

  • Hero Member
  • *****
  • Posts: 2280
Re: WinAPI - examples - post 3
« Reply #8 on: January 22, 2021, 12:43:06 pm »
For example in WinDbg you can simply use gn after an exception to continue it as unhandled thus invoking the application's exception handler (if any).
Yes, true.  Using a system debugger really gives a lot of power and flexibility but, the percentage of programmers who use one is fairly small compared to those who use a "normal" application debugger.   If memory serves, I believe I had to set/patch breakpoints ($CC) in TLS callback routines because even the system debugger would not automatically break into them.

With Windows nowadays supporting non-x86 architectures you should mention that using int 3 directly only works on x86 systems.
Point taken.  I have a tendency to forget Windows runs on other architectures.

Make it too annoying for the crackers to work with and they'll look for more worthwhile targets ;)
If it can annoy Mr. Spock, it will likely remain uncracked. :)
FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

PascalDragon

  • Hero Member
  • *****
  • Posts: 2918
  • Compiler Developer
Re: WinAPI - examples - post 3
« Reply #9 on: January 22, 2021, 02:26:50 pm »
For example in WinDbg you can simply use gn after an exception to continue it as unhandled thus invoking the application's exception handler (if any).
Yes, true.  Using a system debugger really gives a lot of power and flexibility but, the percentage of programmers who use one is fairly small compared to those who use a "normal" application debugger.   If memory serves, I believe I had to set/patch breakpoints ($CC) in TLS callback routines because even the system debugger would not automatically break into them.

Please be aware that I used WinDbg as an application debugger not a system debugger.

Peter H

  • Full Member
  • ***
  • Posts: 197
Re: WinAPI - examples - post 3
« Reply #10 on: January 22, 2021, 04:39:36 pm »
The goal isn't to outsmart a good reverse engineer, the goal is to try his/her patience.  The tricks don't have to be elaborate, what's needed is to force the individual to manually inspect every place where there is int 3 and other simple tricks to determine what should be done in each specific case.

I am not a hacker, but have debugged software and patched for very legal and honest purposes.
I think a good protection would be, if a program uses the IsDebuggerPresent API and introduces bugs, when running in the debugger, for examples releasing some memory and intentionally creating dangling pointers or out of bounds errors.
The program will then crash in the debugger some time later and not at the location where the "bug" was made, and this is very hard to debug.
« Last Edit: January 22, 2021, 04:46:29 pm by Peter H »

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 7052
  • Debugger - SynEdit - and more
    • wiki
Re: WinAPI - examples - post 3
« Reply #11 on: January 22, 2021, 05:43:07 pm »
Interesting to see how this topic took off.... (feigning an innocent look myself...)

I think a good protection would be, if a program uses the IsDebuggerPresent API and introduces bugs, when running in the debugger,
Of course a debugger could be tweaked to intercept those calls and fake the return values.

for examples releasing some memory and intentionally creating dangling pointers or out of bounds errors.
The program will then crash in the debugger some time later and not at the location where the "bug" was made, and this is very hard to debug.
If you get mem access errors in your own app, I highly recommend to use "valgrind --tool memcheck" on it.  In fact I recommend that even if you do not get any errors.

For all else, the hacker always wins.
But, I guess a nice game to play would be to include your own "debugger" in your app. This could tweak values at strategic points and look for other debuggers. I do not know if Windows will allow it, but if you could have two processes that debug each other (circular), then they could each check for side-effects of other debuggers.
Of course the hacker could just debug with a system debugger.

 

TinyPortal © 2005-2018