Realization of API Hook

xiaoxiao2021-03-05  45

I. For most Windows developers, how to intercept the call of the API function in the Win32 system, because this will be a comprehensive test for computer knowledge you have. In particular, some knowledge that is not common to use when using RAD to develop software development, including operating system principles, assembly language or even machine directives (it is a little horror, but this is truth).

In the currently used Windows operating system, like Win 9x and Win NT / 2K provide a more robust mechanism to make each process's memory address space is independent of each other, that is, some of the processes. Effective memory addresses are meaningless to another process, which greatly increases the stability of the system. However, this also makes the difficulty of working system-level API interception greatly increase.

Of course, I will refer to the more elegant interception method, and the dynamic interception of the API call is achieved by modifying the image in the memory in memory. Instead of using a relatively violent way, directly on the executable The machine code is rewritten in disk storage.

Second, the API hook system general framework usually, we call this process of intercepting the call of the API called to install an API hook (API HOOK). An API hook is basically consisting of two modules: one is a hook server module, typically in the form of EXE; one is a hook driver module, typically a DLL form.

The hook server is primarily responsible for injecting the hook drive to the target process, making the hook drive run in the address space of the target process, and the hook driver is responsible for the actual API interception process to work in our concern API Before or after the function calls, you can do some of our hopes. An example of a relatively common API hook is some of the features that must be in real-time translation software (like Jinshan Words): screen scratches. It mainly intercepts the GDI function in the Win32 API, gets the string in their input parameters, and then displayed in your own window.

For the above two parts regarding the API hook, we have the following two points to focus on: What DLL injection technology is selected, as well as what API interception mechanism.

Third, the use of injection technology is independent of the address of each process in the Win32 system, so we can't make effective modifications to another process code in a process, but if you want to complete the API hook, you must . Therefore, we must take some unique means that the API hook (accurately the hook driver) can become part of the target process, and there is a significant modification of the target process data and code.

Several injection methods that are usually available:

1. Using the Registry If we are ready to intercept the process connected to User32.dll, that is, the API in the user32.dll (the application of the general graphics interface is in line with this condition), then you can simply put your hook drive DLL. The name is added as a value in the following registry: HKEY_LOCAL_MACHINE / SOFTWARE / Microsoft / WINDOWSNT / CURRENTVERSION / Windows / Appinit_dlls value can be a single DLL file name, or a set of DLL file names, adjacent names Interval with commas or spaces. All DLL identified by this value will be loaded when the condition-consisted application is started. This is an operating system built-in mechanism. It is more dangerous relative to other ways, but it also has some obvious disadvantages: this method is only suitable for NT / 2K operating system, obviously looks at the name of the key; If you need to activate or stop the injection of the hook, this seems too inconvenient; the last point is also clear, you can't use this method to inject DLL into the DLL, such as console applications, etc. without using the user32.dll. . In addition, whether or not, the hook DLL will inject every GUI application, which will result in a decline in the performance of the entire system! 2. Building a system-wide Windows hook To inject a DLL to a process, a simple method is also a relatively simple method is based on the standard Windows hook. The Windows hook is generally implemented in the DLL, which is a basic requirement for a global Windows hook, which is also in line with our needs. When we successfully call the SetWindowsHooKex function, a type of message hook is installed in the system, which can be a process or all processes in the system. Once this type of message is generated in a certain process, the operating system automatically puts the DLL image of the hook to the address space of the process, so that the message callback function (specified in the parameter of SETWINDOWSHOKEX) can properly properly Treatment, here, of course, is of course not processing for messages, so just put the message hook backwards in the message callback function, but the DLL we need has successfully injected the target process. The address space can so you can complete the follow-up.

We know that different processes cannot be directly shared directly because they are active in different address spaces. But in Windows hook DLL, there are some data, such as Windows hook handle HHook, which is returned by the SetWindowsHookEx function it is to, and will be used as an argument in CallNextHookEx function and UnhookWindoesHookEx function, apparently using the process SetWindowsHookEx function and use CallNextHookEx function The process is generally not the same process, so we must be able to make the handle in all address spaces, that is, its value must have to be between the processes hooked by these hooks DLLs. shared. In order to achieve this, we should store it in a shared data area.

In VC , we can use the pre-compiled instruction #pragma data_seg to create a new segment in the DLL file, and set the properties of this segment to "Shared" in the DEF file, which has established a shared data segment. It's not so lucky for those who use delphi: there is no similar relatively simple method (maybe there, but I have not found). However, we can still use memory image technology to apply for memory area that can be shared using a process, mainly using the two functions of CreateFileMapping and MapViewOffile, which is a universal approach, suitable for all development languages, as long as it can directly or Indirect using the API of Windows. In Borland's BCB, there is an instruction #pragma codeseg in VC . The #pragma Data_SEG instruction is a bit similar, but I can play the same role, but I tried it, there is no effect, and the BCB online help is also There are not many mentioned, I don't know how to use it correctly (maybe it is another directive, huh, huh).

Once the hook DLL loads the address space that goes into the target process, it is unable to stop working before we call UNHOOKWINDOWSHOKEX functions, unless the target process is turned off.

This type of DLL injection has two advantages: this mechanism is supported in WIN 9X / ME and WIN NT / 2K, which is expected to be supported in future versions; hook DLL can be in needless, It can be uninstalled by our initiative to invoke UnHookWindowsHookex, which is convenient than using the mechanism to use the registry. Although this is a fairly simple method, it also has some obvious disadvantages: first worth noting that the Windows hook will reduce the performance of the entire system, because it adds an additional system in message processing; second Only when the target process is ready to accept some kind of message, the DLL in which the hook is located will be mapped to the address space of the process, and the hook can really start playing, so we have to have a whole life cycle of some processes. The API call situation is monitored, and this method will clearly miss the call of certain APIs.

3. Using the CreateremoteThread function in I seem to be a quite a great method, but unfortunately, createremoteThread This function can only be supported in the Win NT / 2K system, although this API can be called safely in Win 9X. Not wrong, but it doesn't do anything except for returning an empty value. The injection process is also very simple: We know that any process can use LoadLibrary to dynamically load a DLL. But the problem is how we let go of the target process (possibly running) to load our hook DLL down to our control (drive)? There is an API function CreateRemThetRead, which can be built and running a remote thread in a process - this seems to be in the injection. Go down!

Calling the API requires a thread function pointer as a parameter, the prototype of the thread function is as follows: Function ThreadProc (LPParam: Pointer): DWORD, let's take a look at LoadLibrary's function prototype: Function LoadLibrary (lpfilename: Pchar): hmodule. Find it! These two function prototypes are almost the same (in fact, whether the return value is not the same, because we can't get the return value of the remote thread function) Load the hook DLL in the target process. Similar to this, when we need to uninstall the hook DLL, you can use Freelibrary as a thread function, uninstall the hook DLL in the target process, everything seems to be very simple and convenient. By calling the getProcAddress function, we can get the address of the LoadLibrary function. Since LoadLibrary is a function in kernel32, the mapping address of this system DLL is the same for each process, so the address of the LoadLibrary function is also the case. This will ensure that we can pass the address of the function as a valid parameter to createremoteThread. FreeLibrary is also the same.

AddrofloadLibrary: = getProcaddress (GetModuleHandle ('kernel32.dll'), 'loadinglibrary');

HREMOTETHREAD: = CreateremoteThread (HtargetProcess, NIL, 0, AddrofloadLibrary, Hookdllname, 0, NIL);

To use CreateRemoteThread, we need the handle of the target process as a parameter. When we use the OpenProcess function to get the handle of the process, it is often desirable to have a full access to this process, which is to open the process as a logo as a process_all_access. But for some system-level processes, it is obviously not possible, and only one empty handle (zero) can be returned. To do this, we must set itself to the privilege of the trial level, which will have the largest access rights, so that we can make some necessary operations for these system-level processes.

4. Injection of DLL through Bho Sometimes, we want to inject the DLL object is just Internet Explorer, very lucky, the Windows operating system provides us with a simple archiving method (this guarantees its reliability!) - Using Browser Helper Objects (BHO). A Bho is a COM object implemented in the DLL, which is mainly an IObjectWithSite interface, and whenever IE runs, it automatically loads all COM objects that implements the interface.

Fourth, intercepting mechanisms in the system level of hook applications, there are two types of API interception mechanisms - the interception of kernel and user-level interception. The kernel hook is mainly implemented by a driver of a core mode. It is clear that its function should be most powerful, can capture any details of the system activity, but the difficulty is also large, not within the scope of this article (especially for me This person using Delphi has not been involved in this area, so it can't discuss, huh, huh).

The user-level hook is usually the interception of the entire API in the ordinary DLL, which is the focus. Intercept the call of the API function, generally can have the following methods:

1. Agent DLL (an easy way to think of Trojm Horse is to replace the DLL where the original output we prepare to intercept the API in the same name. Of course, the agent DLL will be the same as the original, but if you think of the DLL The hundreds of functions may be output, and we should understand that this method is not high, it is estimated to be exhausted. In addition, we have to consider the version of the DLL, very trouble. 2. Rewind execution The code has many interception methods based on the rewriting of executable code, one is to change the function address used in the CALL instruction, which is a bit difficult, and it is easier to erroneously. Its basic idea is to retrieve all you in memory. The Call instruction of the API to intercept, and then change the original address into the address of your own function.

Another method of rewritten method is more complicated. Its main implementation step is to first find the address of the original API function, and then replace the few bytes starting with the function (sometimes have to be Use an int directive) so that the call to the API function can be turned to our own function call. This method is to involve a series of stacks and outstanding operations out of the bottom-up operation, clearly the knowledge of our assembly language and operating system. This method is similar to the infection mechanism of many file viruses.

3. Intercepting another optional method as a debugger is to place a tested breakpoint in the target function, so that the process runs to the debug state. However, such some problems have also come, and more important is that the production of debugging will hang all the threads in the process. It also requires an additional debug module to process all exceptions, the entire process will run in debug state until it runs.

4. This method of rewriting the input address of the PE file is mainly due to the good structure of executable (including Exe file and DLL file) used in the Windows system, so it is Quite robust, it is easy. To understand how this method works, first you have to understand the PE file format.

The structure of a PE file is roughly as follows: General PE files starting a DOS program, when your program is running in a Windows environment, it will display "this program cannot bern in dos mode" Warning statement; then the DOS file header starts the real PE file content, first is a data called "image_nt_header", which is a number of messages on the entire PE file, the end of this data is called Data Directory's data sheet, through it quickly locates the address of some PE files; after this data, it is a list of "image_section_header", each of which describes the following segment Information; then it is the most important segment data in the PE file, and information such as executing code, data, and resources, etc. are stored in these segments.

In all of these segments, there is a paragraph (input data segment) called ".idata" worth paying attention, this segment contains some data lists called an input address table (IAT, Import Address Table) Each DLL where the API loaded with an implicit manner has an IAT and corresponds, and an address of an API is also corresponding to one of IAT. When an application is loaded into the memory, the corresponding assembly instruction is generated for each API function: JMP DWORD PTR [xxxxxxxx]

If _Delcspec (import) is used in VC , then the corresponding instructions become:

Call DWORD PTR [xxxxxxx].

In any case, the above square brackets are always an address, pointing to an entry in the input address table, is a DWORD, but is this DWord is the real address of the API function in memory. So we want to intercept an API call, as long as it is easy to change the DWORD to our own function, then all the calls about this API will go to our own function, and the intercepting work will declare a successful success. . It should be noted here that the call agreement of the custom function should be the call agreement of the API, which is stdcall, and the definition in Delphi is register, and they have a big difference in the method of transfer of parameters.

In addition, the parameters of custom functions are generally the same as the original API function, but this is not necessary, and this will have some problems at some time, I will mention it later. So to intercept the call of the API, first we have to get the corresponding IAT address. The system loads a process module into memory, in fact, putting the PE file is almost unmounted to the address space of the process, and the module handle hmodule is actually the address in the memory, some of the data in the PE file. The address of the item is the offset relative to this address, so it is called a relative virtual address (RVA, Relative Virtual Address).

So we can start from HMODULE and get an address of the IAT after a series of address offset. However, I have a simple way, it uses an existing API function imageDirectoryEntryTodata, which helps us take less steps while positioning IAT, saving the offset address, walking into the detour. But purely using RVA starts from hmodule to locate the IAT's address is not trouble, and this is more helpful to understand the structure of the PE file. The API function mentioned above is output in DBGHELP.DLL (this is starting from Win 2K, before this is provided by imagehlp.dll), detailed introduction to this function can see MSDN.

After finding IAT, we only need to traverse it, find the API address we need, then use our own function address to overwrite it, and give a corresponding source code:

Procedure redirectapical; var importdsc: pimage_import_descriptor; firstthunk: pimage_thunk_data32; s: dword; begin

// get a first address input list described structure, each of a DLL corresponds to a structure in ImportDesc: = ImageDirectoryEntryToData (Pointer (HTargetModule), true, IMAGE_DIRECTORY_ENTRY_IMPORT, sz); while Pointer (ImportDesc.Name) <> nil do begin // Judgment is the desired DLL input description

IF stricomp (Pchar (Dllname), Pchar (HtargetModule ImportDesc.name) = 0 THEN BEGIN

// Get the first address of IAT Firstthunk: = PIMAGE_THUNK_DATA32 (HtargetModule ImportDesc.Firstthunk);

While firstthunk.func <> nil do begin

if firstthunk.func = OlddRessofapi Then Begin

/ / Find the matching API address ... // Remove the address of the API

Break;

END;

Inc (firstthunk);

END;

END;

INC; ImportDesc

END;

END;

Finally, I want to point out if we manually perform the exit target process of the hook DLL, then call the function to change the address to the original address before exiting, that is, the real address of the API, because once your DLL exits, rewritten new The address will point to a meaningless memory area, if the target process is used to use this function, it will obviously appear an illegal operation.

5. The preparation of the replacement function is completed in front of the key, and an API hook is basically completed. However, there are some related things that need us to study, including how to do an alternative function. Here is the step of doing a replacement function: First, it is not neosted, we first assume that there is such an API function, its prototype is as follows:

Function SomeApi (Param1: Pchar; Param2: Integer): DWORD;

Then establish a function type with the same parameters and return values:

TYPE FUNCTYPE = Function (param1: pchar; param2: integer): DWORD;

Then we store the address of the SomeApi function in the OlddRess pointer. Then we can use the code for writing the function:

Function DummyFunc (param1: pchar; param2: integer): DWORD; begin ...

/ / Do some before calling

// Call the replaced function, of course, the result: = functype (OldDress) (PARAM1, PARAM2)

/ / Do some of the calls after calling

END;

Let's save the address of this function into Newaddress, then override the address of the original API with this address. This is actually calling our own functions when the target process calls the API, where we can do some operations, then call the original API function, the result is like anything. Of course, we can also change the value of the input parameters, or even the screen adjustment of this API function.

Although the above method is feasible, there is a significant shortcoming: the method of making this replacement function does not have versatility, only for a small number of functions. If only a few APIs are to intercept, just do it several times as described above. However, if there are a variety of APIs to handle, their parameters and types, and the type of return value are different, and this method is still too efficient.

Indeed, the above given is just a simple and easier way to think, just a basic architecture of a replacement function. As I mentioned earlier, the replacement function is not the same as the parameter type of the original API function. Generally, we can design a function that does not call the parameters and has no return value. Through certain skills, it can adapt to various various The API function calls, but this requires you to know about the assembly language. First, let's take a look at the system stack before performing a function (the function of the function is stdcall), the call parameters of the function are pressed into the stack in the order from right to left (stack is low-ended by high-end Developed), but also pressed into a function returned address. Before entering the function, the ESP is pointing to the return address. Therefore, we can get the call parameters of this function from ESP 4, and each parameter is incremented by 4. In addition, when returned from the function, the return value of the function is generally placed in Eax.

Understand the above knowledge, we can design a relatively universal replacement function, which utilizes the characteristics of Delphi's embedded assembly language.

PROCEDURE DUMMYFUNC;

ASM Add ESP, 4 MOV EAX, ESP // Get the first parameter

MOV EAX, ESP 4 // Get the second parameter ...

/ / Do some processing, here to ensure that the ESP will restore it later

Call OldDress // Call the original API function ...

/ / Do some other things

END;

Of course, this replacement function is still relatively simple, you can call some functions or processes written in the OP language to complete some more complex operations (if you use compilation, you can get you dying) However, these functions should be uniformly set to stdcall mode, which allows them to deliver parameters with the stack, so you just need to master the changes in the stack. If you store the machine instructions corresponding to the above assembly code in a byte array, then use the address of the array as a function address, the effect is the same.

Sixth, after the post, I do an easy thing, especially for my use of Delphi, in order to solve a problem, often in the OP, C and assembly language, Middle East Chess, in the program debugging I still have some unexpected things in time, and I have a hand. However, I have finally made a prototype of an API hook, or a lot of knowledge in my computer system, and it is not shallow. I first wrote this article, I just want to translate an English data from the online Down (the URL is http://www.codeproject.com/, the article named "API Hook Revealed", the sample source code is VC Written, here you have to admire foreigners, the article is very depth, and every detail is very detailed).

转载请注明原文地址:https://www.9cbs.com/read-32916.html

New Post(0)