Inline hook (monitoring any function)

Source: Internet
Author: User

I have already written two inline hook blog posts. The first article is: C/C ++ HOOK API (in-depth analysis of principles-loadlibrarya). the method in this blog post is to modify the first n Bytes of any function to jump to and enter our custom hook function. After executing our hook function, then directly call the hook function. The first method does not consider multithreading, so there will be problems in the multi-threaded environment. The second article is "inline hook api Preview (hot-patching)". The original intention of this article is to solve the problem of multithreading, because this method is always hooked until the program ends. Therefore, in the case of multithreading, the hook function will not be executed during the hook and unhook periods. However, the second method has limitations. It is mainly designed for many windows APIs, in the function header, there must be two idle bytes and five idle bytes before the first address of the function (generally five NOP commands). In this way, a short JMP and a long JMP can be implemented. So as to implement the hook, I will not go into details here.

 

Okay, this is the third time I 've written hook-related stuff. The first two hooks share the same thing: they both directly hook a function. That is to say, after the custom hook function is executed, it starts to be executed from the first part of the hook function, the hook function is redirected as soon as it enters. In this article, you need to hook and jump anywhere inside a function body. After executing our function, you can go back to the original position and continue to execute the unexecuted logic. At first glance, it seems that this method is no different from the previous two hooks, both of which are hooks. They are jump and then return to the function to be hooked. But when you think about it, you will find that the implementation method in this article is more complicated than the previous two hooks, because the hooks are anywhere in the function body, when we get back, we will not directly call the function to be hooked, but return to the previous hook. During this period, the return address of the hook function and the return address of the hook function are involved.

 

Having said so much, it may still be a bit dizzy. No matter why or what the Hook method can be used (the purpose will be explained at the end of this Article ), next we will first write some code to think about the purpose of this method in practice and compare it with the previous two hooks.

 

First, we need a custom hook function, which is the place to jump to after the hook function is hooked. This hook function is responsible for Hook and unhook, and can monitor registers, monitor the memory and manage the number of hooks for flexible hook requirements. Paste the Code directly:

# Include <iostream> <br/> # include <windows. h> </P> <p> # pragma warning (Disable: 4311) <br/> # pragma warning (Disable: 4312) </P> <p> # define hook_bytes 5 <br/> typedef unsigned int uint; </P> <p> uint hookaddr = 0; <br/> char old_code [hook_bytes]; <br/> char new_code [hook_bytes]; </P> <p> void printregisters (void ); </P> <p> bool hook (void) <br/> {<br/> DWORD dwflag; <br/> If (virtualprotect (void *) Ho Okaddr, hook_bytes, page_execute_readwrite, & dwflag) <br/>{< br/> memcpy (old_code, (void *) hookaddr, hook_bytes ); <br/> memcpy (void *) hookaddr, new_code, hook_bytes); <br/> virtualprotect (void *) hookaddr, hook_bytes, dwflag, & dwflag ); <br/> return true; <br/>}< br/> return false; <br/>}</P> <p> void unhook (void) <br/>{< br/> DWORD dwflag; <br/> If (virtualprotect (void *) hookadd R, hook_bytes, page_execute_readwrite, & dwflag) <br/>{< br/> memcpy (void *) hookaddr, old_code, hook_bytes ); <br/> virtualprotect (void *) hookaddr, hook_bytes, dwflag, & dwflag ); <br/>}</P> <p> namespace global <br/>{< br/> uint geax = 0; <br/> uint gebx = 0; <br/> uint gecx = 0; <br/> uint gedx = 0; <br/> uint GESP = 0; <br/> uint gebp = 0; <br/> uint GeSi = 0; <br/> uint Gedi = 0; </P> <p> uint Gret = 0; // temporary return address <br/> uint gtmp = 0; // save some temporary values <br/> uint gpar = 0; // normal return address of the hook function <br/> uint gcnt = 1; // number of current hooks <br/> uint gmax = 0; // The maximum number of hooks. If the value is 0, the hook is always set. <br/> bool bent = 0; // whether the hook function is enabled for the first time <br/>}</P> <p> void _ declspec (naked) hook_jmp (void) <br/>{< br/> _ ASM <br/>{< br/>__ entry: <br/> pushad <br/> {<br/> CMP Global: bent, 0 // unhook is required if no entry is made. <br/> je _ fi RST </P> <p> CMP Global: gmax, 0 // if it is 0, the hook logic is always enabled <br/> je _ second </P> <p> mov eax, Global: gcnt <br/> CMP eax, Global :: gmax // if the current number of hooks does not reach the maximum number, continue <br/> JL _ second </P> <p> mov Global: gcnt, 1 // reset state <br/> mov Global: bent, 0 // reset state <br/> mov Global: gmax, 0 // reset state </P> <p> mov eax, Global: gpar // normal return address of the hook function <br/> mov Global: GREt, eax // prepare to jump to the upper-layer call of the hook function and end the hook <br /> Popad <br/> JMP _ RET <br/>}</P> <p >__ first: <br/> // save important register values <br/> {<br/> popad <br/> mov Global: geax, eax <br/> mov Global :: gebx, EBX <br/> mov Global: gecx, ECx <br/> mov Global: gedx, EDX <br/> mov Global: GESP, ESP <br/> mov Global: gebp, EBP <br/> mov Global: GeSi, ESI <br/> mov Global: Gedi, EDI <br/>}</P> <p> // enter for the first time, unhook and monitor the status <br/> pushad <br/> {<br/> mov Global:: bent, 1 // Record status </P> <p> mov EDI, Global: gebp // EBP of the hook function <br/> mov eax, [EDI + 4] // return address of the hook function (its upper-layer call address) <br/> mov Global: gpar, eax // Save the return address <br/> mov ESI, _ ENTRY // change the return address of the hook function to <br/> mov [EDI + 4], ESI // The first address of this function, which can be returned to this function after the <br/> // residual logic of the function is executed, determine whether to <br/> // hook is required. </P> <p> call printregisters // print register value [test], or other <br/> call unhook // unhook </P> <p> mov eax, hookaddr // obtain the hook memory address <br/> mov Global: GREt, eax <br/>}< br/> popad </P> <p> POP Global:: gtmp // remove the return address of this function, and set the address of the hook <br/> JMP _ RET // The return address of this function, so as to achieve the jump </P> <p >__ Second: <br/> // The second entry, continue to hook, this entry is returned by the hook function ret, no new RET address is pushed to the stack <br/>{< br/> mov Global: bent, 0 // set the status <br/> Add Global: gcnt, 1 // increase the hook count </P> <p> call hook // hook </P> <p> mov eax, Global :: gpar // set the return address of the hook function to this function <br/> mov Global: GREt, eax // return address, to achieve normal function flow <br/>}< br/> popad </P> <p >__ RET: <br/> push Global :: gret // modify the return address of this function <br/> RET <br/>}</P> <p> void sethookbytes (uint ADDR) <br/>{< br/> hookaddr = ADDR; <br/> new_code [0] = (char) 0xe8; // call command machine code <br/> (uint &) new_code [1] = (uint) hook_jmp-ADDR-5; // calculate the jump offset <br/>}</P> <p> void printregisters (void) <br/>{< br/> printf ("eax = 0x % 08x/N", Global: geax ); <br/> printf ("EBX = 0x % 08x/N", Global: gebx); <br/> printf ("ECx = 0x % 08x/N", Global:: gecx); <br/> printf ("edX = 0x % 08x/N", Global: gedx ); <br/> printf ("ESP = 0x % 08x/N", Global: GESP); <br/> printf ("EBP = 0x % 08x/N", Global:: gebp); <br/> printf ("ESI = 0x % 08x/N", Global: GeSI ); <br/> printf ("EDI = 0x % 08x/N", Global: Gedi); <br/>}

As shown above, the hook_jmp function is our custom hook function. When the hook function is hooked, it will jump to this function and execute the relevant logic, I added a detailed comment above. It should be easy to understand. Let's take a look at how to use this method first. The Code is as follows:

Void testhook (void) <br/>{< br/> printf ("this is a Hook Test 1. /n "); <br/> printf (" this is a Hook Test 2. /n "); <br/> printf (" this is a Hook Test 3. /n "); <br/> printf (" this is a Hook Test 4. /n "); <br/> printf (" __________________/N "); <br/>}</P> <p> int main (void) <br/>{< br/> uint hook_addr = 0x0042ec7b; <br/> sethookbytes (hook_addr); </P> <p> Global: gmax = 2; <br/> If (Hook () <br/>{< br/> testhook (); <br/> testhook (); <br/>}< br/> system ("pause"); <br/> return 0; <br/>}

As shown above, the testhook function is the function to be hooked. In the main function, 0x0042ec7b is the address of the second printf call in the testhook function, which may be different on your machine. This is just for testing. The specific disassembly code of the testhook function is as follows:Void testhook (void) <br/>{< br/> 0042ec50 push EBP <br/> 0042ec51 mov EBP, esp <br/> 0042ec53 sub ESP, 0c0h <br/> 0042ec59 push EBX <br/> 0042ec5a push ESI <br/> 0042ec5b push EDI <br/> 0042ec5c Lea EDI, [ebp-0C0h] <br/> 0042ec62 mov ECx, 30 h <br/> 0042ec67 mov eax, 0 cccccccch <br/> 0042ec6c rep STOs dword ptr es: [EDI] <br/> printf ("this is a Hook Test 1. /n "); <br/> 0042ec6e push offset string" this is a Hook Test 1. /n "(487e24 h) <br/> 0042ec73 call @ ILT + 4550 (_ printf) (42d1cbh) <br/> 0042ec78 add ESP, 4 <br/> printf ("this is a Hook Test 2. /n "); <br/> 0042ec7b push offset string" this is a Hook Test 2. /n "(487e08h) <br/> 0042ec80 call @ ILT + 4550 (_ printf) (42d1cbh) <br/> 0042ec85 add ESP, 4 <br/> printf ("this is a Hook Test 3. /n "); <br/> 0042ec88 push offset string" this is a Hook Test 3. /n "(487 dech) <br/> 0042ec8d call @ ILT + 4550 (_ printf) (42d1cbh) <br/> 0042ec92 add ESP, 4 <br/> printf ("this is a Hook Test 4. /n "); <br/> 0042ec95 push offset string" this is a Hook Test 4. /n "(export dd0h) <br/> 0042ec9a call @ ILT + 4550 (_ printf) (42d1cbh) <br/> 0042ec9f add ESP, 4 <br/> printf ("__________________/N"); <br/> 0042ecalcium push offset string "_____________________. /n "(mongodb4h) <br/> 0042eca7 call @ ILT + 4550 (_ printf) (42d1cbh) <br/> 0042 ECAC add ESP, 4 <br/>}< br/> 0042 ecaf pop EDI <br/> 0042ecb0 pop ESI <br/> 0042ecb1 pop EBX <br/> 0042ecb2 add ESP, 0c0h <br/> 0042ecb8 cmp ebp, esp <br/> 0042 ecba call @ ILT + 3570 (_ rtc_checkesp) (42cdf7h) <br/> 0042 ecbf mov ESP, EBP <br/> 0042ecc1 pop EBP <br/> 0042ecc2 RET

We hook the code of line 18th (0042ec7b). sethookbytes constructs a 5-byte CALL statement. 0xe8 is the machine code of the Call Command, the last four bytes are the call offset (5 bytes occupied by the destination address-Current address-call command ).

 

In the main function, the number of hooks is set after the five bytes of the hook are constructed. For example, the Code of the main function contains 15th rows: Global: gmax = 2, it will be hooked twice. The second line of the code of the main function calls the hook function, writes the call command of 5 bytes to 0042ec7b, and saves the original code of 0042ec7b to old_code. Then, we can call the testhook function to test the hook process. The final output result is:

This is a Hook Test 1.
Eax = 0x00000017
EBX = 0x7ffdc000
ECX = 0x8df97741
EdX = 0x00499148
ESP = 0x0012fd84
EBP = 0x0012fe54
ESI = 0x00000000
EDI = 0x0012fe54
This is a Hook Test 2.
This is a Hook Test 3.
This is a Hook Test 4.
______________________
This is a Hook Test 1.
Eax = 0x00000017
EBX = 0x7ffdc000
ECX = 0x8df97741
EdX = 0x00499148
ESP = 0x0012fd84
EBP = 0x0012fe54
ESI = 0x00000000
EDI = 0x0012fe54
This is a Hook Test 2.
This is a Hook Test 3.
This is a Hook Test 4.
______________________
This is a Hook Test 1.
This is a Hook Test 2.
This is a Hook Test 3.
This is a Hook Test 4.
______________________

 

We can see that the hook_jmp function was executed when the testhook function was called the previous two times, and the printregisters function was called to print out the registers, and then return to testhook, output the following three strings. When testhook is called for the third time after two hooks, no registers are output or hooked.

 

Let's take a look at several key points in hook_jmp:

Rows 104th to 110:This assembly code is mainly used to save the normal return address of the testhook function (the address of the next command to call testhook in the main function) to global :: in the gpar variable, and write the first address of hook_jmp (that is, the address indicated by the _ ENTRY tag) into the memory of the return address of the testhook function. In this way, after unhook is executed and testhook is executed, it can return to hook_jmp to further determine whether the next hook is required. If you do not need to hook (the maximum number of hooks has been reached), two lines of assembly code from 79th to 80 will be executed, the compilation Code sets the return address of the hook_jmp function to the normal return address of the testhook function, that is, the address of the next Assembly Code to call the testhook function in the main function (if you are not clear about the RET instruction principle, please refer to the first two hook articles or relevant materials ). In this way, when the hook is no longer needed, it can be smoothly returned from the hook_jmp function and directly jump to the scope of the main function. In this way, the entire call process conforms to the original call process.

 

Row 3:This code performs the unhook operation after calling the printregister function and copies the original five-byte code to the memory of the corresponding Code address of the testhook function, in this example, it is the address of the second printf function call in the testhook function. After unhook, the two sentences in lines 115th to 116 are similar to those in lines 79th to 80, except that the hook's memory address is set to the return address of hook_jmp, in this way, when you enter the testhook function for the first time and return the result after execution, you can jump to the hook address (the address of the 2nd printf call in the hooktest function) and continue to execute the remaining logic.

 

Rows 131st to 132:These two Assembly codes are the same as those of lines 79th to 80. They both set the corresponding Code address in the main function to the RET return address of the hook_jmp function, in this way, you can directly jump from hook_jmp to the main function and continue to execute it. This means that testhook is successfully called.

 

Therefore, the hook_jmp function is used twice to monitor some data for the first time. In this example, only related registers are monitored, and the specified memory address can be monitored. Upon first entry, the return address of the hook function (testhook function) will be saved and changed to the first address of the hook_jmp function, this is done to enter the hook_jmp function for the second time after the testhook function is executed. Then, after the second entry, the first step is to determine whether a hook is still required. If no hook is required, it is directly returned to the main function. If the hook function is to be continued, the hook function is called again, then jump to the main function. In this way, a strict call process is formed, and everything looks harmonious, similar to the principle of buffer overflow attacks.

 

The hook_jmp function needs to pay attention to the storage of registers. Otherwise, the output register value is not the register status when the testhook function is executed to the hook location, thus losing the meaning of monitoring.

 

The principle is actually relatively simple, and the construction is a little more detailed. The biggest difference from the first two hooks is that you need to manually modify the RET return address to achieve the purpose of the hook, unlike the previous two hooks, to return to the function to be hooked after entering the hook function, you only need to call the function directly and do not need to maintain the return address of the RET command. In addition, since the Hook method in this article is similar to the hook method in the first article, this method does not support multi-threaded environments.

 

In addition, the method in this article has some limitations:

1. you cannot hook code other than the function braces, that is, the code before and after the braces in the disassembly code, because the Hook method in this article needs to get the RET address through the EBP of the hook function, if the EBP-related code is hooked, the correct RET return address will not be obtained. In addition, the Code beyond the braces of the hook is of little significance.

2. The stack frame of the hook function cannot be optimized by the compiler; otherwise, the RET address and EBP are not obtained.

3. the hook Code address must be the first address of the sequence of machine code of a Compilation instruction. This article is not compatible with any code address hook. Of course, any code address can be implemented, however, it does not make much sense to monitor the status in practice. Therefore, you need to clearly know that the address of your hook code is legal and will not destroy the original code logic.

 

Well, the logic and principle of the entire hook are almost introduced, and a flowchart is attached:

If the testhook function is hooked, it will not directly return to the main function, but will first enter the hook_jmp function, and then the hook_jmp function will return to the main function, testhook has the right to directly return to the main function.

 

I almost finished the introduction, and finally talked about the purpose of the hook. In fact, I initially wanted to use it for anti-plug-in detection, so I wrote such a set of hook logic, it is convenient to monitor some key anti-plug-in Detection Points of clients running plug-ins without debugging client programs, such as whether a anti-plug-in detection logic is executed, monitoring of some detection result values. The plug-in is generally equipped with powerful protective cases, which have anti-debugging functions. Typical examples are the VMP shell and UPX shell. Of course, before that, I tried to debug and track clients with plug-ins, but the workload for shelling and bypassing the anti-debugging mechanism is relatively large, our consistent idea is to achieve a good anti-plug-in effect with the least amount of work. So I wrote this set of hook rules to check whether the current anti-plug-in system is cracked and bypassed. With this set of rules, you can easily set up observation points and monitor related logic and data. Of course, you can also use more functions. Here we will not describe them one by one. In addition, we have also written a visualization tool for this purpose, it is easy to use at work and has successfully detected multiple plug-in cracking mechanisms,

 

The hook logic interface is displayed on the left, and the tool page on the right is used to call any function. It also supports viewing modules and viewing and modifying memory. Basically enough. --

 

Now, this article is over. Due to the limited level, there may be bugs. I hope you can advise me. Thank you very much!

 

 

 

 

 

 

 

 

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.