
Learning the Debugger, Part Three
What is a debugger, how to use it and how it is implemented, after reading the first and second part of the article, you know. In the final part of the article, we will try to consider some methods of dealing with the debugger, based on knowledge of the principles of its operation. I won’t give a template set of anti-debugging tricks, if you wish, all this can be found on the Internet, I’ll try to do it in a slightly different way, based on some abstract application, from which I will expand the protection code from the simplest scheme to ... until I get tired :)
Immediately, I’ll make a reservation, in opposing the application / debugger, the last one will always win :)
But, only if a competent specialist will use it, and to deal with such specialists is practically useless (well, unless of course you have at least the same qualifications).
True, as practice shows, competent specialists do not engage in tasks uninteresting to them, leaving them at the mercy of beginning reversers who have not yet bitten their granite of science and may stumble on some not obvious tricks.
Here is something we will consider, only in a very simplified form.
Suppose we have some software that we decided to sell. For simplicity, let it be a normal VCL application from an empty form (well, albeit not empty, but with a picture in its entirety) and we want to sell it. The first question that needs to be taken care of is how to make our picture visible only to those who paid for it? More precisely - how to distinguish between trial and legal users?
The most obvious solution is the key. The trial user does not know him, and the legal one, who paid for it with real money, can activate a legal copy of the application and enjoy the picture.
The key is so key.
We create a new VCL application, throw on the TImage form with a picture, set Visible to False. Then we place two TEdit on the form, the first for the username and the second for the activation code. Well, two buttons - close the application and activate.
Well, something like this:

After which we write a “top secret” activation code:
The essence of the code is as follows:
based on the username, the application generates a certain serial number and compares it with the user entered. If everything is normal, then all the controls responsible for activation are removed and the picture that the user was eager to see is displayed.
Let's say this:

(well ... the first thing I found :)
After the manipulations, "this" is published on various shareware sites and even sometimes links are made to programmer forums in topics of the form: "test plz protection".
And what does the cracker look like?
He takes a debugger (for simplicity we take the same Olly Debug) and sees this picture:

He doesn’t have an application code, but there is a typical error of the beginning software “defenders” - displaying a dialog about the wrong key.
What does this give a cracker?
He puts BP on the MessageBoxA call and, starting the application, catches the call of the given message, after which, by clicking on the “OK” button, he can return to the code in which the error is called, where, looking a little higher, he can determine the conditional jump based on which this call takes place:

In the picture, the decision point of the program is highlighted with exclamation marks.
All that remains for him to do is to correct the JE instruction on the JMP, thus disabling serial code verification and ensuring a valid transition to the code area, which should be executed only when the application is activated.
Somehow it’s not clear, right?
Well then, here's a picture from the Delphi debugger:

Here, the code is more understandable for debugging, and its reading is more convenient due to the deciphering addresses and bringing them into a readable form. For example, it is now clearly visible that before reaching the address 0x475729, at which the decision is made, the text is received from the TEdits and the GenerateSerial procedure is called.
The cracker does not have such information, and as can be seen in the previous image, he will have to analyze all the calls in order to make a picture more or less understandable for analysis. Well, the truth is, here I exaggerate a little, in fact the application map is built quite simply, with the tools, but ... But some people sometimes try hard to debug the system modules of the dolphin, the honor and praise for their perseverance :)
Well, the nuance, at 0x475729 on the screenshots there are two different instructions - JZ and JE, these are the nuances of interpreting the disassemblers, they are identical.
There is one interesting approach that has been voiced to me several times.
Here's a little higher, I announced that I will put BP on MessageBoxA, and they tell me that they will call MessageBoxW and the call cannot be caught. This statement is for a solid four with a plus, for yes, indeed, if the application calls the Unicode API, there will be a small miss with the breakdown, but there is a nuance. Let's expand the entire MessageBox call stack.
See what an interesting scheme it turns out:
MessageBoxA -> MessageBoxExA -> MessageBoxTimeOutA -> MessageBoxTimeOutW-> SoftModalMessageBox ()
So yes, we can put BP on a call to any of the listed functions (usually MessageBoxTimeOutW is enough) to catch the call we need, by the way the MessageBoxW function will also call it.
There is a really small nuance, in Delphi there are other ways to display a window.
Well, for example ShowMessage (). This method does not call the MessageBox API.
It’s funny enough to listen to the reasoning that this method is completely implemented in the form of creating a separate form in which the buttons are placed as they need and in general these are the internals of the VCL itself, of which nothing is clear in the debugger.
That’s how it is, if this call didn’t rest on the ShowWindow API, from which we will also go out onto the stack of code we need.
There are still dialogue challenges, but they will have exactly the same cuisine. All of this is detected without much time.
Therefore, make the first conclusion to your notebook:
Calling a message about an unsuccessful code check, immediately after this check is a sign of bad taste.
Well then - they hacked us, and made only a change in one single byte of the application. Now our fun picture is available to everyone absolutely free.
Sadly, but not critical - we will fight ...
Hacking occurred through direct editing of the application body.
So the task has grown: to provide verification of the integrity of the source code.
It sounds awesome, but in reality it is practically impossible :)
Is that what we can apply for this test?
There are a lot of buzzwords: digitally sign, verify the image of the file on disk, check the code section with the checksum. Everything is empty - in the end, anyway, we come to the need to somehow get the current value of the application code in memory ...
Well, we’re watching a digital signature. She, firstly, paid. Secondly, it is checked by calling the API of the WinVerifyTrust function, which is vulnerable to interception. Thirdly, it is easily removed by regular means through ImageRemoveCertificate.
So it’s not an option, what do we have to check the image of a file on disk?
Here, too, everything is sad. Look, our executable file was patched, we want to determine this by comparing it with the image on the disk and what we do is get the path to the current file through the same ParamStr (0) (for example) and then open the file on this path and start checking, but ...
But at the stage of calling OpenFile / CreateFile, the cracker replaces the path in the corresponding parameter with the path to the original, unmodified image, and all our checks go through the forest.
There is another interesting point. But your application can be stored on disk and unchanged. There is such a thing as loaders. Their essence lies in the fact that they start the process and modify the application body directly in memory.
For example, take our debugger from a previous article and use it to launch our application with a magic picture, and upon reaching the entry point we will execute the following code:
The application on the disk will remain unchanged, but instead of the JE instruction, a direct jump will be performed due to the recorded JMP instruction. Which is much sadder, because in this case, the first two options for checking integrity are guaranteed to not work.
The third option remains, checking code sections directly in the application body.
This is a rather resource-intensive implementation option and also not always leading to success for the following reasons.
Firstly, the checksum constants. If they are stored in the body of the application, the cracker will change them to the correct ones. (the second conclusion to your notebook is the constants of CRC blocks of code in the application, there is a bad tone).
Secondly, in the second part of the article I talked about the MIA - Memory Breakpoint. This is an ideal mechanism for detecting code integrity checks (if you do not take into account the even more competent HBP - Hardware BreakPoint).
It works simply - if there is a suspicion that the current section of the code is controlled by the protection mechanism, the MVR or HBP is hung on it in order to determine where the code integrity check itself is located.
If such a check is detected, it is also disabled by the patch.
Well, we actually sailed to the stalemate situation: the subscriber is not the subscriber :)
However ... You can get out

, of course, but ...
But first, let's see how to implement the application code integrity check in general.
More specifically, we want to protect the code that was at the very beginning from changes. To do this, we need to somehow find out its location in memory while the application is running.
Labels are our "everything."
Most hinged protectors work on the basis of labels, so why should we come up with another bike. What is a label - in principle, it is such a label that is so unloved by everyone, used with goto (), about which only the most lazy did not express their highly qualified "FI".
However ... what do we think of them? As I said - our labels are all :)
True, there is a nuance, label is convenient to use when controlling a small part of the code inside the procedure (with cross-checking - about it later), now we are interested in several procedures in total.
For this, the label will not work, but empty procedures as labels, the address of which we can get from the integrity check code, will do.
Well, the whole procedure for calculating the integrity is needed to the heap, and also (which was actually one of the nuances voiced above) a certain constant with which we will compare the CRC of the data block.
Well, enough stop ranting, we write:
What we have here:
Two labels in the form of empty procedures CheckedCodeBegin and CheckedCodeEnd, calculation of the “checksum” of data between these two labels, performed by the CheckCodeProtect procedure, well, the checksum itself, taken out of the area of the code being verified and represented by the constant CheckedCodeValidCheckSum (for its value so far do not pay attention).
In principle, nothing is complicated at all, but let's analyze it, but what does this even give us?
Actually a lot, because:

Yes, yes, this is the Breakpoint installed by the debugger. And his given code also perfectly detects, because if you remember, the BP installation mechanism consists in modifying the application body.
Here is the third note in your notebook - BP detection is performed by checking the body of the code.
True, unfortunately, not everything is so simple here, in some cases this code will not work, but we will not rush, we will get to this.
Now, sadly, as I said, this check is easily detected. For example, here is a screenshot from under the debugger, where it is interrupted immediately at the beginning of the test:

Blue indicates the code of the procedure for calculating the checksum, the debugger was interrupted at 0x467069, just the very first attempt to read the protected area.
Well, more precisely, here I cheated a little, if the verification code was outside the scope of the area being checked, then the stop would have occurred just on this instruction, and so, of course, I stopped at the very first “PUSH EBX”.
But this is lyrics, the question is different, and now what to do?
Well, firstly, everything is not so scary. Here, only one single check of the integrity of the application code is implemented. Yes, it is easily detected. Yes, it is also easily removed with a patch, but what prevents us from making several of them cross-controlling each other? Will they be removed as well? Well, not a question, add more, but what does it cost us?
Once I was sent a product to the analysis of application security directly by the developers of the protection itself (sorry - no names). Quickly looking at the VM initialization code, I immediately outlined the path for its analysis, I just had to pull out the crypt algorithm of small data blocks when calling a specific API function. The problem was that as soon as I patched the only byte of the application, the checksum verification mechanism worked. Naturally, I quickly flooded it, but as it turned out, the flooded code was controlled by four different algorithms. I started patching them and what do you think? For each patch, we understood more and more pieces of code that control the integrity of the code like an avalanche. As a result, I just drowned in the volume of manual patches and I had to write an automatic utility / debugger, which took almost a week of work, taking into account all the nuances.
However, this is no longer important, the meaning is important - if you wish, it’s possible to implement a decent headache to the cracker, even on a trivial verification of checksums.
Well, now to reality.
To detect the integrity check code, the cracker applied MBP.
And now we recall how they work - correctly through assigning the PAGE_GUARD attribute to the page. So, knowing the principles of the debugger, we can prevent this, just remove this attribute and the debugger will no longer respond to access to the supposedly controlled memory.
True, there is a nuance, we can do this by calling VirtualProtect, which is vulnerable, because the debugger can intercept it and prohibit its call. But we also have a bolt with a reverse thread, for example, you can do it as described in this article:read .
The truth is, we’ll not consider the option of removing PAGE_GUARD in the demo application. But do not worry, I will show you another interesting way, only for this you need to consider a few more nuances, so a little later.
Well, from now on, we believe that the application integrity control code is written so that it can’t be hacked (in order to simplify it) ...
Well, now we have come to the conclusion that they want our form with a picture, and with the help of a debugger. Of course you need to learn how to detect it. For now, let's dwell on the IsDebuggerPresent function, that's enough for a start.
We write the code:
Everything is very simple, if we are under the debugger, this function will return True.
We assume that the code for checking the integrity of the application is so complicated that it is impossible to patch it and the call to this function is placed in a protected area.
What will the cracker apply in this case?
There are actually only three options, given the fact that you cannot patch the application body:
It is difficult to deal with the third option (it is possible, but not necessary), but the first two we will examine in more detail, more precisely we will consider the second option, because in the first, the application code patch is also produced when installing BP with the 0xCC opcode.
First, add this code in the debugged application to the FormCreate procedure:
It will show the first 4 bytes of the IsDebuggerPresent function.
You can’t write such code:
For in the second option we use a static function, and the address will not point to the beginning of the function body, but to the import table, where there is an adapter in the form of JMP.
Let's execute the code and remember the value.
Under each system, it will be different, for example, in XP it will be the body of the original function, in the seven there will be an adapter for an analog from kernelbase. I got the value 9090F3EB, which corresponds to the following picture:

Now, let's take our debugger from the second part of the article, and in the OnBreakPoint method we will patch the body of this function with this code:
Here is a nuance, the address of the kernel32.dll library is the same for all applications, so the address of the IsDebuggerPresent function will be the same in the debugger and in the debugged application.
The meaning of the patch is to nullify the EAX register, through which the result of the function is returned and return to the code that calls this function.
We start the debugger, it will launch our application and, as a result of interfering with the process memory, the code in the FormCreate function of the debugger will not be detected. True, now the code that reads the first 4 bytes of this function will return not the number 9090F3EB, but the number 90C3C031, which corresponds to the opcodes of the patch.
How can we determine that the body of a given function is patched? In principle, we can read the first 4 bytes of this function from the kernel32.dll file located on the disk, however, in this case, when you open the library body, we can be replaced with the path to the same patched file and the check will say that everything is fine.
But there is another way that is rarely used in practice (I have met, if I’m not mistaken, only 1 time) and it consists in the following.
Since we cannot read the correct value from the disk, we can get it by reading the 4 bytes we need from the memory of some other process. There is of course a small chance that this process is also under the debugger and it intercepts the function we require in the same way, but it is very small.
As a result, we write this code:
Here I didn’t get smart and used the standard TlHelp32 features to get a list of processes, for example it’s enough.
Accordingly, another note in your notebook - whenever possible always check the integrity of critical API functions, in any way known to you, it is not necessary to do as I showed.
Yes, well, here too there is another nuance, under seven, calling IsDebuggerPresent from kernel32.dll will lead to calling the same function from kernelbase.dll, where it can also be patched, but think about it yourself.
As a result, after all the modifications, this function is tightly patronized by the integrity monitoring system and patching it on the forehead is already quite problematic. How to get around this piece of protection, I will show later, but now let's look at something else.
Look, before that we started the application under the debugger, and what should we start it without the debugger, wait until all the checks for debugging pass and only after that connect the debugger to the application?
Yes, in this case, all of our code will not work, or rather, it will work, but partially.
Alternatively, to detect such an outrage, for example, you can set a timer and periodically call the CheckIsDebugerPresent procedure, but de facto, we do not need this to detect a connection. The fact is that when the DebugActiveProcess function is called in the debugger, the DbgUiRemoteBreakin function is always called in the debugged application. Knowing this, we can crank up the next trick.
We will patch ourselves, or rather, the body of the DbgUiRemoteBreakin function, adding a transition to the address of the TerminateProcess function at its beginning, so as soon as the debugger is connected to the process, the process will end immediately.
We write the next block of code:
As a result of such a patch, the following code will be placed at the beginning of the DbgUiRemoteBreakin function:

That is, roughly two parameters necessary for the TerminateProcess function are placed roughly on the stack (they go in the opposite order), this is the uExitCode parameter equal to zero and the hProcess parameter, which is replaced with the DWORD (-1) pseudo-handle, which means current process. After that, the EAX register is initialized with the address of the TerminateProcess function and it is called.
If we try to join the process using the debugger from the second part of the article, then all we can see is the arrival of the CREATE_PROCESS_DEBUG_EVENT event, but even at the time this event arrives, we can’t do anything with the debugged process, for example, an attempt to install BP will fail, and etc.
For most debuggers, this is enough.
Unfortunately, this is not an armor-piercing option, because nothing will prevent you from re-patching the body of our application by returning the original code back before calling DebugActiveProcess. (True, I have not seen this, but still ...)
As I already said, you can determine the presence of MBP by checking the security attribute of the PAGE_GUARD page. This is done by calling the VirtualQuery function, or you can simply reassign attributes by invoking VirtualProtect.
But there is another tricky way and it is called ReadProcessMemory. This is the same function with which we read data from the debugged process in the debugger. Its nuance is the following, if it tries to read the data from the page protected by the PAGE_GUARD flag, the data block of the corresponding page will be filled with zeros, and the cimus is that the EXCEPTION_GUARD_PAGE event will not be raised in the debugger. Such is the "silent check of the region." If we use it when checking the integrity of the application code, if the MVR is installed on it, the data is not considered correct and, as a result, the checksum does not converge with the expected one. Moreover, if the Hardware Breakpoint monitoring record is set to the address from where this function will read, the read / write debugger will also not receive notifications about its operation.
Therefore, we rewrite the CalcCheckSum function as follows:
Thus, with one single function, we defend ourselves from BP, and from the Ministry of Internal Affairs, and even from NVR.
Well, I will not complicate the application protection code anymore, this information is enough.
I’ll still dwell on some nuances at the end of the article, and now we’ll try to write a loader based on the debugger, given the moment that we agreed to consider the integrity verification code as impenetrable and therefore we will not patch the verification body.
We are writing a wireframe. Start and stop will look like this:
I will skip the secondary handlers, they can be viewed in the source code of the example, in principle there is nothing new, everything has already been described in the last part of the article.
Our first task is to somehow disable the detection of the debugger by the application. Since the application checks the integrity of IsDebuggerPresent, and you can’t patch the check (by the condition of the task), we have only one option - change the value of the Peb.BeingDebugged parameter.
Let's do it with the following code:
Everything is simple here, we get the address of the process environment block, change the BeingDebugged parameter and write everything back. Therefore, the IsDebuggerPresent function stops responding to the debugger. The declaration of the structures used can be found in the demo source.
We completed the first stage, now the second - we need to somehow make the application not respond to the incorrectly entered code and show us the picture in any case.
We will do this:
You have probably changed the values of variables more than once in the debugger (this was described in the first part of the article). Here we will do something similar. As you remember, the JE instruction is responsible for displaying the picture, if you are coarser, imagine that we have a Boolean variable and the condition if value then..else, if we break off on such a condition, then we can control the execution conditions of the code, i.e. indicate, by changing the value variable, what exactly should be executed: then or else block.
The JE operator makes a decision on the transition based on just such a Boolean variable, although it is presented in the form of the ZF flag. If the flag is enabled, a jump to the new address occurs. Therefore, our task is to make the application interrupt on JE instructions so that we can change the value of this flag to what we need.
We do this by setting the HBP to the address of the JE instruction, because this is the only thing that cannot control our secure application. How to find out this address I will miss. In the example, the archive contains the crackme.exe executable file, I specially put it into the archive due to the fact that with each recompilation, and depending on the version of the dolphin and other things, this address will be different. In the compiled executable, this address is already calculated and is equal to the value 0x467840.
It remains to write the code:
After that you need to process the interrupt on HBP and set the correct flag value:
Well, that’s all, you can run it, enter any left value and enjoy the picture.
The result will be something like this:

So it usually happens, you think you wrote armor-piercing protection, and then once it costs on your knee, well, not always of course, but it does :)
I intentionally did not stop at detecting NVR within the framework of the protected application, for the reason that if such a check were there, I would have to write a fairly complicated bypass code. And in general, of course, it is advisable to check their presence, thus closing the debugger the possibility of normal operation.
The detection of the presence of HBP is quite simple, it can be implemented either through the same GetThreadContext and checking the DR7 register (if it is not empty, then HBP is standing), or so that we are not intercepted by an API function call, we can get the thread context by throwing an exception.
Here is the first option
And the second option, in which a debug exception is raised and information about the thread context is removed in the _except_handler handler.
By the way, an interesting point. Pay attention to how much information comes to the exception handler. All this information is not available to us in the except handler, which is why I often call try..finally..except a short wrapper over SEH :)
Now you know several ways to deal with the debugger, though now you know the methods of counteracting them, but then there is an article for you to draw conclusions.
The source code with examples can be taken from this link: http://rouse.drkb.ru/blog/dbg_part3.zip
And on this, my task can be completed.
All that I wanted to tell about a debugger, I told. Initially, the truth was only one article planned, but you can see for yourself how much material you got :)
In the future, I will talk about using the debugger, but this will be a completely different story.
© Alexander (Rouse_) Bagel
Moscow, November 2012
Immediately, I’ll make a reservation, in opposing the application / debugger, the last one will always win :)
But, only if a competent specialist will use it, and to deal with such specialists is practically useless (well, unless of course you have at least the same qualifications).
True, as practice shows, competent specialists do not engage in tasks uninteresting to them, leaving them at the mercy of beginning reversers who have not yet bitten their granite of science and may stumble on some not obvious tricks.
Here is something we will consider, only in a very simplified form.
The simplest ShareWare:
Suppose we have some software that we decided to sell. For simplicity, let it be a normal VCL application from an empty form (well, albeit not empty, but with a picture in its entirety) and we want to sell it. The first question that needs to be taken care of is how to make our picture visible only to those who paid for it? More precisely - how to distinguish between trial and legal users?
The most obvious solution is the key. The trial user does not know him, and the legal one, who paid for it with real money, can activate a legal copy of the application and enjoy the picture.
The key is so key.
We create a new VCL application, throw on the TImage form with a picture, set Visible to False. Then we place two TEdit on the form, the first for the username and the second for the activation code. Well, two buttons - close the application and activate.
Well, something like this:

After which we write a “top secret” activation code:
function TForm1.GenerateSerial(const AppUserName: string): string;
const
MagicSerialMask: int64 = $C5315E6121543992;
var
I: Integer;
SN: int64;
RawSN: string;
begin
SN := 0;
Result := '';
for I := 1 to Length(AppUserName) do
begin
Inc(SN, Word(AppUserName[I]));
SN := SN * 123456;
end;
Sn := SN xor MagicSerialMask;
RawSN := IntToHex(SN, 16);
for I := 1 to 16 do
if ((I - 1) mod 4 = 0) and (I > 1) then
Result := Result + '-' + RawSN[I]
else
Result := Result + RawSN[I];
end;
procedure TForm1.btnCheckSerialClick(Sender: TObject);
begin
if edSerial.Text <> GenerateSerial(edAppUserName.Text) then
Application.MessageBox('Неверный код активации',
PChar(Application.Title), MB_OK or MB_ICONERROR)
else
begin
Image1.Visible := True;
Label1.Visible := False;
Label2.Visible := False;
Label3.Visible := False;
edAppUserName.Visible := False;
edSerial.Visible := False;
btnCancel.Visible := False;
btnCheckSerial.Visible := False;
end;
end;
The essence of the code is as follows:
based on the username, the application generates a certain serial number and compares it with the user entered. If everything is normal, then all the controls responsible for activation are removed and the picture that the user was eager to see is displayed.
Let's say this:

(well ... the first thing I found :)
After the manipulations, "this" is published on various shareware sites and even sometimes links are made to programmer forums in topics of the form: "test plz protection".
And what does the cracker look like?
He takes a debugger (for simplicity we take the same Olly Debug) and sees this picture:

He doesn’t have an application code, but there is a typical error of the beginning software “defenders” - displaying a dialog about the wrong key.
What does this give a cracker?
He puts BP on the MessageBoxA call and, starting the application, catches the call of the given message, after which, by clicking on the “OK” button, he can return to the code in which the error is called, where, looking a little higher, he can determine the conditional jump based on which this call takes place:

In the picture, the decision point of the program is highlighted with exclamation marks.
All that remains for him to do is to correct the JE instruction on the JMP, thus disabling serial code verification and ensuring a valid transition to the code area, which should be executed only when the application is activated.
Somehow it’s not clear, right?
Well then, here's a picture from the Delphi debugger:

Here, the code is more understandable for debugging, and its reading is more convenient due to the deciphering addresses and bringing them into a readable form. For example, it is now clearly visible that before reaching the address 0x475729, at which the decision is made, the text is received from the TEdits and the GenerateSerial procedure is called.
The cracker does not have such information, and as can be seen in the previous image, he will have to analyze all the calls in order to make a picture more or less understandable for analysis. Well, the truth is, here I exaggerate a little, in fact the application map is built quite simply, with the tools, but ... But some people sometimes try hard to debug the system modules of the dolphin, the honor and praise for their perseverance :)
Well, the nuance, at 0x475729 on the screenshots there are two different instructions - JZ and JE, these are the nuances of interpreting the disassemblers, they are identical.
There is one interesting approach that has been voiced to me several times.
Here's a little higher, I announced that I will put BP on MessageBoxA, and they tell me that they will call MessageBoxW and the call cannot be caught. This statement is for a solid four with a plus, for yes, indeed, if the application calls the Unicode API, there will be a small miss with the breakdown, but there is a nuance. Let's expand the entire MessageBox call stack.
See what an interesting scheme it turns out:
MessageBoxA -> MessageBoxExA -> MessageBoxTimeOutA -> MessageBoxTimeOutW-> SoftModalMessageBox ()
So yes, we can put BP on a call to any of the listed functions (usually MessageBoxTimeOutW is enough) to catch the call we need, by the way the MessageBoxW function will also call it.
There is a really small nuance, in Delphi there are other ways to display a window.
Well, for example ShowMessage (). This method does not call the MessageBox API.
It’s funny enough to listen to the reasoning that this method is completely implemented in the form of creating a separate form in which the buttons are placed as they need and in general these are the internals of the VCL itself, of which nothing is clear in the debugger.
That’s how it is, if this call didn’t rest on the ShowWindow API, from which we will also go out onto the stack of code we need.
There are still dialogue challenges, but they will have exactly the same cuisine. All of this is detected without much time.
Therefore, make the first conclusion to your notebook:
Calling a message about an unsuccessful code check, immediately after this check is a sign of bad taste.
Introducing application integrity control:
Well then - they hacked us, and made only a change in one single byte of the application. Now our fun picture is available to everyone absolutely free.
Sadly, but not critical - we will fight ...
Hacking occurred through direct editing of the application body.
So the task has grown: to provide verification of the integrity of the source code.
It sounds awesome, but in reality it is practically impossible :)
Is that what we can apply for this test?
There are a lot of buzzwords: digitally sign, verify the image of the file on disk, check the code section with the checksum. Everything is empty - in the end, anyway, we come to the need to somehow get the current value of the application code in memory ...
Well, we’re watching a digital signature. She, firstly, paid. Secondly, it is checked by calling the API of the WinVerifyTrust function, which is vulnerable to interception. Thirdly, it is easily removed by regular means through ImageRemoveCertificate.
So it’s not an option, what do we have to check the image of a file on disk?
Here, too, everything is sad. Look, our executable file was patched, we want to determine this by comparing it with the image on the disk and what we do is get the path to the current file through the same ParamStr (0) (for example) and then open the file on this path and start checking, but ...
But at the stage of calling OpenFile / CreateFile, the cracker replaces the path in the corresponding parameter with the path to the original, unmodified image, and all our checks go through the forest.
There is another interesting point. But your application can be stored on disk and unchanged. There is such a thing as loaders. Their essence lies in the fact that they start the process and modify the application body directly in memory.
For example, take our debugger from a previous article and use it to launch our application with a magic picture, and upon reaching the entry point we will execute the following code:
procedure TTestDebugger.OnBreakPoint(Sender: TObject; ThreadIndex: Integer;
ExceptionRecord: Windows.TExceptionRecord; BreakPointIndex: Integer;
var ReleaseBreakpoint: Boolean);
var
JmpOpcode: Byte;
begin
if ExceptionRecord.ExceptionAddress =
Pointer(FCore.DebugProcessData.EntryPoint) then
begin
JmpOpcode := $EB;
FCore.WriteData(Pointer($475729), @JmpOpcode, 1);
The application on the disk will remain unchanged, but instead of the JE instruction, a direct jump will be performed due to the recorded JMP instruction. Which is much sadder, because in this case, the first two options for checking integrity are guaranteed to not work.
The third option remains, checking code sections directly in the application body.
This is a rather resource-intensive implementation option and also not always leading to success for the following reasons.
Firstly, the checksum constants. If they are stored in the body of the application, the cracker will change them to the correct ones. (the second conclusion to your notebook is the constants of CRC blocks of code in the application, there is a bad tone).
Secondly, in the second part of the article I talked about the MIA - Memory Breakpoint. This is an ideal mechanism for detecting code integrity checks (if you do not take into account the even more competent HBP - Hardware BreakPoint).
It works simply - if there is a suspicion that the current section of the code is controlled by the protection mechanism, the MVR or HBP is hung on it in order to determine where the code integrity check itself is located.
If such a check is detected, it is also disabled by the patch.
Well, we actually sailed to the stalemate situation: the subscriber is not the subscriber :)
However ... You can get out

, of course, but ...
But first, let's see how to implement the application code integrity check in general.
More specifically, we want to protect the code that was at the very beginning from changes. To do this, we need to somehow find out its location in memory while the application is running.
Labels are our "everything."
Most hinged protectors work on the basis of labels, so why should we come up with another bike. What is a label - in principle, it is such a label that is so unloved by everyone, used with goto (), about which only the most lazy did not express their highly qualified "FI".
However ... what do we think of them? As I said - our labels are all :)
True, there is a nuance, label is convenient to use when controlling a small part of the code inside the procedure (with cross-checking - about it later), now we are interested in several procedures in total.
For this, the label will not work, but empty procedures as labels, the address of which we can get from the integrity check code, will do.
Well, the whole procedure for calculating the integrity is needed to the heap, and also (which was actually one of the nuances voiced above) a certain constant with which we will compare the CRC of the data block.
Well, enough stop ranting, we write:
const
CheckedCodeValidCheckSum: DWORD = 248268; // << тут мы будем хранить контрольную сумму
procedure CheckedCodeBegin;
begin
end;
function TForm1.CalcCheckSum(Addr: Pointer; Size: Integer): DWORD;
var
pCursor: PByte;
I: Integer;
Dumee: DWORD;
begin
Result := 0;
pCursor := Addr;
for I := 0 to Size - 1 do
begin
if pCursor^ <> 0 then
Inc(Result, pCursor^)
else
Dec(Result);
Inc(pCursor);
end;
end;
procedure TForm1.CheckCodeProtect;
var
CheckedCodeBeginAddr, CheckedCodeEndAddr: Pointer;
CurrentCheckSum: DWORD;
begin
// получаем адрес начала защищенного кода
CheckedCodeBeginAddr := @CheckedCodeBegin;
// получаем адрес конца защищенного кода
CheckedCodeEndAddr := @CheckedCodeEnd;
// Считем контрольную сумму и сверяемся с оригиналом
CurrentCheckSum := CalcCheckSum(CheckedCodeBeginAddr,
Integer(CheckedCodeEndAddr) - Integer(CheckedCodeBeginAddr));
if CurrentCheckSum <> CheckedCodeValidCheckSum then
begin
MessageBox(Handle, 'Нарушение целостности исполняемого кода.',
PChar(Application.Title), MB_ICONERROR);
TerminateProcess(GetCurrentProcess, 0);
end;
end;
function TForm1.GenerateSerial(const AppUserName: string): string;
const
MagicSerialMask: int64 = $C5315E6121543992;
var
I: Integer;
SN: int64;
RawSN: string;
begin
SN := 0;
Result := '';
for I := 1 to Length(AppUserName) do
begin
Inc(SN, Word(AppUserName[I]));
SN := SN * 123456;
end;
Sn := SN xor MagicSerialMask;
RawSN := IntToHex(SN, 16);
for I := 1 to 16 do
if ((I - 1) mod 4 = 0) and (I > 1) then
Result := Result + '-' + RawSN[I]
else
Result := Result + RawSN[I];
end;
procedure TForm1.btnCheckSerialClick(Sender: TObject);
begin
// Проверяем целостность кода
CheckCodeProtect;
if edSerial.Text <> GenerateSerial(edAppUserName.Text) then
ShowMessage('Неверный код активации')
else
begin
Image1.Visible := True;
Label1.Visible := False;
Label2.Visible := False;
Label3.Visible := False;
edAppUserName.Visible := False;
edSerial.Visible := False;
btnCancel.Visible := False;
btnCheckSerial.Visible := False;
end;
end;
procedure CheckedCodeEnd;
begin
end;
What we have here:
Two labels in the form of empty procedures CheckedCodeBegin and CheckedCodeEnd, calculation of the “checksum” of data between these two labels, performed by the CheckCodeProtect procedure, well, the checksum itself, taken out of the area of the code being verified and represented by the constant CheckedCodeValidCheckSum (for its value so far do not pay attention).
In principle, nothing is complicated at all, but let's analyze it, but what does this even give us?
Actually a lot, because:
- This code detects a patch of the application body on disk (because when it starts, it will already have changed bytes).
- This code detects a patch of the application body by the loader (as described above).
- And this code detects ... remember the picture from the last article?

Yes, yes, this is the Breakpoint installed by the debugger. And his given code also perfectly detects, because if you remember, the BP installation mechanism consists in modifying the application body.
Here is the third note in your notebook - BP detection is performed by checking the body of the code.
True, unfortunately, not everything is so simple here, in some cases this code will not work, but we will not rush, we will get to this.
Now, sadly, as I said, this check is easily detected. For example, here is a screenshot from under the debugger, where it is interrupted immediately at the beginning of the test:

Blue indicates the code of the procedure for calculating the checksum, the debugger was interrupted at 0x467069, just the very first attempt to read the protected area.
Well, more precisely, here I cheated a little, if the verification code was outside the scope of the area being checked, then the stop would have occurred just on this instruction, and so, of course, I stopped at the very first “PUSH EBX”.
But this is lyrics, the question is different, and now what to do?
Well, firstly, everything is not so scary. Here, only one single check of the integrity of the application code is implemented. Yes, it is easily detected. Yes, it is also easily removed with a patch, but what prevents us from making several of them cross-controlling each other? Will they be removed as well? Well, not a question, add more, but what does it cost us?
Once I was sent a product to the analysis of application security directly by the developers of the protection itself (sorry - no names). Quickly looking at the VM initialization code, I immediately outlined the path for its analysis, I just had to pull out the crypt algorithm of small data blocks when calling a specific API function. The problem was that as soon as I patched the only byte of the application, the checksum verification mechanism worked. Naturally, I quickly flooded it, but as it turned out, the flooded code was controlled by four different algorithms. I started patching them and what do you think? For each patch, we understood more and more pieces of code that control the integrity of the code like an avalanche. As a result, I just drowned in the volume of manual patches and I had to write an automatic utility / debugger, which took almost a week of work, taking into account all the nuances.
However, this is no longer important, the meaning is important - if you wish, it’s possible to implement a decent headache to the cracker, even on a trivial verification of checksums.
Well, now to reality.
To detect the integrity check code, the cracker applied MBP.
And now we recall how they work - correctly through assigning the PAGE_GUARD attribute to the page. So, knowing the principles of the debugger, we can prevent this, just remove this attribute and the debugger will no longer respond to access to the supposedly controlled memory.
True, there is a nuance, we can do this by calling VirtualProtect, which is vulnerable, because the debugger can intercept it and prohibit its call. But we also have a bolt with a reverse thread, for example, you can do it as described in this article:read .
The truth is, we’ll not consider the option of removing PAGE_GUARD in the demo application. But do not worry, I will show you another interesting way, only for this you need to consider a few more nuances, so a little later.
Well, from now on, we believe that the application integrity control code is written so that it can’t be hacked (in order to simplify it) ...
Debugger detection
Well, now we have come to the conclusion that they want our form with a picture, and with the help of a debugger. Of course you need to learn how to detect it. For now, let's dwell on the IsDebuggerPresent function, that's enough for a start.
We write the code:
function IsDebuggerPresent: BOOL; stdcall; external kernel32;
procedure TForm1.FormCreate(Sender: TObject);
begin
if IsDebuggerPresent then
begin
MessageBox(Handle, 'Работа приложения под отладчиком запрещена.',
PChar(Application.Title), MB_ICONERROR);
TerminateProcess(GetCurrentProcess, 0);
end;
end;
Everything is very simple, if we are under the debugger, this function will return True.
We assume that the code for checking the integrity of the application is so complicated that it is impossible to patch it and the call to this function is placed in a protected area.
What will the cracker apply in this case?
There are actually only three options, given the fact that you cannot patch the application body:
- put BP on the call of this function, where to replace the result of its call.
- patch the code of this function so that it always returns False
- make a change to the Peb.BeingDebugged variable in the address space of the debugged process.
It is difficult to deal with the third option (it is possible, but not necessary), but the first two we will examine in more detail, more precisely we will consider the second option, because in the first, the application code patch is also produced when installing BP with the 0xCC opcode.
First, add this code in the debugged application to the FormCreate procedure:
procedure TForm1.FormCreate(Sender: TObject);
var
P: PCardinal;
begin
P := GetProcAddress(GetModuleHandle(kernel32), 'IsDebuggerPresent');
ShowMessage(IntToHex(P^, 8));
It will show the first 4 bytes of the IsDebuggerPresent function.
You can’t write such code:
function IsDebuggerPresent: BOOL; stdcall; external kernel32;
procedure TForm1.FormCreate(Sender: TObject);
var
P: PCardinal;
begin
P := @IsDebuggerPresent;
ShowMessage(IntToHex(P^, 8));
For in the second option we use a static function, and the address will not point to the beginning of the function body, but to the import table, where there is an adapter in the form of JMP.
Let's execute the code and remember the value.
Under each system, it will be different, for example, in XP it will be the body of the original function, in the seven there will be an adapter for an analog from kernelbase. I got the value 9090F3EB, which corresponds to the following picture:

Now, let's take our debugger from the second part of the article, and in the OnBreakPoint method we will patch the body of this function with this code:
procedure TTestDebugger.HideDebugger;
const
PachBuff: array [0..2] of Byte =
(
$31, $C0, // xor eax, eax
$C3 // ret
);
var
Addr: Pointer;
begin
Addr := GetProcAddress(GetModuleHandle(kernel32), 'IsDebuggerPresent');
FCore.WriteData(Addr, @PachBuff[0], 3);
end;
procedure TTestDebugger.OnBreakPoint(Sender: TObject; ThreadIndex: Integer;
ExceptionRecord: Windows.TExceptionRecord; BreakPointIndex: Integer;
var ReleaseBreakpoint: Boolean);
var
JmpOpcode: Byte;
begin
if ExceptionRecord.ExceptionAddress =
Pointer(FCore.DebugProcessData.EntryPoint) then
begin
HideDebugger;
Here is a nuance, the address of the kernel32.dll library is the same for all applications, so the address of the IsDebuggerPresent function will be the same in the debugger and in the debugged application.
The meaning of the patch is to nullify the EAX register, through which the result of the function is returned and return to the code that calls this function.
We start the debugger, it will launch our application and, as a result of interfering with the process memory, the code in the FormCreate function of the debugger will not be detected. True, now the code that reads the first 4 bytes of this function will return not the number 9090F3EB, but the number 90C3C031, which corresponds to the opcodes of the patch.
How can we determine that the body of a given function is patched? In principle, we can read the first 4 bytes of this function from the kernel32.dll file located on the disk, however, in this case, when you open the library body, we can be replaced with the path to the same patched file and the check will say that everything is fine.
But there is another way that is rarely used in practice (I have met, if I’m not mistaken, only 1 time) and it consists in the following.
Since we cannot read the correct value from the disk, we can get it by reading the 4 bytes we need from the memory of some other process. There is of course a small chance that this process is also under the debugger and it intercepts the function we require in the same way, but it is very small.
As a result, we write this code:
function IsDebuggerPresent: BOOL; stdcall; external kernel32;
procedure TForm1.CheckIsDebugerPresent;
var
Snapshot: THandle;
ProcessEntry: TProcessEntry32;
ProcessHandle: THandle;
pIsDebuggerPresent: PDWORD;
OriginalBytes: DWORD;
lpNumberOfBytesRead: DWORD;
begin
pIsDebuggerPresent :=
GetProcAddress(GetModuleHandle(kernel32), 'IsDebuggerPresent');
Snapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if Snapshot <> INVALID_HANDLE_VALUE then
try
ProcessEntry.dwSize := SizeOf(TProcessEntry32);
if Process32First(Snapshot, ProcessEntry) then
begin
repeat
if ProcessEntry.th32ProcessID = GetCurrentProcessId then Continue;
ProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, False,
ProcessEntry.th32ProcessID);
if ProcessHandle <> 0 then
try
if ReadProcessMemory(ProcessHandle, pIsDebuggerPresent,
@OriginalBytes, 4, lpNumberOfBytesRead) then
begin
if OriginalBytes <> pIsDebuggerPresent^ then
begin
MessageBox(Handle, 'Функция IsDebuggerPresent перехвачена.',
PChar(Application.Title), MB_ICONERROR);
TerminateProcess(GetCurrentProcess, 0);
end;
if IsDebuggerPresent then
begin
MessageBox(Handle, 'Работа приложения под отладчиком запрещена.',
PChar(Application.Title), MB_ICONERROR);
TerminateProcess(GetCurrentProcess, 0);
end;
end;
finally
CloseHandle(ProcessHandle);
end;
until not Process32Next(Snapshot, ProcessEntry)
end;
finally
CloseHandle(Snapshot);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
CheckIsDebugerPresent;
CheckCodeProtect;
end;
Here I didn’t get smart and used the standard TlHelp32 features to get a list of processes, for example it’s enough.
Accordingly, another note in your notebook - whenever possible always check the integrity of critical API functions, in any way known to you, it is not necessary to do as I showed.
Yes, well, here too there is another nuance, under seven, calling IsDebuggerPresent from kernel32.dll will lead to calling the same function from kernelbase.dll, where it can also be patched, but think about it yourself.
As a result, after all the modifications, this function is tightly patronized by the integrity monitoring system and patching it on the forehead is already quite problematic. How to get around this piece of protection, I will show later, but now let's look at something else.
Detecting the connection of the debugger to the process.
Look, before that we started the application under the debugger, and what should we start it without the debugger, wait until all the checks for debugging pass and only after that connect the debugger to the application?
Yes, in this case, all of our code will not work, or rather, it will work, but partially.
Alternatively, to detect such an outrage, for example, you can set a timer and periodically call the CheckIsDebugerPresent procedure, but de facto, we do not need this to detect a connection. The fact is that when the DebugActiveProcess function is called in the debugger, the DbgUiRemoteBreakin function is always called in the debugged application. Knowing this, we can crank up the next trick.
We will patch ourselves, or rather, the body of the DbgUiRemoteBreakin function, adding a transition to the address of the TerminateProcess function at its beginning, so as soon as the debugger is connected to the process, the process will end immediately.
We write the next block of code:
type
TDbgUiRemoteBreakinPath = packed record
push0: Word;
push: Byte;
CurrProc: DWORD;
moveax: byte;
TerminateProcAddr: DWORD;
calleax: Word;
end;
procedure TForm1.BlockDebugActiveProcess;
var
pDbgUiRemoteBreakin: Pointer;
Path: TDbgUiRemoteBreakinPath;
OldProtect: DWORD;
begin
pDbgUiRemoteBreakin :=
GetProcAddress(GetModuleHandle('ntdll.dll'), 'DbgUiRemoteBreakin');
if pDbgUiRemoteBreakin = nil then Exit;
Path.push0 := $006A;
Path.push := $68;
Path.CurrProc := $FFFFFFFF;
Path.moveax := $B8;
Path.TerminateProcAddr :=
DWORD(GetProcAddress(GetModuleHandle(kernel32), 'TerminateProcess'));
Path.calleax := $D0FF;
if VirtualProtect(pDbgUiRemoteBreakin, SizeOf(TDbgUiRemoteBreakinPath),
PAGE_READWRITE, OldProtect) then
try
Move(Path, pDbgUiRemoteBreakin^, SizeOf(TDbgUiRemoteBreakinPath));
finally
VirtualProtect(pDbgUiRemoteBreakin, SizeOf(TDbgUiRemoteBreakinPath),
OldProtect, OldProtect);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
BlockDebugActiveProcess;
CheckIsDebugerPresent;
CheckCodeProtect;
end;
As a result of such a patch, the following code will be placed at the beginning of the DbgUiRemoteBreakin function:

That is, roughly two parameters necessary for the TerminateProcess function are placed roughly on the stack (they go in the opposite order), this is the uExitCode parameter equal to zero and the hProcess parameter, which is replaced with the DWORD (-1) pseudo-handle, which means current process. After that, the EAX register is initialized with the address of the TerminateProcess function and it is called.
If we try to join the process using the debugger from the second part of the article, then all we can see is the arrival of the CREATE_PROCESS_DEBUG_EVENT event, but even at the time this event arrives, we can’t do anything with the debugged process, for example, an attempt to install BP will fail, and etc.
For most debuggers, this is enough.
Unfortunately, this is not an armor-piercing option, because nothing will prevent you from re-patching the body of our application by returning the original code back before calling DebugActiveProcess. (True, I have not seen this, but still ...)
Memory Breakpoint Bypass
As I already said, you can determine the presence of MBP by checking the security attribute of the PAGE_GUARD page. This is done by calling the VirtualQuery function, or you can simply reassign attributes by invoking VirtualProtect.
But there is another tricky way and it is called ReadProcessMemory. This is the same function with which we read data from the debugged process in the debugger. Its nuance is the following, if it tries to read the data from the page protected by the PAGE_GUARD flag, the data block of the corresponding page will be filled with zeros, and the cimus is that the EXCEPTION_GUARD_PAGE event will not be raised in the debugger. Such is the "silent check of the region." If we use it when checking the integrity of the application code, if the MVR is installed on it, the data is not considered correct and, as a result, the checksum does not converge with the expected one. Moreover, if the Hardware Breakpoint monitoring record is set to the address from where this function will read, the read / write debugger will also not receive notifications about its operation.
Therefore, we rewrite the CalcCheckSum function as follows:
function TForm1.CalcCheckSum(Addr: Pointer; Size: Integer): DWORD;
var
pRealData, pCursor: PByte;
I: Integer;
Dumee: DWORD;
begin
pRealData := GetMemory(Size);
try
ReadProcessMemory(GetCurrentProcess, Addr, pRealData, Size, Dumee);
Result := 0;
pCursor := pRealData;
for I := 0 to Size - 1 do
begin
if pCursor^ <> 0 then
Inc(Result, pCursor^)
else
Dec(Result);
Inc(pCursor);
end;
finally
FreeMemory(pRealData);
end;
end;
Thus, with one single function, we defend ourselves from BP, and from the Ministry of Internal Affairs, and even from NVR.
How to get around this?
Well, I will not complicate the application protection code anymore, this information is enough.
I’ll still dwell on some nuances at the end of the article, and now we’ll try to write a loader based on the debugger, given the moment that we agreed to consider the integrity verification code as impenetrable and therefore we will not patch the verification body.
We are writing a wireframe. Start and stop will look like this:
constructor TTestDebugger.Create(const Path: string);
begin
FCore := TFWDebugerCore.Create;
if not FCore.DebugNewProcess(Path, True) then
RaiseLastOSError;
FCore.OnCreateProcess := OnCreateProcess;
FCore.OnLoadDll := OnLoadDll;
FCore.OnDebugString := OnDebugString;
FCore.OnBreakPoint := OnBreakPoint;
FCore.OnHardwareBreakpoint := OnHardwareBreakpoint;
FCore.OnUnknownBreakPoint := OnUnknownBreakPoint;
FCore.OnUnknownException := OnUnknownException;
end;
destructor TTestDebugger.Destroy;
begin
FCore.Free;
inherited;
end;
I will skip the secondary handlers, they can be viewed in the source code of the example, in principle there is nothing new, everything has already been described in the last part of the article.
Our first task is to somehow disable the detection of the debugger by the application. Since the application checks the integrity of IsDebuggerPresent, and you can’t patch the check (by the condition of the task), we have only one option - change the value of the Peb.BeingDebugged parameter.
Let's do it with the following code:
procedure TTestDebugger.HideDebugger(hProcess: THandle);
var
pProcBasicInfo: PROCESS_BASIC_INFORMATION;
pPeb: PEB;
ReturnLength: DWORD;
begin
if NtQueryInformationProcess(hProcess, 0,
@pProcBasicInfo, SizeOf(PROCESS_BASIC_INFORMATION),
@ReturnLength) <> STATUS_SUCCESS then
RaiseLastOSError;
if not ReadProcessMemory(hProcess, pProcBasicInfo.PebBaseAddress,
@pPeb, SizeOf(PEB), ReturnLength) then
RaiseLastOSError;
pPeb.BeingDebugged := False;
if not WriteProcessMemory(hProcess, pProcBasicInfo.PebBaseAddress,
@pPeb, SizeOf(PEB), ReturnLength) then
RaiseLastOSError;
end;
Everything is simple here, we get the address of the process environment block, change the BeingDebugged parameter and write everything back. Therefore, the IsDebuggerPresent function stops responding to the debugger. The declaration of the structures used can be found in the demo source.
We completed the first stage, now the second - we need to somehow make the application not respond to the incorrectly entered code and show us the picture in any case.
We will do this:
You have probably changed the values of variables more than once in the debugger (this was described in the first part of the article). Here we will do something similar. As you remember, the JE instruction is responsible for displaying the picture, if you are coarser, imagine that we have a Boolean variable and the condition if value then..else, if we break off on such a condition, then we can control the execution conditions of the code, i.e. indicate, by changing the value variable, what exactly should be executed: then or else block.
The JE operator makes a decision on the transition based on just such a Boolean variable, although it is presented in the form of the ZF flag. If the flag is enabled, a jump to the new address occurs. Therefore, our task is to make the application interrupt on JE instructions so that we can change the value of this flag to what we need.
We do this by setting the HBP to the address of the JE instruction, because this is the only thing that cannot control our secure application. How to find out this address I will miss. In the example, the archive contains the crackme.exe executable file, I specially put it into the archive due to the fact that with each recompilation, and depending on the version of the dolphin and other things, this address will be different. In the compiled executable, this address is already calculated and is equal to the value 0x467840.
It remains to write the code:
procedure TTestDebugger.OnBreakPoint(Sender: TObject; ThreadIndex: Integer;
ExceptionRecord: Windows.TExceptionRecord; BreakPointIndex: Integer;
var ReleaseBreakpoint: Boolean);
begin
if ExceptionRecord.ExceptionAddress =
Pointer(FCore.DebugProcessData.EntryPoint) then
begin
Writeln;
Writeln(Format('!!! --> Process Entry Point found. Address: %p',
[Pointer(FCore.DebugProcessData.EntryPoint)]));
Writeln;
HideDebugger(FCore.DebugProcessData.AttachedProcessHandle);
FCore.SetHardwareBreakpoint(ThreadIndex, Pointer($467840), hsByte,
hmExecute, 0, 'wait JE');
end
else
begin
Writeln;
Writeln(Format('!!! --> BreakPoint at addr 0X%p - "%s"',
[ExceptionRecord.ExceptionAddress,
FCore.BreakpointItem(BreakPointIndex).Description]));
Writeln;
end;
end;
After that you need to process the interrupt on HBP and set the correct flag value:
procedure TTestDebugger.OnHardwareBreakPoint(Sender: TObject;
ThreadIndex: Integer; ExceptionRecord: Windows.TExceptionRecord;
BreakPointIndex: THWBPIndex; var ReleaseBreakpoint: Boolean);
var
ThreadData: TThreadData;
begin
Writeln;
ThreadData := FCore.GetThreadData(ThreadIndex);
Writeln(Format('!!! --> Hardware BreakPoint at addr 0X%p - "%s"',
[ExceptionRecord.ExceptionAddress,
ThreadData.Breakpoint.Description[BreakPointIndex]]));
FCore.SetFlag(ThreadIndex, EFLAGS_ZF, True);
Writeln;
end;
Well, that’s all, you can run it, enter any left value and enjoy the picture.
The result will be something like this:

So it usually happens, you think you wrote armor-piercing protection, and then once it costs on your knee, well, not always of course, but it does :)
Detecting Hardware BreakPoint:
I intentionally did not stop at detecting NVR within the framework of the protected application, for the reason that if such a check were there, I would have to write a fairly complicated bypass code. And in general, of course, it is advisable to check their presence, thus closing the debugger the possibility of normal operation.
The detection of the presence of HBP is quite simple, it can be implemented either through the same GetThreadContext and checking the DR7 register (if it is not empty, then HBP is standing), or so that we are not intercepted by an API function call, we can get the thread context by throwing an exception.
Here is the first option
procedure TForm1.CheckHardwareBreakPoint;
var
Context: TContext;
begin
Context.ContextFlags := CONTEXT_DEBUG_REGISTERS;
GetThreadContext(GetCurrentThread, Context);
if Context.Dr7 <> 0 then
begin
MessageBox(Handle, 'Обнаружен HardwareBreaakPoint.',
PChar(Application.Title), MB_ICONERROR);
TerminateProcess(GetCurrentProcess, 0);
end;
end;
And the second option, in which a debug exception is raised and information about the thread context is removed in the _except_handler handler.
type
// структура для восстановления
TSeh = packed record
Esp, Ebp, SafeEip: DWORD;
end;
var
seh: TSeh;
function _except_handler(ExceptionRecord: PExceptionRecord;
EstablisherFrame: Pointer; Context: PContext;
DispatcherContext: Pointer): DWORD; cdecl;
const
ExceptionContinueExecution = 0;
begin
if Context^.Dr7 <> 0 then
begin
MessageBox(0, 'Обнаружен HardwareBreaakPoint.',
PChar(Application.Title), MB_ICONERROR);
TerminateProcess(GetCurrentProcess, 0);
end;
// возвращаем регистры на место
Context^.Eip := seh.SafeEip;
Context^.Esp := seh.Esp;
Context^.Ebp := seh.Ebp;
// и говорим продолжить выполнение
Result := ExceptionContinueExecution;
end;
procedure TForm1.CheckHardwareBreakPoint2;
asm
// устанавливаем SEH фрейм
push offset _except_handler
xor eax, eax
push fs:[eax]
mov fs:[eax], esp
// заполняем данные для восстановления
lea eax, seh
mov [eax], esp
add eax, 4
mov [eax], ebp
add eax, 4
lea ecx, @done
mov [eax], ecx
// генерируем исключение
mov eax, [0]
@done:
// удаляем SEH фрейм
xor eax, eax
pop fs:[eax]
add esp, 4
end;
By the way, an interesting point. Pay attention to how much information comes to the exception handler. All this information is not available to us in the except handler, which is why I often call try..finally..except a short wrapper over SEH :)
Summarizing
Now you know several ways to deal with the debugger, though now you know the methods of counteracting them, but then there is an article for you to draw conclusions.
The source code with examples can be taken from this link: http://rouse.drkb.ru/blog/dbg_part3.zip
And on this, my task can be completed.
All that I wanted to tell about a debugger, I told. Initially, the truth was only one article planned, but you can see for yourself how much material you got :)
In the future, I will talk about using the debugger, but this will be a completely different story.
© Alexander (Rouse_) Bagel
Moscow, November 2012