Real-time monitoring articles for anti-virus engine design

xiaoxiao2021-04-01  203

Real-time monitoring articles for anti-virus engine design

Author: NJUE

Article Source: Safety Focus January 15, 2004

Editor's press:

The Introduction "Virtual Machine Turning" in the anti-virus engine design "We focus on virtual machine torment. Let's take a look at how to monitor viruses.

table of Contents

3.1 Introduction to real-time monitoring

3.2 Introduction to the real-time monitoring of viruses

3.3 Virus real-time monitoring under Win9X

3.3.1 Implementing Technical Details

3.3.2 Arrival structure and processes

3.3.3 Hooksys.vxd Reverse Engineering Code Analysis

3.3.3.1 Hook function entry code

3.3.3.2 get the current process name code

3.3.3.3 Communication part of the code

3.4 Virus real-time monitoring under Windows NT / 2000

3.4.1 Improvement of technology

3.4.2 Program Structure and Process

3.4.3 Hooksys.sys Reverse Engineering Code Analysis

3.4.3.1 Name Code of Current Process

3.4.3.2 Start the hook function work code

3.4.3.3 Mapping System Memory to User Space Code

3. Virus real-time monitoring

3.1 Introduction to real-time monitoring

Real-time monitoring technology is actually not any new technology, it has been in the DOS programming era. However, people did not give this technical name to this professional name. The hard disk write protection software that is commonly used in the early communities machine room is using real-time monitoring technology. The hard disk write protection software generally writes the part of the hard disk zero magnetic head (the 64 sectors of the 0 head 0 cylindrical 1 fan is reserved, the DOS access is not available) and modify the original main boot. Record allows the hard disk write protection program to get control at startup. The hard disk write protection program that acquires the control will modify the INT13H interrupt vector points to the hook code that has been reside in memory so that all the operations of the disk are intercepted at any time. The role of the hook code is of course apparent, it is responsible for determining whether the entry parameters, including function number, disk target address, etc., which can be permitted, such that the write operation protection of a particular area can be implemented. Later, it was born on this product that improved the magnetic disk recovery card, which utilizes techniques to redirect write operations to temporary partitions outside the target area and save the disk's previous state, and other technologies. Restore the function. Anyway, the core technology of such products is still real-time monitoring of disk operations. For those who are interested in this can refer to Gao Yunqing's "hard disk protection technical manual". There are many programs that have been resident and intercepted with some useful interrupts to implement certain specific purposes. We are often called TSR (terminating and waiting for Terminate-and-stay-responsient, this program is not easy to make , Require a lot of knowledge about hardware and DOS interruption, but also solve the problem of DOS re-entry, TSR program re-entry, and it will be a machine). Under Windows, real-time monitoring is not easy, and ordinary user programs are impossible to monitor system activities, which is also for system security considerations. HPS viruses can directly monitor the file operation in the user state is actually due to the Win9X in the design of a vulnerability. The two virus real-time monitoring (for Win9X & Windows NT / 2000) we have to discuss (for Win9X & Windows NT / 2000) use driver programming technology to allow drivers working on the system to intercept all file access. Of course, due to the difference in the working system, these two drivers are not the same from the structure or working principle, of course, the procedure is more than a matter of course, so we decided to divide each of them into independent section. discuss. The real-time monitoring of the virus mentioned above is actually the monitoring of the document, saying that document monitoring should be more reasonable. In addition to file monitoring, there are also a variety of real-time monitoring tools, which also have their own characteristics and functions. Here you recommend a site about the Windows system kernel programming: www.sysinternals.com. On it, you can find a lot of real-time monitoring gadgets, such as REGMON that can monitor registry accesses (by modifying the system call table related service entry), you can observe TDIMON in TCP and UDP activities in real time (drive TCPIP via Hook System Protocol The Dispatch function in .sys intercepts the request to send it to it), which is very beneficial to understand the internal operation details of the system. After introducing the relevant background, let's take a look at the specific implementation techniques of the virus real-time monitoring. 3.2 Introduction to the real-time monitoring of viruses

As mentioned above, the virus real-time monitoring is actually a file monitor, which checks if the file is opened, closed, cleared, written, etc., check if the file is a virus carrier, if so, select different processing schemes according to the user's decision Such as clearing the virus, prohibiting accessing the file, deleting the file or simply ignoring. This can effectively avoid the spread of infections on the local machine because the executable file loader first requires the file to open the file, and this request will be monitored in real time in the first time It is ensured that every execution is a clean non-poisonable file, which does not give the virus in any opportunity to perform and episode. The above is only the virus to monitor a rough working process in real time, and the detailed description will leave it into the corresponding chapter. The design of the real-time monitoring of the virus mainly has the following difficulties: First, the process of the driver is different from the writing of ordinary user programs, and it is very difficult. When writing a user program, you need to call some familiar API functions to complete specific purposes, such as opening the file You just need to call CreateFile; but you will not be able to use the familiar CreateFile. Under NT / 2000 you can use ZwcreateFile or NTCReateFile (Native API), but these functions are often required on an IRQL (interrupt request grade), if you call, delay / asynchronous process calls, non-paging / Sub-memory memory and other concepts are not particularly clear, then you write the driver will easily lead to the blue screen crash (BSOD), and the exception under RING0 will often cause the system to crash, because it is always trusted for the system, so there is no corresponding processing code Capture this exception. The call to KebugCheckex in NT will result in a blue screen, and then the system will dump and then restart. In addition, the debugging of the driver is not as convenient as the user program, and the debugger like VC is not linen, you must use the system-level debugger, such as Softice, Kd, ​​TRW, and so on.

Its two is the communication problem of the driver and the client program under RING3. The proposal of this problem is natural. Imagine that when the driver intercepted to a file, it must be notified to check the opened file under RING3, and then the consolidation module also needs to pass the result of the invisible A monitoring program is transmitted to RING0, and the final driver determines whether the request is allowed based on the result returned. This obviously there is a two-way communication process. People who write drivers know a API that can be used to send device I / O control information to the driver, which can be found in MSDN, but it is unidirectional, ie Ring3 client program can pass Call DeviceioControl to pass some of the information to the monitoring program under RING0 but not in turn. Since it is unable to find a ready-made function to realize the communication of the monitoring program under RING0 to the communication of the client program, we must use a roundabout way to indirectly. To this end we must introduce the concept of asynchronous process calls (APCs) and event objects, which is the key to achieving the wake up between privilege levels. Now briefly introduce these two concepts, please refer to the technical implementation details in each subsequent subsequent chapter. Asynchronous process calls are a mechanism for executing a process in the context of a particular thread when the condition is appropriate. When you queue an APC to a thread APC queue, the system will issue a software interrupt. When the next thread is scheduled, the APC function will run. APC is divided into two: The APC created by the system is called the kernel mode APC, and the APC created by the application is called the user mode APC. An APC can also be run only when the thread is in a Alertable state. For example, when calling an asynchronous mode, you can specify a user-defined callback function FileiocompletionRoutine, which is called when the asynchronous I / O operation is complete or canceled and the thread is called, which is a typical usage of the APC. The QueueUseraPC function exported in kernel32.dll can add an APC object to the queue of the specified thread, because we write the driver, this is not the function we want. Fortunately, in vwin32.vxd, a synergistic QueueUseraPc, after the monitor intercepts a file to open the request, it immediately calls this service to queue a RING3 APC that needs to be awakened in the client program, this function will be Soon the customer program is called when it is scheduled. This APC wake-up method is suitable for Win9x. We will use global shared events and semaphore objects to solve mutual awakening problems in WinNT / 2000. I will explain in Section 3.4.2 in Section 3.4.2. In the NT / 2000 monitor, we will use the keReleaseSemaphore to wake up a thread waiting for a client program under Ring3. At present, many anti-virus software have moved the driver's consolidation module to RING0, that is, the "active and operating system seamless connection" as it is promoted, so that the province has the consumption of communication, but writes the inspection module into a driver form. At the same time, there will be some troubles, such as can't call a lot of well-known API, can not interact with the user, so we still choose to analyze traditional anti-virus software monitoring procedures. The third is the resource problem occupied by the driver. If the system performance is too much due to frequent intercept file operations, the system performance is not worthless. This thesis will thoroughly analyze a successful anti-virus software monitoring program, which contains some of the techniques to improve their performance, such as setting history, built-in file type filtration, and set waiting timeout.

3.3 Virus real-time monitoring under Win9X

3.3.1 Implementing Technical Details

Implementation of real-time monitoring under Win9X mainly depends on virtual equipment driver (VXD) programming, can be installed with file system hook, VXD and RING3 client program communication (APC / Event) three technologies. We have mentioned that only drivers working on the system have the ability to effectively complete the intercepting system range file operation, and VXD is a virtual device driver for Win9X, so it is aweight. Of course, the VXD function is far more than the intercepting files provided by IFSMgr.vxd, and the system's VXDS provides almost all underlying interfaces - you can see VXD as a DLL under RING0. The virtual machine manager itself is a VXD, which is generally referred to as a VMM service, and other VXD call interfaces are called VXD services.

The two Ring0 call methods are all the same, that is, after INT20 (CD 20), it is tightly followed by a service identification code. The VMM uses the first half of the service identification code to find the corresponding VXD, and then use the service identification code. The second half part is positioned in the VXD service table and calls:

CD 20 INT 20H

01 00 0d 00 DD VKD_DEFINE_HOTKEY

After the command is performed for the first time, the VMM will replace with a same 6-byte indirect call command (not all modified as the CALL instruction, sometimes using JMP instructions), so that the query service table work:

FF 15 XX XX XX XX Call [$ VKD_DEFINE_HOTKEY]

It must be noted that the above call method only applies to RING0, ie a RING0 interface that is called from VXD / VMM services. VXD also provides V86 (virtual 8086 mode), Win16 protection mode, Win32 protection mode call interface. Where the call interface of the V86 and Win16 protection model is quite:

XOR DI DI

MOV ES, DI

MOV AX, 1684; INT 2FH, AX = 1684H -> Getting equipment entrance

MOV BX, 002A; 002AH = VWIN32.VXD device identity

INT 2F

MOV AX, ES; now ES: DI should include entrance

OR AX, AX

JE Failure

MOV AH, 00; VWIN32 Service 0 = VWIN32_GET_VERSION

Push DS

MOV DS, Word PTR CS: [0002]

MOV Word Ptr [lpfnvmin32], DI

MOV Word PTR [lpfnvmin32 2], ES; Save ES and DI

Call far [lpfnvmin32]; Call Gate (call door)

ES: DI points to a protective mode callback from the 3B segment:

003b: 000003d0 INT 30; # 0028: C025DB52 VWIN32 (04) 0742

INT30 forced CPUs from RING3 to Ring0, then the WIN95 INT30 processing function first checks whether the call is sent from the 3B segment, if it utilizes the CS index of the callback: IP indexes a protective mode callback table to obtain a RING0 address. In this example, 0028: C025DB52 is 0028: the entry address of the required service VWIN32_GET_VERSION.

VXD Win32 Protection Mode Call Interface We have already mentioned in front. One is DeviceIocontrol, our Ring3 client uses it to make one-way communication with the monitoring driver; the other is vxdcall, it is an unapproved call of the kernel32.dll, which is frequently used by the system, and there is not much place for us. You can see WIN95DDK help, where there is a detailed description of the call interface provided by each system VXD, which can be selected according to the needs of the appropriate service.

The installation of file system hook (IFShook) is derived from a service IFSMGR_InstallFileSystemapiHook provided by IFSMGR.vxd, using this service driver to register a hook function to the system. All file operations in the system will pass through this hook, and the document read and write under Win9x is as follows:

When the read and write operation is performed, the number of MUSTCOMPLETECOUNT variables are first incremented by unappromant function EntermustComplete, telling the operating system This operation must be completed. This function sets the internal variables in the Kernel32 module to display that there is a key operation now. If you have a sentence, there is also a function in the VMM, and the function name is also EntermustComplete. That function also tells VMM, there is a key operation being in progress. Prevent threads from being killed or hang.

Next, Win9X has been processed _MapHandlewithContext operations. The specific significance of this operation itself is unclear, but its operation is a pointer to the object referred to in Handle and adds a reference count.

Subsequently, it is a fundamental operation: KERNEL32 issued a VxDCall called Vwin32_int21dispatch. After being caught in VWIN32, it checks if the call is read or written. If so, the file handle is switched into a handle that can be identified, and IFSMGR_RING0_FILEIO is called. Next task is transferred to IFS Manager.

IFS Manager generates an ioreq and jumps to the Ring0Readwrite internal routine. Ring0ReadWrite Checks the handle validity, and gets the FSD returned to the CONTEXT returned when the file handle is created, and it is incremented together to the Calliofunc internal routine. Calliofunc Checks the existence of ifshook. If there is no existence, IFS Manager generates a default IFS Hook and invokes the corresponding vfatreadfile / vfatwritefile routine (because the MS itself only provides VFAT driver); if IFSHOOK exists, the IFShook function is controlled The right, and the IFS Manager itself is separated from the document reading and writing. Then, the call is returned by the layer layer. KERNEL32 calls an unaffected function LeavemustComplete, reducing the MustCompleteCount count, and eventually returns to the caller.

This shows that it is unlunably through the IFSHOOK to intercept the local file operation, and there is more missing files through APIHOOK or VXDCALL. The famous CIH virus is using this technology to achieve its resident infection, the code fragment is as follows:

Lea Eax, FileSystemapiHook- @ 6 [EDI]; Acquisition of the address of the hook function to be installed

Push EAX

INT 20H; call IFSMGR_INSTALLESYSTEMAPIHOK

IFSMGR_INSTALLSYSTEMAPIHOK = $

DD 00400067H

Mov DR0, EAX; save the address of the previous hook

POP EAX

As we see, all hook functions installed in the system are arranged in a chain. Finally installed hooks, first being called by the system. We must store the address of the previous hooks returned while installing the hook, to pass the request down to pass the request: MOV Eax, DR0; get the address of the previous hook

JMP [EAX]; jump to where to continue

For viral real-time monitoring, we also need to save the address of the previous hook when we install the hook. If the object of the file is carried with a virus, we can easily cancel the file request by do not call the previous hook; contrary, we need to pass the request in time, if the time in the hook is too long - use The processing feedback waiting for the RING3 tubular module will make the user significantly sensation system slow.

As for the hook function entry parameter structure and how to get an operation type (such as IFSFN_Open) and file name (in Unicode form), please refer to the corresponding code profiling section.

Another technology we need - APC / Event is also a service derived from a VXD export, which is a famous Vwin32.vxd. This strange VXD exports many services corresponding to Win32 API: such as _vwin32_queueuSerapc, _vwin32_waitsingleObject, _vwin32_resetwin32event, _vwin32_get_thread_context, _vwin32_set_thread_context, etc. This VXD is called virtual WIN32, and the probably name is thereby. Although the name of the service is the same as the Win32 API, the calling rules are large, and they are not available. _Vwin32_queueuserapc is used to register a user-state APC, and the APC function herein refers to the toxic threads we are in a alarm state in Ring3. Ring3 Client First Pass the address of the thread to the driver via IOCTL, then call this service queue an APC when the hook function is intercepted to the predetermined file, when the Ring3 client is scheduled, the APC routine is executed . _Vwin32_waitsingleObject is used to wait on an object so that the current RING0 thread is suspended. Our Ring3 Client first calls Win32 API - CreateEvent Create a set of event objects, and then converts the event handle to VXD handle (which should be a pointer to the object) and use IOCTL to use IOCTL through an unprecedented API - OpenVxDHandle Sending the Ring0 VXD, the hook function is called _vwin32_waitsingleObject on the VXD handle of the event in the VXD handle of the hook, and finally the Win32 API - SetEvent is called after the Ring3 client is completed. Waiting for the hook function.

Of course, there is a terrible problem with this: If you do what I said, you will find it working properly within one end, but the time is long, the system is hanging. Even the Drive Programming Master Walter One is also known in some of its APC routines in some cases in its book "System ProGramming for Windows 95". Microsoft's engineers claim that the document operation request cannot be interrupted, you can't block file operations in the drive and rely on Ring3 feedback to respond. There are also some discussions on this issue. Non-advice: Some people think that when the system DLL - KERNEL32 has a mutex (Mutex) when it calls RING0 processing file request, and in some cases to handle the APC to have the same Mutually exclusive, so deadlocks have occurred;

Some people think that although the 32-bit thread under Win9X is a multitasking, the Win16 subsystem is running in collaborative multitasking. In order to smoothly run the old 16-bit program, it introduces a global mutual exclusion --win16mutex. Any 16-bit thread has Win16Mutex in its entire lifecycle, and 32-bit threads are converted into 16-bit code, because the Win9x core is 16-bit, such as knrl386.exe, gdi.exe. If a file request from a thread with Win16Mutex is blocked, the system will fall into a deadlock state. The correct answer to this question seems to be proven before you get the Win9X source code, but this is the key to our real-time monitoring, so you must solve it. By tracking the process of Win95 file operation, I repeatedly experimentally verified, I finally found a better solution: I get the RING0TCB of the current thread through GET_CUR_THREAD_HANDLE before intercepted the file request, and find TDBX, then find TDBX. The RING3TCB obtained in TDBX, according to its structure, we get the FLAGS domain value from the offset 44h, I found that if it is equal to 10h and 20h, it is easy to cause the dead lock, which is just an experimental result, the reason I also say unclear, probably this File requests come from threads with Win16Mutex, so it cannot be blocked; another fundamental solution is to specify timeout when calling _vwin32_waitsingleObject, if there is no Ring3 wake-up signal from the specified time, the waiting is automatically released to prevent deadlock happened.

The main techniques of real-time monitoring under Win9X have been described in detail. Of course, there is also a part of the structure of VXD, writing, and compiling methods because the relationship between the space is not possible herein. For more details, please refer to Walter ONEY's book "System Programming for Windows 95", this book still has Taiwan's successful translation version "Windows 95 system program design".

3.3.2 Arrival structure and processes

The following program structure and process analysis from a famous anti-virus software Win9x real-time monitoring virtual device driver hooksys.vxd:

1. When VXD receives ON_SYS_DYNAMIC_DEVICE_INIT messages from VMM - Need Note that this is a dynamic VXD, it does not receive Sys_critical_init, device_init, and init_complete control messages when the system virtual machine is initialized - when it starts to initialize some global variables and Data structure, including HEAPALLOCATE, create alternate, history, open file, wait operation, 5 two-way loop linked lists for displaying files and 5 semapies used for linked lists (call CREATE_SEMAPHORE), Set the total number of variables_gnoffilters, the file name filter item is set to 0.

2. When VXD receives an ON_W32_DEVICEICONTROL message from the VMM, it acquires the user program from the entrance parameters to use DeviceIoControl to transfer the IO Control Code (IOCTLCODE) to deliver the user program. The RING3 customer program for hooksys.vxd works with Hooksys.vxd will send IO control requests to hooksys.vxd to complete a series of jobs, and the specific order and code meanings are as follows:

83003C2B: Passing the operating system version of Guidll to the drive (saved in the iOSVERSION variable), depending on the variable value, different offsets will be used from the Ring0TCB structure, because the operating system version will affect The kernel data structure.

83003C1B: Initialize the rear preparation chain table, saved a set of event pointers converted with OpenVxDHandle in each linked list element.

83003C2F: Passing the drive type value of Guidll to the drive (saved in the Drivertype variable), in accordance with this variable, call Vwin32_WaitsingleObject Set different wait timeout, because the read and write time of the non-fixed drive may be slightly longer.

83003C0F: Save the user-specified intercept file specified by the user-transmitted user, in fact, this type of filter already exists in the check module, and then set it clearly to improve processing efficiency: it ensures that non-specified type files will not be sent to RING3 Check module saves the overhead of communication. The parsed file type filter block pointer will be saved in the _gafilenamefilterarra array, and the value of the filter item number _Gnumoffilters variable is updated.

83003C23: Save the APC function address and the current thread KThread pointer to the current thread KThread pointer to the GUIDLL to kill open files.

83003C13: Install the system file hook, start the workmonhookProc of the block function of the intercept file operation.

83003C27: Save the APC function address and the current thread KThread pointer to close the file to close the file.

83003C17: Uninstall the system file hook, stop the work of the hook function FilemonhookProc, which intercepts file operation.

The issuance of the IO control code listed above is fixed, and when the hook function is started, some random control code is also issued:

83003C07: Driver to remove the head element of the file list, the first-first request opened and inserted into the tail of the waiting lin list while transmitting the user's space address to the RING3 level waiting to kill the APC function of the Kill Open file.

83003c0b: Driver The head element of the file is turned off, the first request is closed, and inserted into the end of the standby list, and transfer the file name skewers in the element to the APC function waiting to kill the shutdown file. Processing

83003C1F: When the file is checked, it is updated when the file is a virus.

The following describes the cook function and the GUIDLL to kill the APC function of the Open file, write files, and closes the process of processing, and the class:

When the file request enters the hook function FilemonHookProc, it first acquires the code of the function executed from the entrance parameter and determines whether it is open operation (ifsfn_open 24h), if it is not, the IRQ is transferred down, that is, constructs the inlet parameter and calls Save the previous hook function in the PrevifshookProc; if the program process turns to the processing branch of opening the file request. At the entrance to the entrance, you must first determine if the current process is our own, if so, you must put it.

Because file operations are frequent in the check module, intercepting document requests from their own will lead to a serious system dead lock. The next is to obtain a complete file path name from the stack parameters and filter the array by saving whether it is in the interception type, such as by further checking if the file is one of the following files: system .Dat, user.dat, / pipe /. Then look for a history chain list to determine if the file has been checked and recorded. If you find the record about the file in the history list and the record has not been fed, that is, its timestamp, the current system time is not greater than 1f4h, then Read the test results directly from the record.

At this time, enter the real check Open file function _ravcheckopenfile, this function entry is first removed from the standby, waiting, or closing the chain header (_GetfreeEntry) and fill it (file path name, etc.). The file request is queued by the file request before the value (RING3TCB-> Flags) in the undisclosed data structure is then judged. If you can add an idle element to the end of the file linked list and queue a RING3 check Open the file function APC. Then call _vwin32_waitsingleObject to wait for Ring3 to accomplish the completion of RING3 in an event object saved in idle elements. When the hook function hangs, Ring3's APC function is executed: it will send a request for a request for an IO control code to be 83003C07 to obtain the open file linked header element to save the first submission, the unreasonable file request, the driver can The virtual address of the elements in the kernel space is directly transmitted to it without having to consider remapping it. In fact, because there is no page protection in Win9x kernel space, the RING3 program can be read directly. Then it then calls the fnscanonefile function in RSENGINE.DLL for torch and sets the tubular result bit in the element, and then the event object saved in the element will call SetEvent Wake the hook function on this event. The waken hook function checks the result of the RING3 check code to determine whether the file request is delivered to the EAX or the cancel is placed directly in EAX, and the history is added.

The above is just a brief introduction to the hook function and the APC function process, which omitted, such as judging the fixed drive, timeout, etc. For details, please refer to the anti-assembly code comments for Guidll.dll and Hooksys.vxd.

3. When VXD receives an ON_SYS_DYNAMIC_DEVICE_EXIT message from VMM, it releases the heapfree assigned when the initialization is initialized, and clears 5 semaphors (Destroy_Semaphore) for mutual exclusive.

3.3.3 Hooksys.vxd Reverse Engineering Code Analysis

It is necessary to introduce the concept of reverse engineering before analyzing the code. Reverse Engineering refers to an enforcement of executable to understand the meaning of the machine code itself without source code. There are many uses of reverse engineering, such as the software protection, peek its design and writing technology, explore the mystery of the internal mystery of the operating system. The many unappromant data structures and services we used herein are obtained by the reverse approach. The difficulty of reverse engineering can I know that there is a 1000 line after an EXE file in a 1K size, and the three files we have to reverse add more than 80 K, the total code amount is more than 80,000 lines. So you must master a certain reverse skill, otherwise it will be very difficult.

First of all, you must complete the reverse work and you must choose an excellent disassembly and debug tracking tool. The Ida (The Interactive Disassembler) is a powerful disassembly tool: it is known for its interaction ability, allowing users to increase labels, annotations, and definition variables, function names; there are many anti-assembly tools for special treatment Document, such as import festival damage, etc., the IDA is still competent. Dynamic tracking needs to be used when the file is plurled or inserted into the interference instruction. Numega's Softice is a leader in debug tools: it supports all types of executable files, including VXD and SYS drivers, can call out with a hotkey to perform, memory, memory and port access, in summary Very strong, even the President of Microsoft is amazed.

Second, there must be a certain understanding of the compiler commonly used compiler, which helps us understand the meaning of the code.

The following code is a form of compiling advanced language functions that MS compilers commonly used:

000124A Push EBP; Save the base register 0001224B MOV EBP, ESP

0001224D SUB ESP, 5CH; Local variable space in the stack

00012250 PUSH EBX

00012251 PUSH ESI

00012252 Push EDI

......

0001225B Lea EDI, [EBP-34H]; reference local variable

......

0001238D MOV ESI, [EBP 08H]; reference parameters

......

00012424 POP EDI

00012425 POP ESI

00012426 POP EBX

00012427 Leave

00012428 RETN 8; function returns

The following code is a form of compilation advanced language that is commonly used by the MS compiler:

0001170d Lea EDI, [EAX 1CH]; Series Address Pointer

00011710 OR ECX, 0FFFFFFFH; set ECX as -1

00011713 XOR EAX, EAX; Sweeper end symbol (NULL)

00011715 Push Offset 00012C04H; Compiler Optimization

0001171A repne scaSB; scanning string end symbol position

0001171C Not ECX; get the string length

000117E SUB EDI, ECX; Restore Serial Address Pointer

The last point is that you must have a perseverance and clear mind. The reverse engineering itself is a painful work: the variables and function names used in the advanced language source code are just an address here, which requires repeated debugging to determine their meaning; additional compiler optimization is more understanding of the code to increase a lot of obstacles As in the above example, the stack command is placed in advance when the back function calls when the rear function call is set. Therefore, the perseverance and the mind are not possible.

The following enters the hooksys.vxd code analysis, because the code is too large, I only choose a representative and wonderful part of the introduction. The variables and functions in the code and the label name are what I added after my analysis, and may have some access to the original author.

3.3.3.1 Hook function entry code

C00012E0 PUSH EBP

C00012E1 MOV EBP, ESP

C00012E3 SUB ESP, 11CH

C00012E9 PUSH EBX

C00012EA PUSH ESI

C00012EB PUSH EDI

C00012EC MOV EAX, [EBP ARG_4]; code for executed functions

C00012EF MOV [EBP VAR_11C], EAX

C00012F5 CMP [EBP VAR_11C], 1; IFSFN_WRITE

C00012FC JZ WRITEFILEFILE

C0001302 CMP [EBP VAR_11C], 0BH; IFSFN_CLOSE

C0001309 JZ Closefile

C000130F CMP [EBP VAR_11C], 24h; IFSFN_Open

C0001316 JZ Short OpenFile

C0001318 JMP Irqpassdown

At the entrance to the hook function, the stack parameters are distributed as follows:

EBP 00H -> Save the EBP value.

EBP 04H -> Return the address.

EBP 08H -> Provides the address of this API to call the FSD function

EBP 0CH -> Provide the code that is executed

EBP 10H -> provides a driver code based on 1-based driver (if UNC is -1)

EBP 14H -> provides the type of resource operated thereon. EBP 18H -> Provides the code page on which the user string is delivered.

EBP 1CH -> Provides a pointer to the ioreq structure.

The hook function determines the type of the request using the code saved in [EBP 0CH]. It also uses the pointer of the IOREQ structure saved in [EBP 0CH] from which the PATH_T IR_PPATH domain acquires a complete file path name.

3.3.3.2 get the current process name code

C0000870 PUSH EBX

C0000871 PUSH ESI

C0000872 Push EDI

C0000873 CALL VWIN32_GETCURRENTPROCESSHANDLE; Ren0 PDB (Process Database) in EAX (Process Database)

C0000878 MOV EAX, [EAX 38H]; HTASK W16TDB

; Offset 38H is the Win16 Task Database Selection Sub

C000087B Push 0; DWORD FLAGS

C000087D OR Al,

C000087F Push Eax; DWORD Selector

C0000880 CALL GET_SYS_VM_HANDLE @ 0

C0000885 Push EAX; Handle of the System VM VMHandle HVM

C0000886 call _selectormapflat; Linear address of the selection sub-address to flat mode

C000088B Add ESP, 0CH

C000088E CMP EAX, 0FFFFFFFH; mapping error

C0000891 JNZ Short Loc_C0000899

......

C0000899 Lea EDI, [EAX 0F2H]; acquire module name from offset 0f2h

CHAR TDB_MODNAME [8]

3.3.3.3 Communication part of the code

Hooksys.vxd China code:

C00011BC PUSH ECX; Ring0 thread handle of customer program

C00011BD PUSH EBX; Parameters Parameters Passing APC

C00011BE PUSH EDX; flat mode address of Ring3 APC function

C00011BF call _vwin32_queueuserapc; queue APC

C00011C4 MOV EAX, [EBP 0CH]; RING0 handle of event object

C00011C7 Push EAX

C00011C8 Call _vwin32_resetwin32event; Setting event object is no signal

......

C00011E7 MOV Eax, [EBP 0CH]

C00011EA PUSH 3E8H; timeout setting

C00011EF PUSH EAX; Ring0 handle of event object

C00011f0 call _vwin32_waitsingleObject; wait for Ring3 to complete

GUIDLL.DLL China Code:

APC function entry:

10001AD1 MOV EAX, HDEvice; acquired device handle

10001AD6 LEA ECX, [ESP 4]

10001ADA PUSH 0

10001ADC PUSH ECX; Return by Number

10001ADD LEA EDX, [ESP 8]

10001AE1 PUSH 4; Output Buffer Size

10001AE3 Push EDX; Output Buffer Pointer

10001AE4 PUSH 0; input buffer size

10001AE6 PUSH 0; Input Buffer Pointer 10001AE8 PUSH 83003C07H; IO Control Code

10001AED PUSH EAX; Equipment handle

10001AEE CALL DS: DeviceIocontrol

10001AF4 Test Eax, EAX

10001AF6 JZ Short Loc_10001B05

10001AF8 MOV ECX, [ESP 0]; get the open file linket head element

10001AFC PUSH ECX

10001AFD Call Scanpenfile; Calling Test Function

Scanopenfile functions:

1000185D Call DS: fnscanonefile; call the true invisible library export function

10001863 MOV EDX, HMUTEX

10001869 Add ESP, 8

1000186C MOV ESI, EAX;

1000186E Push EDX

1000186F Call DS: ReleaseMutex

10001875 Test ESI, ESI; check results

10001877 JNZ Short openfileisvirus; if the virus jumped to OpenFileisviru for further processing

10001879 MOV EAX, [EBP 10H]; Ring3 handle of event object

1000187C MOV BYTE PTR [EBP 16H], 0; Set the result in the element is no virus

10001880 Push EAX

10001881 Call DS: setEvent; Set the event object to have a signal to wake up hook function

3.4 Virus real-time monitoring under Window NT / 2000

3.4.1 Improvement of technology

Implementation of real-time monitoring in Winnt / 2000 mainly depends on NT core mode driver programming, intercepts IRP, drives communication with communication with RING3 client programs (named event and semapital object). The design ideas and general processes of the program are very similar to the real-time monitoring of the Virus under Win9x, but only the technology will show great difference due to the different operational environment.

VXD no longer supports in Winnt / 2000, I will analyze the hooksys.sys.sys, which is actually a driver called NT core mode device. This driver is very different from VXD from its structure or working mode. In contrast, the NT core mode device driver is more difficult than VXD is more difficult: because it requires programmers to familiarize with the overall architecture and operational mechanism of Winnt / 2000, NT / 2000 is a pure 32-bit micro-kernel operating system, which is very large with Win9X. Difference; flexible use of kernel data structures, such as driver objects, device objects, file objects, IO request packages, execution body process / thread block, system service schedule, etc. In addition, the programmer also needs to pay attention to many important matters when programming, such as the IO request grade, paging / non-paging memory, etc. of the current system.

Here first introduces several important kernel data structures, they are often used in the programming of the NT core mode device, including file objects, driver objects, device objects, IO request packs (IRPs), IO stack units (IO_STACK_LOCATION) :

The file is clearly compliant with object standards in NT: they are system resources that can be shared by two or more user-state processes; they can have names; they are protected by object-based security; and they support synchronization. For user-protected subsystems, file objects typically represent an open instance of a file, device directory, or volume; for device and intermediate driver, file objects usually represent one device. The domain in the file object structure is transparently driven to access the domain including: PDEvice_Object DeviceObject: Pointer to the device object to which the file is opened.

Unicode_String FileName: The name of the file opened on the device, if the device represented by DeviceObject is opened, this string length is 0.

The driver object represents the image driven by the loaded kernel mode. When the driver is loaded into the system, I / O Manager is responsible for being created. The pointer to the driver object will be transmitted as an input parameter to the driven initialization routine (Reinitialize Routines), and unload routine. Most of the domains in the driver object structure are transparent, and the domains that drive can access include:

PDEvice_Object DeviceObject: Pointer to the device object that drives the driver. This domain will be automatically updated after successfully calling IOCREATEDEVICE in the initialization routine. When the driver is uninstalled, its uninstalling routine will use this domain and device object to call IodeDeleteDevice to clear each device object that the driver created.

PDRIVER_INITIALIZE DRIVERINIT: The initialization routine set by I / O Manager. This routine is responsible for creating a device object for each device of the driver, and you can create a symbolic link to the user state visible name in the device name and device. At the same time, it also fills the driver routine entry points into the domain of the driver object.

PDRIVER_UNLOAD DRIVERUNLOAD: The uninstall routine entry address of the driver.

PDRIVER_DISPATCH MAJORFUNCTION [IRP_MJ_MAXIMUM_FUNCTION 1]: One or more driver scheduling routines inlet address arrays. Each driver must set at least one scheduling entry for the IRP_MJ_XXX request set for the drive processing in this array, so all IRP_MJ_XXX requests are imported by the I / O Manager into the same scheduling routine. Of course, the driver can also set a separate scheduling entry for each IRP_MJ_XXX request.

Of course, the routines that may be included in the driver will be much more than listed above. For example, start I / O routines, interrupt service routines (ISRs, interrupt service DPC routines, one or more completion routines, cancel I / O routines, system closing notification routines, error record routines. Only in Hooksys.sys we will analyze is only very few in the routines, so the rest will not be described in detail.

The device object represents a logic, virtual, or physical device that processes the I / O request for the loaded driver. Each NT Kernel Mode driver must call IOCREATEDEVICE in its initialization routine to create its supported device objects. For example, TCPIP.sys creates three shared device objects that share this drive in its DriveRentry: TCP, UDP, IP. There is currently a relatively popular driver called WDM (Windows Driver Model). In most cases, its binary image can be compatible with WIN98 and WIN2000 (32-bit versions). The main difference between the WDM and the NT Kernel Mode driver is how to create a device: In the WDM driver, the Plug and Play (PNP) manager knows when to add a device to the system, or remove the device from the system. The WDM driver has a special AddDevice routine, and the PNP manager calls this function for each device instance of sharing the driver; the NT core mode driver needs to do a lot of additional work, and they must detect their hardware and create hardware. Equipment objects (usually in Driverentry), configure and initialize hardware make it work properly. Most of the domain in the device program object is transparent, the domain that can be accessed includes: PDRIVER_OBJECT DriverObject: Points to the driver object representing the driver load image.

All I / O is driven by I / O request pack (IRP). The so-called IRP driver means that the I / O manager is responsible for allocating a certain space in the non-page memory of the system. When the command is received or caused by the event, the work instruction is placed in it and passes to it. The service routine of the driver. In other words, the IRP contains the information instructions required for the service routine of the driver. IRP has two parts: a fixed portion (called title) and one or more stack units. The fixed portion information includes: the type and size of the request, is a synchronous request or asynchronous request, a pointer to buffer I / O, a pointer to a buffer, and a status information varying due to progress.

PMDL MDLADDRESS: Points to a memory descriptor table (MDL), which describes a user mode buffer associated with the request. If the FLAGS field of the top-level device object is do_direct_io, the I / O Manager creates this MDL for IRP_MJ_READ or IRP_MJ_WRITE. If an IRP_MJ_DEVICE_CONTROL request is required to specify a method_in_direct or method_out_direct operation mode, the I / O Manager creates an MDL for the output buffer used for the request. The MDL itself is used to describe the user mode virtual buffer, but it also contains the physical address of the buffer lock memory page.

PVOID AssociatedirP.systemBuffer: The SystemBuffer pointer points to a data buffer, the buffer is located in non-page memory in the kernel mode in IRP_MJ_READ and IRP_MJ_WRITE operations. If the top-level device specifies the DO_BUFFERED_IO flag I / O manager, this data buffer is created. For IRP_MJ_DEVICE_CONTROL operations, if the I / O control function code indicates that a buffer is required, the I / O Manager creates this data buffer. I / O Manager sends the user mode program to the driver's data to this buffer, which is also part of the creation of the IRP process. These data can be data related to WriteFile calls, or so-called input data in the DeviceIocontrol call. For a read request, the device driver fills the read data into this buffer, then copy the contents of the buffer to the user mode buffer. For the I / O control operation specified for Method_Buffered, the driver puts so-called output data in this buffer, then copy the data to the output buffer of the user mode. IO_STATUS_BLOCK IOSTATUS: IOSTATUS (IO_STATUS_BLOCK) is a structure that contains only two domains that set this structure when the driver is finally completed. The iostatus.status domain will receive a NTSTATUS code.

PVOID UserBuffer: For the IRP_MJ_DEVICE_CONTROL request for Method_neither mode, the domain contains the user mode virtual address of the output buffer. The domain is also used to save the user mode virtual address of the read and write request buffer, but specify the driver of the DO_BUFFERED_IO or DO_DIRECT_IO flag, and its read-write routine usually does not need to access this domain. When processed a Method_neither control operation, the driver can create its own MDL with this address.

Any kernel mode program creates an IRP while also created an associated IO_STACK_LOCATION structure array: each stack unit in the array corresponds to a driver that will process the IRP, and there is a stack unit for IRP The founder is used. The stack unit contains the type code and parameter information of the IRP and the address of the completion function.

Uchar Majorfunction: The main function code for this IRP. This code should be a value similar to IRP_MJ_READ and correspond to a dispatch function pointer to the Majorfunction table in the driver object.

Uchar minorfunction: The subfaming code of the IRP. It further pointed out which main function class belonging to the IRP.

PDEvice_Object DeviceObject: The address of the device object corresponding to the stack unit. This domain is fill in by the IocallDriver function.

Pfile_Object FileObject: The address of the kernel file object, the target of IRP is this file object.

The following is a brief introduction to the I / O request processing process in Winnt / 2000. First, I / O requests for the synchronization of single-layer drivers: I / O Requests The corresponding services in I / O Manager via subsystem DLL subsystem DLL. The I / O Manager sends a request to the device driver in the form of IRP. The driver starts I / O operation. When the device completes the operation and interrupts the CPU, the device driver service is interrupted. Final I / O Manager completes I / O request. The above six steps is just a very rough description, and the interrupt processing and the I / O completion phase are more complicated.

When the device completes I / O operation, it will issue an interrupt request service. When the device is interrupted, the processor gives control to the kernel trap handler, and the kernel trap will locate the ISR for the device in its interrupt schedule (IDT). After the ISR routine of the driver obtains control, it usually only stays on the device IRQL for a period of time, and then stop the device interrupt, then it queues a DPC and clears the interrupt exit operation. Before IRQL is reduced to DISPATCH / DPC, all intermediate priority interrupts can be served. When the DPC routine is controlled, it will start the next I / O request in the device queue, then complete the interrupt service. When the driven DPC routine is executed, there are some work to do before I / O requests can be considered. In some cases, the I / O system must copy data stored in the system's memory to the caller's virtual address space, such as recording the operation result in the I / O state block provided by the caller or buffer I / O The service returns the data to the calling thread. Thus when the DPC routine calls the I / O manager Complete the original I / O request, the I / O Manager will call a thread to queue a core state APC for calling the thread. When the thread is scheduled, the pended APC is delivered. It will copy the data and return status to the caller's address space, release the IRP representing the I / O operation, and set the event or I / O completion port provided by the caller's file handle or caller to a signal state. If the caller specifies the user APC with an asynchronous I / O function ReadFileEx and WriteFileEx, then the user APC is also required to queue. Finally, you can consider completing I / O. The thread waited on the file or other object handle will be released.

The I / O request processing process based on the file system device is basically the same, and the main difference is to increase one or more additional processing layers. To,, The system service scheduler KisystemService in Ntoskrnl.exe then locates NTWREADFILE in Ntoskrnl.exe in the system service schedule, and the interrupt is released. This service routine is part of the I / O manager. It first checks the parameters passed to them to protect the system security or prevent the user mode program from illegally accessing data, and then create an IRP for IRP_MJ_READ and send it to the entry point of the file system driver. The following work will be done by the file system driver and the disk driver.

The file system driver can reuse an IRP or create a set of parallel work-related IRPs for a single I / O request. The disk driver that performs IRP is finally possible to access the hardware. For PIO modes, an IRP_MJ_READ operation will result in a direct read device's port or a memory register implemented by the device. Although the driver running in the kernel mode can be directly connected to their hardware sessions, they usually use hardware abstraction layers (HAL) access hardware: read operations will eventually call the read_port_uchar routine in HAL.DLL to come from an I / O port Read the single byte data.

Winnt / 2000 equipment and drivers have a significantly stacked hierarchy: the object object in the stack is called a physical device object, or is referred to as PDO, and the corresponding driver is called a bus driver. There is an object in the middle of the device object stack, an object is called a functional device object, or referred to as FDO, its corresponding driver is called a functional driver. There will be some filter device objects on the top of the FDO and below. The filter device object located on the FDO is called the upper filter, and its corresponding driver is called the upper filter driver; the filter device object located below the FDO (but still on the PDO) is called a lower filter, The corresponding driver is called the lower filter driver. This stack structure can make the I / O request process more. Each operation affecting the device uses IRP. Usually IRP first is first sent to the uppermost driver of the device stack, and then gradually filters the driver below. Each driver can determine how to handle IRP. Sometimes the driver does not do anything, it is only to pass the IRP next to the lower layer. Sometimes, the driver is processed directly to the IRP and is no longer transferred down. Sometimes, the driver is handled both IRP and passes the IRP. This depends on the contents carried by the device and IRP.

Through the above introduction, you can know: If we want to intercept the file operations of the system, you must intercept the I / O Manager IRP to the file system driver. The easiest way to intercept the IRP is to create an upper filter device object and add it to the device stack where the file system device is located. The specific method is as follows: Firstly, you can create your own device objects via IOCREATEDEVICE, then call IoGetDeviceObjectPointer to get a pointer to the file system device (NTFS, FASTFAT, RDR, or MRXSMB, CDFS) object, and finally put your own device in the device stack by Ioattachdevicetodeventack. filter.

This is a method of intercepting IRP's most commonly used is also the most insurance. ART Baker's "Windows NT Device Driver Design Guide" has been described in detail, but there is two problems with real-time monitoring of viruses: one of them is to filter Put it to the uppermost layer of the stack. When there is other upper filter, it cannot guarantee that the filter is above the file system device; its second due to the filter system needs to perform, such that all of its characteristics is required Replication in the file system device. In addition, the scheduling routine filter drive must support, which means that we cannot make the scheduling routines in the filter driver for their own RING3 customer program, because the original sent to file system driver scheduling Cheng IRP will now pass through the scheduling routine driver driven.

So hooksys.sys do not use the above method. Its method is simpler and more directly: it gets the pointer to the file system drive object through ObreferenceObjectByname. Then the open, closes, clear, set file information in the MajorFunction array in the drive object, and write scheduling routine inlet addresses to the entry address of the corresponding hook function in hooksys.sys to reach the purpose of intercepting IRP. Please refer to the code for details.

The following describes the communication technologies for driving and RING3 client programs. Same as Win9X, the client program communication technology is the same, and NT / 2000 still supports unidirectional communication from Ring3 to Ring0 using DeviceIoControl, but from Ring0 to wake up the Ring3 thread by queuing APC, it is not possible. The reason is that I didn't find an open function to achieve (the Walter One is said that there is an unapproved function to achieve from Ring0 queue APC). In fact, we can also achieve bidirectional wake up by named event / semaphore objects, and this may be more reliable than APC.

Object Manager has an extremely important location in the Windows NT / 2000 core, and one of its most important functions is to organize management system kernel objects. In Windows NT / 2000, the kernel object manager has introduced a large number of object-oriented ideas, that is, all kernel objects are encapsulated inside the Object Manager, except for the object manager, and some other people who want to reference kernel objects. The system is opaque, that is, you need to access these structures through the Object Manager. Microsoft highlights the kernel driver code follows this principle (the user code cannot directly access this data), which provides a series of routines starting with OB for us to use. The kernel named object exists in the overall naming kernel area of ​​the system, similar to the traditional DOS directory and file organization, and the object manager also manages these objects, so that the kernel object can be quickly retrieved. Of course, this tree-like structure tissue kernel has named objects, and another advantage, that is, make all naming objects organizations are very organized, such as device objects under / device, and the object type name is in / ObjectTypes, etc. . In this way, it can also reach the user-only process that can only access the objects under the / BaseNameDObjects, and the kernel code does not have any restrictions. As for how to organize these named objects inside, in fact, Windows NT / 2000 is directed by the Directory object to which the kernel variable obprootdirectoryObject is meant, and use hashtable to organize these named kernel objects.

Use the named semaphore to wake up the Ring3 thread in hooksys.sys. The specific practices are as follows: First call CreateSemaphore in Guidll.DLL to create a named semaphore hookpen and set to no signal, and call CreateThread to create a thread. The entrance to the thread code is waiting to be awakened by the Ring0 hook function by calling WaitForsingleObject. The driver This is the pointer to the named semaphore object hookopen by unapproved routine OBReferenceObjectByname (/ basenaMedObjects / hookopen) during initialization. When it intercepts the file to open the request, call the keReleaseSemaphore to set the hookopen to wake up 3 Level Waiting to check the thread that opens the file. In fact, GUIDLL.DLL created two named sessions, as well as a hookclose to wake up Ring3 waits to check threads that turn off files.

GUIDLL.DLL uses a named event to wake up temporarily suspend the Ring0 hook function waiting for the test. The specific practices are as follows: hooksys.sys Create a group of naming events through the ZwcreateEvent function during its initialization (here you must reasonably set the security descriptor, otherwise the RING3 thread will not use the event handle) and get its handle, and get the handle through ObreferenceObjectbyHandle The pointer of the event object referenced. Then hooksys.sys will save this group of event handles and fingers, and the event names in each element of the standby list: Ring3 uses the handle, RING0 uses the pointer. When the hook function intercepts the file request, it first wakes up 3 to check the drug thread, then call KewaitForsingleObject to wait for the completion of the completion of the recovery of the incident / basenaMedObjects / hookxxxx. And waken Ring3 check threads get their handles by the OpenEventa function by the event name, and send a setEvent call after the end of the hook, set the event as a signal state to awakening the Ring0 hangs. Of course, the above discussion is limited to open file operations, and the hook function does not adjust the completion of the recovery of KewaitForsingleObject when intercepted by other file requests, but wakes up the Ring3 check the thread to return directly; the corresponding RING3 check thread is not Call SetEvent for remote wobble after checking. In addition, you must pay attention to some matter when you write a NT core mode driver. The first is the interrupt request grade (IRQL), which is a problem that is particularly worth noting when the NT driver programming is performed. Each kernel routine requires running on a certain IRQL. If you cannot determine which level of the current IRQL is in the call, you can call KegetCurrentiRQL to get the current IRQL value and determine. For example, to obtain a pointer pointing to the current process may consider Eprocess Analyzing current IRQL, such as larger than the DISPATCH_LEVEL call IoGetCurrentProcess equal; IRQL is less than when scheduling / deferred procedure call level (DISPATCH_LEVEL / DPC) and can be used PsGetCurrentProcessId PsLookupProcessByProcessId. Secondly, the problem is paging / non-paging memory. Since the system will not be able to handle the page failure because the system is executed, the system will not process page faults in the APC level, and the general principle here is that the code is absolutely unable to cause page faults. This also means that the code to perform at a level above or equal to the DISPATCH_LEVEL level must exist in non-page memory. In addition, all of these code to access must also exist in non-page memory. Finally, synchronous mutual exclusive problems, which is especially important for drivers sharing such as viral real-time monitoring. Although there is no multithreading in hooksys, PSCreateSystemThread, but because it hooks the system file hook, all threads of all threads in the system will pass from hooksys. When a thread's file request is processed, Hooksys will go to access some global shared data, such as filters, history, etc., which may be preempted when accessing is for some reason, and the result is other threads. The file request will be wrong when the request is passed. To this end, the driver must be synchronized using the kernel synchronization objects such as spin lock, mutex, and resources, and synchronize all threads of shared global data.

3.4.2 Program Structure and Process

The following program structure and process analysis of Winnt / 2000 real-time monitoring NT core mode device drivers from a famous anti-virus software hooksys.sys:

1. Initialization routine: Call _GetProcessNameOffset acquires the offset of the process name in EPROCESS. Initialize, open file waiting operation, shut down file, history 5 two-way loop linches and 4 self-spinch and 1 fast mutual exclusion with the linked list operation mutually exclusive. Set the global variable_irqcount (IRP) to 0. Create an uninstall protection event object. Initialize the synchronization resource variable for file name filter array. Retrieve the Hookopen and HookClose two named sessions (_CreateSemaphore) in the system global named kernel area. To spare (_allocatebuff), the list is allocated in the system non-paged pool, and create a set of naming event object hookxxx and saves each element of the standby list (_createoneEvent). Create a device, set the drive routine port, set a symbolic connection for the device. Create a disk drive device object pointer (_QuerySymbolicLink) and a list of file system driver object pointers (_hooksys). 2. Open the routine (IRP_MJ_CREATE): Map the standby list with system non-paged memory (preserved in _sysbufaddr) to the user space (saved in _userbufaddr) so as to directly access this memory directly from the user state (_mapMemory) .

3. Device Control Routines (IRP_MJ_DEVICE_CONTROL): It acquires the user program from the IRP current stack unit to deliver the user program to use DeviceIOControl to deliver the intent of the user program. The RING3 customer program Guidll.dll, which works with hooksys.sys, will send IO control requests to hooksys.sys to complete a series of work, the specific order and code meanings:

83003C2F: Passing the drive type value obtained by GUIDLL to the drive (saved in the DrivertYpe variable), set different waiting (KewaitforsingleObject) timeout, because the read and write time of the non-fixed drive will be slightly longer.

83003C0F: Save the user-specified intercept file specified by the user-transmitted user, in fact, this type of filter already exists in the check module, and then set it clearly to improve processing efficiency: it ensures that non-specified type files will not be sent to RING3 Check module saves the overhead of communication. The parsed file type filter block pointer will be saved in the _gafilenamefilterarra array, and the value of the filter item number _Gnumoffilters variable is updated.

83003C13: Modify the file system driver object scheduling routine entry, start the work of the hook function of the intercept file operation.

83003C17: Restore the file system driver original scheduling routine port, stop the hook function of the intercept file operation.

The issuance of the IO control code listed above is fixed, and when the hook function is started, some random control code is also issued:

83003C07: Driver to remove the header of the file list, the first-first request opened and inserted into the tail of the waiting linked list while transmitting the user's space address to the RING3 level waiting to kill the thread.

83003c0b: Driver to turn off the header element of the file list, the first request, close file delete and inserted into the end of the standby list, and transfer the file name string in the element to the thread of the RING3 waiting to locate the shutdown file processing 83003C1F: When you check the file is a virus, update the history chain list.

The following describes the hook function _Hookcreated TracyDispatch and GUIDLL to kill the thread of the Open file, and the processing of closing, cleaning, setting file information, and writing operations:

When the file request enters the hook function _Hookcreated InterSpatch, it first positions the current stack unit from the entrance IRP and acquires the file object representing the request. Then determine if the current process is our own, if you must put it, because the file operations are frequent in the check module, the file request from RavMon will cause a serious system deadlock. Next, use the file object in the stack unit to get a complete file path name and make sure the file is not: / PIPE /, / IPC. Then look up the history linked list to determine if the file has been checked and recorded. If you find the record about the file in the history lin list and the record is not expired, the timestamp and the current system time must be greater than 1f4h, then Read the test results directly from the record.

If the record does not have this file in the historical chain table, filter the array check if the file has been intercepted file type. At this time, enter the real check Open file function _ravcheckopenfile, this function entry is first removed from the standby, waiting, or closing the chain header (_GetfreeEntry) and fill it, such as file path name. Then add the idle element to the end of the file, and release the hookopen semaphore to wake up RING3 and wait for the thread to open the file. Then call the KewaitForsingleObject on an event object saved in the idle element to wait for the completion of RING3.

When the hook function hangs, the RING3 checks the thread: it will send a request for the driver to the driver to obtain a request to open the file linop header, which will save the first submission, the unreasonable file request, the drive will put the elements The offset address mapped to the user space is passed directly to it. Then it then calls the fnscanonefile function in RSENGINE.DLL for torch and sets the tubular result bit in the element, and then the event object saved in the element will call SetEvent Wake the hook function on this event. The waken hook function checks the result bits set by RING3 Test Code to determine the file request is the original scheduler routine that is saved, or is canceled, the IOFCOMPLETEREQUEST is directly returned, and the history is added.

The above is just a brief introduction of the hook function and the RING3 thread process, which omits such as judging the fixed drive, timeout, etc. For details, please refer to Guidll.dll and Hooksys.sys' disassembly code comments.

4. Close the routine (IRP_MJ_CLOSE): Stop the hook function, restore the file system driver original scheduling entry (_stopfilter). Unlock memory mapping to user space.

5. DRIVERUNLOAD: Stop the hook function, recover the original scheduling portal of the file system driver. Delete devices and symbolic connections. To delete a set of named event objects hookxxxx when initialization, including the release of the pointer reference, close the open handle. It is released as MDL (_PMDL), alternate linked list (_sysbufaddr), history of memory, and memory space allocated by filters. Delete the resource variable (_filterResource) set for the file name filtered array access. Release two pointers references to HOOKOPEN and HOOKCLOSE in the overall naming kernel area of ​​the system. 3.4.3 Hooksys.sys Reverse Engineering Code Analysis

3.4.3.1 Name Code of Current Process

The process name is offset in EPRocess in the initialization routine.

00011889 Call DS: __ ivp__iogetcurrentprocess @ 0;

Get the current process system EPROCESS pointer

0001188F MOV EDI, EAX; EPROCESS base address

00011891 xor ESI, ESI; initialization offset is 0

00011893 Lea Eax, [ESI EDI]; Scanner

00011896 Push 6; process name length

00011898 Push EAX; Scanner

00011899 Push Offset $ sg8452; "system"; process name string

0001189E Call DS: __ IMP__Strncmp; Compare Whether the scan pointer is a process name

000118A4 Add ESP, 0CH; Restore Stack

000118A7 TEST EAX, EAX; test comparison results

000118A9 jz short Loc_118b9; find it out of the loop

000118ab Inc ESI; increasing offset

000118AC CMP ESI, 3000H; Scan in the 12K range

000118B2 JB Short Loc_11893; Continue to compare within the range

The hook function starts to get the current process name

00010d1e Call DS: __ IMP__IOGETCURRENTPROCESS @ 0; get the current process system EPROCESS pointer

00010D24 MOV ECX, _ProcessNameOffset; Save process name offset

00010D2A Add Eax, ECX; pointer to the process name

3.4.3.2 Start the hook function work code

000114f4 push 4; driving the file system in advance

000114F6 MOV ESI, Offset fsdriverObjectptrlist;

Number of file system drive object numeral list offset address

000114FB POP EDI; Douses the operator with EDI, the initial value is 4

000114FC MOV EAX, [ESI]; pointers that get the first driving object

000114fe test Eax, EAX; test is legal

00011500 JZ Short Loc_11548; Continue to the next modification driver object without legal

00011502 MOV EDX, OFFSET _HOOKCREATEDISPATCH @ 8;

Obtain the offset address of your hook function

00011507 LEA ECX, [EAX 38H]; Open Schedule Routing (IRP_MJ_CREATE) offset in the object

@ InterlockedExchange @ 8;

Atomic operation, replacing the entrance to the scheduling routine in the replacement drive object is the offset address of the hook function

0001150F MOV [ESI-10H], EAX; hold the entry of the original open scheduling routine

3.4.3.3 Mapping System Memory to User Space Code

0001068E Push ESI; system memory size

0001068F push _sysbufaddr; system memory base site

00010695 Call DS: __ IMP__MMSIZEOFMDL @ 8; Calculation Description System Memory The Memory Descriptive Description Table (MDL) size

0001069B Push 206B6444H; Debugging tag

000106A0 Push Eax; MDL size

000106A1 PUSH 0; allocated in the system non-page memory pool

000106A3 Call DS: __ ix__exallocatepoolwithtag @ 12; assigns memory for MDL

000106A9 Push ESI; system memory size

000106AA MOV _PMDL, EAX; save MDL pointer

000106AF PUSH _SYSBUFADDR; system memory base site

000106B5 Push Eax; MDL pointer

000106B6 Call DS: __ IMP__mmcreatemdl @ 12; Initializing MDL

000106BC PUSH EAX; MDL pointer

000106BD MOV _PMDL, EAX; save MDL pointer

000106C2 Call DS: __ ivp__mmbuildmdlfornonPagedPool @ 4

; Fill in the physical page number after MDL

000106C8 Push 1; Access mode

000106CA PUSH _PMDL; MDL pointer

000106d0 Call DS: __ ivp_mmmaplockedpages @ 8; Mapping MDL Description Physical Memory Page

......

000106dB MOV _USERBUFADDR, EAX; save the user space address after mapping

_Userbufaddr and _sysbufaddr are mapped to the same physical address.

main reference:

David A. Solomon, Mark Russinovich "INSIDE Microsoft Windows 2000" September 2000

David A. Solomon "INSIDE Windows NT" May 1998

Prasad Dabak, Sandeep Phadke, Milind Borate "undocuplented windows nt" october 1999

Matt Pietrek "Windows 95 System Programming Secret" March 1996

Walter OneY "System Programming for Windows 95" March 1996

Walter OneY "Programming The Windows Driver Model" 1999

Lu Lin "Windows9x file read and write internal" 2001

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

New Post(0)