"UndocuMented Windows 2000 SECRETS" translation --- Chapter 5 (3)

xiaoxiao2021-03-06  24

Chapter 5 Monitoring Native API Call

Translation: kendiv (fcczj@263.net)

Update:

Thursday, March 24, 2005

Disclaimer: Please indicate the source and guarantee the integrity of the article, and all rights to the translation.

The maximum feature of the HOOK mechanism for this book is that it is a full data drive (Data-Driven). Simply add a new API symbol table that adapts to the new version of Windows 2000. Moreover, the call to these additional API functions can be recorded at any time by adding a new API function to an APDTFORMATS [] array. This does not need to write any additional code --- API SPY action can be determined by a set of strings! However, in defining a new format string must be careful because W2k_spy.sys is the driver running in the kernel mode. Because in this system level, the system cannot be moderately handled. Give the Win32 API function to provide an invalid parameter is not a problem ----- You will receive an error prompt window, and the program will be automatically terminated by the system. In the kernel mode, a tiny access violation will trigger a system blue screen. Therefore, be careful. Where needed, if there is no correct formatting control ID or missing this ID will make your system completely collapse. Even if a simple string is sometimes fatal!

Now only SPYHOKInitializeEx () in SpyhookInitializeEx () has not been discussed, this code is identified by Spyhook2 and Spyhook9. An interesting feature of this code is that when spyhookInitializeex () is called, they never be executed. After entering SpyHOKInitializeEx (), the function code will skip this whole code, and then start recovery at the SpyHook9 tab, which contains the initialization code of the AspyHooks [] array. This large ASM code can only be entered through the ASPYHOOKS [] array. Say, I will show how these entry points are connected to SDT.

One of my important goals is to make it completely non-invasive when designing this ASM code. Intercept operating system calls are very dangerous, because you never know if the code called the code relies on some unknown features of the Calling Context. In theory, these ASM code are fully compliant with the __stdcall agreement, but there is still an error. I have to choose to put the original Native API handling routine in almost exactly the same environment, which means that these raw functions will use the initial parameter stack and access all CPU registers, just like they are modified. Of course, you must accept the risk of minimum due to the insertion of Hook, otherwise, monitoring will not be possible. Here, meaningful changes are the returning address in the stack. If you are turned back to Figure 5-3, you will find that when you enter a function, the call back address of the caller is not at the top of the stack. SPYHOKInitializeEx () Hook Dispatcher takes this address and writes its own spyhook6 tag to here. Therefore, the original Native API processing routine will be interrupted, then enter SPYHOK6, so Hook Dispatcher can check the parameters of the original Native API processing routine and the data it wants to return.

Before calling the original processing routine, Dispatcher will create a spy_call (see List 5-3) control block, which contains the parameters that it will be used later. Some of these parameters are used when the API call is properly recorded, and some of them provide information about the caller, so the dispatcher can return the control to the caller after writing the log, just like anything. The SPY device maintains an array of SPY_CALL structures in its global data block device_context, which can be accessed through global variables GPDeviceContext. Hook Dispatcher finds an empty spy_call in an array by checking InUse members in a spy_call structure. Hook Dispatcher uses the CPU's XCHG instruction to load and set the value of the value (the XCHG instruction ensures this operation). This is very important, because when the code is running in a multi-threaded environment, protect measures must be taken to avoid conditional competition when reading and writing global data. If you find an empty spy_call in an array, Dispatcher will use the caller's thread ID (by psgetCurrentThreadID (), the address of the SPY_HOOK_ENTRY structure associated with the current API function and the entire parameter stack to the spy_call structure. The number of parameters that requires copying is taken from the KiaarQumentTable array, which is saved in the SDT of the system. If all spy_calls are used, the original API function processing routine will be called without any logging. The spy_call array must be used because of the multi-threaded nature of Windows 2000. When the Native API function is suspended (SUSPENDED), this situation will often appear - at this point, another thread will get control, then call another Native API in its own time slice. function. This means that the Hook Dispatcher of the SPY device must allow reenters on any time and any execution point. If Hook Dispatcher has a single global SPY_CALL storage area, it is possible to use the currently running thread overwriting before the thread in the waiting state. This is the best candidate for a blue screen. To further understand the nested Native API, I added DLEVEL and DMISSES members in Spy's Device_Context structure. Whenever you want to enter Hook Dispatcher (eg, add a new spy_call) DLEVEL to a spy_call array, you will not be accumulated 1. If the maximum number of nested layers (eg, the spy_call array is full), DMISSES will add 1 to identify a log record. According to my observation, in the actual environment, it can be easily found that the nested layer reaches 4. This means that the Native API will be returned immediately in the case of Heavy-load, so I set the upper limit of the nested layer to 256.

Before calling the original API processing routine, Hook Dispatcher saves all CPU registers (including EFLAGs), and then execute the path to the entry point of the function. This will be completed immediately before the Spyhook5 tag in Listing 5-3. At this time, Spyhook6 will be on top of the stack, only followed by the caller's parameters. Once the API processing routine is launched, the control will be transferred back to the spyhook6 tag of Hook Dispatcher. The code that is executed from this is also designed to be non-invasive. At this point, the main goal is to allow caller to see call context, which is almost entirely identical to the context established by the original API function. The main problem with Dispatcher is to immediately find the SPY_CALL structure saved with the current API call information. The only thing that can rely on the caller's thread ID, which is saved in the hthread member of the Spy_Call structure. Therefore, the Dispatcher loops traverses the entire spy_call array to find the matching thread ID. Note that the code will not care about the value of the FMUSE flag; this is not necessary, because all unused spy_call structures in arrays are set to 0, which is the ID of the system idle thread. The loop will terminate at the end of the array. Otherwise (the translation: "Nothing to find the matching thread ID), Dispatcher does not return control to the caller, because this will be fatal. In this case, the selection of the code is small, so it will enter KebugCheck (), which is of course to terminate the system in a controlled manner. However, this situation should never happen, but if it happens, that means that the system will inevitably have a very serious mistake, so make the system termination are the best solution. If you find the matching spy_call, Hook Dispatcher will end its work. The final action is to call the logging function SpyhookProtocol (), which needs to be introduced to a pointer to the SPY_CALL structure. The information required for logging is stored in this structure. When SpyHookProtocol () returns, Dispatcher releases the spy_call it just used, restores all the CPU registers, and then returns to the caller.

API HOOK Agreement

A good API SPY should be able to view the parameters it use after the original function is called, because the function may return additional data by the incoming buffer. Therefore, the log function spYhookProtocol () will be called at the end of the HOOK routine, and at this time the API function has not returned to the caller. Before discussing its implementation, please take a look at the two exemplary protocols given below, which will provide you with a probably direction. Figure 5-6 is a snapshot of log files generated when executing DIR C: / when the command line is executed.

Please compare the protocol formatted strings given by the log entries and list 5-6 listed in Figure 5-6. In the illustrated 5-1, NTOPENFILE () and NTClose () formatted strings correspond to the first row and fourth lines in Figures 5-6, respectively. They have amazing similarities; each formatted control ID is followed by a% (Refer to Table 5-2), and the parameter item related to it will be included in the protocol. However, the agreement also contains some additional information, which is obviously not a format string. I will explain this difference later.

Example 5-2 gives a general format of a protocol item. Each item contains the same number of domains, which are separated by separators. This separation can make the program easily parsing it. These domains are built in accordance with a simple basic rule:

l All numbers have been hexadecimal, no 0 prefix or common prefix "0x"

l Multiple parameters of the function are separated by commas

l String parameters will be in a pair of double quotes

l The value of the structural member is separated by "." symbols 5-6. Command DIR C: / List Protocol

"% s = NTOPENFILE (% ,% N,% o,% i,% n,% n)"

18: SO = NTOPENFILE ( 46C.18, NL00001, O "/ ?? / c: /", I0.1, n3, n4021) lbfee5ae05b6710,278,2

"% s = ntclose (% - l)"

LB: so = ntclose (-46c.18 = "/ ?? / c: /") LBFEE5AE05B6710, 278, L

List 5-1. Compare formatted strings and protocol items

<#>: = ()

List 5-2. General format of protocol items

l The value of the object name and handle of the handle is divided by "=".

l Date / Time Stamp is 1601-01-01 So far, its format relies on the basic time format of Windows 2000, with accuracy to 1/10 milliseconds.

l The thread ID is the unique numeric identity of the thread that calls the API function.

The state of the handle count indicates the number of handles currently registered into the SPY device handle list. The protocol function uses this list to find the handle related to the object name.

Figure 5-7. Command Type C: /Boot.ini's List Agreement

Figure 5-7 is executed in the console: the API SPY protocol result generated by the Type C: /Boot.ini command. Here are the meaning of some columns in the log entry:

l At 0x31 line, NTCReateFile () is called to open the /??/c:/boot.ini file. (O "/ ?? / c: /boot.ini") This function returned to the NTSTATUS value of 0 (S0), ie status_success, and allocated a new file handle, its value is 0 small 8, the handle belongs to Process 0x46c ( 46c.18). Therefore, the handle count is increased from 1 to 2.

l At 0x36 rows, the Type command reads the first 512 bytes of files per file / to CV into the buffer located at the linear address 0x0012f5b4, and analyzes the handle obtained from ntcreatefile (). Give the ntreadfile () function. The system successfully returns 512 bytes (IO.200).

l At 0x39, the other 512 bytes of file block will be processed. This time, the end of the file will be reached, so NTREADFILE () only returns 75 bytes (IO.4B). Obviously, my Boot.ini file is: 512 75 = 587 bytes.

l At 0x3c, NtClose () successfully released file handles (-46.18 = "/ c: / c: / c: / c:"), so the handle count will 2 Reduce is 1.

Now, you should understand how the API of the SPY protocol is built, which will help you master the details of the agreement build mechanism, and then we will discuss this mechanism. In front, I mentioned that the main API function used for logging is SpyhookProtocol (). Listing 5-7 gives this function that will use data in the spy_call structure to generate a protocol record for each API function and write it into a ring buffer, where the SPY_CALL structure is incorporated by Hook Dispatcher. A SPY device client can read this protocol through IOCTL calls. Each record item is a line of text, each line is ended by a single row end value (ie "/ n" in the C language). The serial read protocol buffer is implemented by using the kernel Mutext Kmutex KmProtcol, and KMProtocol is located in the global structure of the SPY device Device_Context. Listing 5-7 SpyhookWait () and spyhookrelease () functions are used to request and release this Mutext object. All access to the protocol buffer must be preprocessed by spyhookwait () and at the end of spyhookrelease (), the spyhookprotocol () function shows this behavior. NTSTATUS SpyhookWait (Void)

{

Return Mutex_Wait (GPDeviceContext-> kmprotocol);

}

/ / -------------------------------------------------------------------------------------------- -----------------

Long SpyhookRelease (Void)

{

Return Mutex_Release (GPDeviceContext-> kmprotocol);

}

/ / -------------------------------------------------------------------------------------------- -----------------

// <#>: =

Void SpyhookProtocol (PSPY_CALL PSC)

{

Large_integer Litime;

PSPY_PROTOCOL PSP = & gpDeviceContext-> spyprotocol;

KequerySystemTime (& Litime);

Spyhookwait ();

IF (SpyWriteFilter (PSP, PSC-> pshe-> pbformat,

PSC-> adparameters,

PSC-> DPARAMETERS)))

{

Spywritenumber (PSP, 0, (PSP-> Sh.dcalls)); // <#>:

SpywriteChar (PSP, 0, ':');

// =

SpywriteFormat (PSP, PSC-> pshe-> pbformat, //

PSC-> adparameters; // ()

SpywriteLarge (PSP, 0, & Litime); //

SpyWriteChar (PSP, 0, ',');

Spywritenumber (PSP, 0, (DWORD) PSC-> hthread); // ,

SpyWriteChar (PSP, 0, ',');

Spywritenumber (PSP, 0, PSP-> Sh.DHandles); // spywritechar (psp, 0, '/ n');

}

SpyhookRelease ();

Return;

}

Listing 5-7. Major Hook Protocol SPYHOOKPROTOCOL ()

If you compare the general format of the SPYHOKPROTOCOL () function given by the list 5-7 and the general format of the protocol item given by the List 5-2, it will easily find which domain in the protocol item ( Field). In this way, everything is clear why the protocol string in List 5-6 does not describe the entire data item --- Some independent data will be added by spyhookProtocol (), which will not require the help of the format string. SpyhookProtoCl () The core call is spywriteformat (), which generates the = [] section, depending on the format string associated with the current API function to be recorded. Please refer to the source file w2k_spy.c and w2k_spy.h in the / src / w2k_spy directory with the book discs to get more implementations of the spywrite * () function used in the SPY device driver.

Note that these code is slightly dangerous. These code written and 1997 are for Windows NT 4.0. After porting to Windows 2000, when the hook works a long period of time, it will occasionally trigger a blue screen. Worse, some special operations will immediately trigger a blue screen, for example, when you open my computer in the File / Open dialog box of the My Favoriter text editor. After analyzing the Crash Dump, I found that the system crash is caused by passing the NULL pointer to some functions. First, the SPY device tries to use some of these pointers to record the data referenced by the pointer, the system will crash. Typical is to point to the pointer of the IO_STATUS_BLOCK structure, there is an invalid string pointer in the unicode_string and object_attributes structures. I also found that some unicode_string structures with Buffer members did not / 0 end. Therefore, I will once again emphasize that you should not assume that all Unicode_String structures end in / 0. When you cannot be sure, use the Length member, which always tells you the valid byte number in Buffer.

In order to correct this problem, I add a pointer validity check for all log functions that use the customer pointer. At the end, I use the SpyMemoryTestAddress () function discussed in Chapter 4 to verify whether a linear address pointer points to a valid page table item (PTE). For more detailed information, please refer to List 4-22 and List 4-24. Another possible alternative is to use structured abnormalities (__TRY / __EXCEPT).

…………….to be continued………………

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

New Post(0)