-
-
[翻译]API检测
-
发表于: 2008-1-19 10:10 8894
-
API Spying Techniques for Windows 9x, NT and 2000
Yariv Kaplan
API spying utilities are among the most powerful tools for exploring the inner structure of applications and operating systems. Unfortunately, neither the SDK nor the DDK provide any documentation or examples demonstrating a way for implementing such a utility. This article will attempt to shed some light on this subject by presenting several techniques for hooking API calls issued by Windows applications.
API检测技术是探索应用程序和操作系统内部结构最有效的工具。不幸的是,SDK和DDK没有提供任何文档或例子来演示这种技术。这篇文章将试图对这个线索演示一些挂载应用程序请求的API技术。
There are several things you'll need to consider before sitting down to write your own API spying utility. First, you'll need to decide whether you want to spy upon a single application or set up a system-wide API interceptor. Each approach can be useful in different situations. For example, assume that you need to write an application that blocks the execution of specific processes according to rules set by an administrator. Obviously, you will need a way to monitor the execution of new processes and terminate the ones, which are marked as restricted. One way to accomplish that would be to establish a system-wide API interceptor that monitors calls made to the CreateProcess function (actually, to both the Ansi and Unicode versions of this function). Whenever an application issues a call to one of these functions in order to create a new process, your interceptor will gain control and perform whatever processing is necessary.
在你坐在电脑前写你自己的API检测工具的时候有很多事情需要事先考虑一下。首先,你要决定一下你是想在一个单一的API上进行检测还是要拦截系统范围内的全部API函数。在不同的情形下每一个步骤都是有用的。例如,假设你要写一个应用程序阻塞一个指定的Aminidtrator进程。显然,你需要一个方法去监视新进程的执行并终止它,这个方法是要受到限制的。一种思路是建立一个系统范围的API拦截机监视CreateProcess函数(Ansi和Unicode版本都是这个函数)。每当应用程序发出调用这个函数创建一个新进程的时候,你的拦截机将获得控制并做一些必要的操作。
Other types of applications might require a simpler API interceptor capable of monitoring just one application at a time. A good example is BoundsChecker from NuMega - a tool capable of analyzing API calls in order to detect memory leaks and other bugs lurking inside Windows applications.
另一种是可能一次监视一个应用程序可能请求简单的API。一个很好的例子是NuMega公司的BoundsChecker--一个可以分析API调用为了检测内存泄漏和其它应用程序可能潜伏的BUG。
Whether you decide to employ a system-wide solution or opt for a simpler one, you'll still have to choose one among several API hooking techniques. The following sections will explore the various ways available for implementing an API interceptor for Windows, focusing on the advantages and disadvantages of each technique.
不管你决定是用一个系统范围还是简单的单个应用程序的监视技术,你都不得不选择多个API挂载技术之一。下面的段落将探讨不同的API拦截器的技术,集中讨论这些技术的有利一面和不利的方面。
Proxy DLL
This is by far the easiest technique for hooking API calls under Windows. As an example for a use of this technique, consider an anti-virus application that scans incoming email messages for viruses. An obvious requirement for such an application is the ability to hook Winsock's I/O functions in order to analyze data transfers between email clients and remote mail servers.
代理DLL
这是目前为止在Windows下挂载API最简单的技术。一个这种技术的例子是,考虑一个扫描邮件病毒的反病毒应用程序。一个显然的需求就是这种应用程序需要挂载Winsock's的I/O函数为了分析邮件客户端和服务器的数据传输。
This can be easily accomplished by creating a proxy DLL, which contains a stub for each of the functions exported from the Winsock library. If the proxy DLL is named exactly like the original Winsock library (i.e. wsock32.dll) and placed in the same directory where the target email application resides, then the interception occurs automatically. When the target application attempts to load the original Winsock library, the proxy DLL is loaded instead. All the calls made to Winsock's functions are routed to the exported stubs in the proxy. After performing the necessary processing, the proxy DLL simply routes the calls to Winsock and returns control back to its caller.
可以通过创建一个代理DLL来完成这项任务,它包含每一个在Winsock库中的输出函数的一个stub。如果代码DLL和Winsock库中的函数(i.e. wsock32.dll)一样命名严谨并且和邮件应用程序放在同一个目录下,那么这个拦截机可以自动运行。每当应用程序试图加载原始Winsock库时,替代的就会加载这个代理DLL。所有的Winsock's函数调用都被发送到代理DLL的输出stubs。经过一些特殊的操作后代理DLL简单的把请求发送给Winsock并返回控制结果给调用者。
Lest you be afraid that the above method of API interception is too simple, there is a catch. Although simple to implement, this technique has one major drawback - hooking a single function located in a DLL that exports 200 functions would require creating a stub for each of these functions in your proxy DLL. This could be rather tedious and also impossible at times, when some of the functions are not fully documented.
If you wish to see a working example of this technique, consult Matt Pietrek's WinInet utility, which was published on the Dec 94 issue of MSJ.
虽然执行简单,但这个技术有一个主要的缺点 - 在DLL里监视一个简单的本地函数这样的操作需要在你的代理DLL中请求创建200个输出函数stub.这是非常单调并且也不可能一次完成的且其中的一些函数还没有足够的文档说明。
如果你想看一下这方面技术的例子,可以看看Matt Pietrek's的WinInet工具,出版在1994年12月的MSJ专栏。
Patch those calls
When thinking about ways for intercepting an API call, there are two locations where you can intervene - either at the source of the call (the application code) or at the destination (the target function). This technique relies on the first option.
For each API function that you wish to intercept, you patch all the locations in the target application where calls to this function are issued. The modification can be done either on disk (to the executable file itself) or in memory (after the executable is already loaded). The tough part is pin pointing the exact locations where patching is necessary. In order to accomplish that, you'll need to implement a disassembler capable of analyzing assembly instructions. Obviously, writing a disassembler is far from being a trivial task, making this API interception technique one of the least
当考虑拦截一个API调用时,有两个位置你能插入 - 调用的源文件(应用程序代码)或是调用的目标(目的函数).这种技术依靠第一个选项。
对于你想拦截的API函数,你patch目标应用程序调用这个函数的所有位置。这个修改可以在磁盘上(可执行文件本身)或是在内存中(可执行文件加载之后).困难的部分是Patch所要指定的那些精确的位置。为了完成这些,你需要反汇编并且分析汇编指令。显然,写一个反汇编有点浪费时间,使用这种API拦截技术的很少。
IAT Patching
If you have ever looked into API hooking before, then you probably heard about Import Address Table patching. The numerous advantages of this technique make it the most elegant and common way of hooking API functions under Windows.
如果你曾经观察过API挂载,那么你可能听说过输入地址表。许多这方面的优势使得这项技术挂载API非常流行和普遍。
The foundation of this technique relies on the fact that 32-bit Windows executable files and DLLs are built upon the Portable Executable (PE) file format. Files based on this specification are composed of several logical chunks known as sections. Each section contains a specific type of content. For example, the .text section holds the compiled code of the application while the .rsrc section serves as a repository for resources such as dialog boxes, bitmaps and toolbars.
这个技术的基础是依靠建立在PE文件格式之下32位Windows可执行文件和DLL。在这个规格下文件是由许多段组成的。例如,.text段保存应用程序编译后的代码而.rsrc段提供对话框,位图,工具栏这样的资源。
Among all of the sections present in a Windows executable file, the .idata section is particularly useful for those who wish to implement an API interceptor. A special table located in this section (known as the Import Address Table) holds file-relative offsets to the names of imported functions referenced by the executable's code. Whenever Windows loads an executable into memory, it patches these offsets with the correct addresses of the imported functions.
在PE文件的所有这些段中,.idata段对于那些想拦截API的人非常有用。在这个段中有一个指定的表(众所周知是输入地址表)保存文件偏移到被执行代码引用的输入函数名。当Windows加载一个可执行文件到内存时,它patch这些偏移用输入函数正确的地址。
Why does Windows go into the trouble of patching the IAT in the first place? Well, as you probably know, Windows executable files and DLLs are often relocated (due to collisions) after they are loaded into memory. This makes it impossible to set in advance the target addresses of calls made to imported functions in the executable code. In order to ensure that these calls successfully reach their destination, it would have been necessary for Windows to locate and patch every single call made to an imported function after an executable image is loaded into memory. Obviously, such a large amount of processing during initializing of new processes and DLLs would have slowed down the system, giving the user the notion as if Windows is a slow and unresponsive operating system ;-).
为什么Windows Patching IAT陷入困难之中?你可能知道,Windows可执行文件和DLL加载到内存后经常重新部署(因为冲突)。这使得不可能在可执行代码输入函数中预先设置目标调用的地址。为了确保调用成功到达目标,在一个可执行映像加载进内存后需要Windows定位和patch每一个调用到输入函数。显然,如此大量的工作在新进程和DLL初始化的时候要做会减慢系统速度,能使用者一个概念是否Windows是一个反应迟钝的操作系统;
Fortunately, the designers of Windows were quite resourceful when they addressed this issue. In the current implementation of Windows executables and DLLs, calls made to imported functions are routed through the IAT using an indirect JMP instruction. The fact that imported function calls are "drained" through one location saves Windows the trouble of traversing the executable image in memory, looking for call instructions that are destined for patching.
目前Windows的可执行文件和DLLs调用输入函数传递到IAT用一个间接的JMP指令。事实是入口函数调用是“消耗”,通过一个位置节省了Windows在内存传输可执行文件映像的麻烦,寻找patching的调用指令。
What all of this got to do with API spying? Well, it seems that Windows IAT redirection mechanism offers a perfect way for intercepting API calls. By overwriting a specific IAT entry with the address of a logging routine, an API interceptor can gain control before the original function gets a chance to be executed by the processor.
API监视怎么开始呢?Windows IAT重定向机制提供了一个很好的方法拦截API调用。通过用记录例程指定的地址重写一个指定的IAT入口,一个API拦截器能获得控制在原函数被处理机执行之前。
Obviously, there are other issues involved in the implementation of this technique, such as the requirement for the logging code to be executed in the memory context of the intercepted application. These issues are discussed in the following resources:
显然,这还有其他的棘手的问题,比如拦截器的请求记录代码在内存中被执行。这些问题在下面的资源中有过讨论。
Matt Pietrek's excellent book: "Windows 95 System Programming Secrets" contains the source code of an API interception utility called APISpy32. This utility was initially published as part of an article written by Matt for the Dec 1995 issue of MSJ. A newer version of APISpy32 is available and can be ordered through Matt's web site at 505K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4N6Z5k6h3q4@1P5g2)9J5k6h3&6W2N6q4)9J5k6g2)9J5y4X3&6T1M7%4m8Q4x3@1t1`.
Spying on COM Objects by Dmitri Leman, WDJ, July 1999.
Matt Pietrek's的书"Windows 95 System Programming Secrets"包含了一个APISpy32的拦截器的源代码。这个工具最初出版是在1995年12月MSJ作为文件的一部分。
Patch the API
This is my favorite technique for hooking API functions. It has the inherent advantage of being able to trace API calls issued from different parts of an application while requiring a modification only to a single location - the API function itself.
这是我最喜爱的挂接API函数的技术。它有天生的优点在于能够跟踪来自一个应用程序不同的位置API调用,同时要求修改只是一个单一的位置-API函数本身。
There are several approaches that can be used here. One option is to replace the first byte of target API with a breakpoint instruction (Int 3). Any call issued to that function would generate an exception, which would be reported to your API interceptor in case it serves as a debugger of the target process. Unfortunately, there are several problems with this approach. First, the poor performance of Windows exception handling mechanism would considerably slow down the system. A second problem is related to the implementation of Windows debugging API. As soon as a debugger shuts down, it terminates all the applications that were under its control. Obviously, such a behavior is completely unacceptable in case you're implementing a system-wide interceptor, which must be able to terminate itself before its target applications cease executing.
有很多方法可以用在这。一个方法就是重定位目标API的第一个字节用断点(int3).任何调用这个函数将会产生一个异常,如果它作为一个目标进程的调试模块将会报告给你的拦截器。不幸的是,这个方法有很多问题存在。首先,Windows异常将会降低系统性能。其次的问题是涉及到Windows调用API。当一个调用器关闭的时候,它会关闭在它控制之下的所有应用程序。显然,上述行为完全不能满足你的系统范围的拦截器的需求,它必须能终止它自己在目标程序结束之前。
Another possibility is to patch the target function with one of the control-transfer instructions of the CPU (i.e. either a CALL or a JMP). Once again, there are several problems with this approach. First, it is possible that the patching would overrun the end of the intercepted function. This can occur in cases where the target API is shorter than 5 bytes (CALL and JMP are each 5 bytes long). Another issue is the need to constantly switch between the patched and "unpatched" versions of the intercepted function. This means that once your logging routine receives control from the CPU, it must restore the intercepted function to its previous unhooked state. This is required to allow the API interceptor to route the call to the original function without generating an infinite loop of calls back to the logging routine. Note that during the time the CPU is executing the original function, other calls to that function might be issued from different parts of the system. Since the function is in the unhooked state at that stage, the API interceptor will miss those calls. A more sophisticated API interceptor might utilize a better technique in order to overcome this limitation. Take a look at the following figure to get a better idea how this could be accomplished.
另一个可能的方法是patch目标应用程序用一个CPU控制传输指定(i.e.either a CALL or a JMP).再一次,这有很多的问题有解决。首先,这个Patching可能溢出在指令函数的结尾。如果目标API小于5个字节的话这就会发生(CALL和JMP都是5個字節长),另一个是需要经常在Patch和"Unpatched"之间切换拦截函数的版本。这意味着当你记录例程受到CPU控制的时候,必须保存拦截器到它先前未挂载的状态。这个请求允许API拦截发送调用到最初的函数没有记录例程产生循环。注意在CPU执行以前函数的这段时间,其他调用这个函数的例程可以从系统的其他地方传送过来。因为函数在这个期间处于无挂载状态,所以API拦截器将不会拦截这些调用。一个好的API拦截器可以利用更好的技术克服这些限制。注意下面的是些内容得到实现这个技术的更好方法。
(这有个图,不会传)
In this case, the API interceptor places a JMP instruction at the beginning of the target function, but not before saving the first 5 bytes of the function to a pre-allocated buffer in memory (a stub). The exact number of bytes to be copied to the stub may change depending on the instructions present at the head of the function. It cases where 5 bytes do not fall within instruction boundary, it is necessary to copy additional bytes until there is enough space for the JMP instruction to be inserted. Note that relative control-transfer instructions (i.e. JMPs and CALLs) need to be modified during copying to ensure that they transfer control to the right location in memory when executed from the stub. Obviously, performing such an analysis of assembly instructions requires the assistance of a disassembler, which, as I have mentioned before, is not very easy to implement.
在这种情况下,API拦截器在目标函数开始的位置放置一个JMP跳转指令,但没有在函数中设置5个字节作为内存缓冲(a stub).精确的字节数被拷贝到stub可能改变通过函数头出现的指令.5个字节没有在指令边界之内,这有必要拷贝附加的字节直到有足够的空间为Jmp指令插入.注意控制传输指定(i.e. JMPs and CALLs)需要在拷贝期间被修改以确保它们在stub执行的时候传输控制内存的正确位置.显然,执行这样一个汇编分析要反汇编的软件,前面我已经提及了,这不是一件简单的事.
If you are not intimidated by the complexity of this technique and wish to use it with your own applications, you might want to have a look at the source code of Detours - an API interception library, which was developed by one of the members in Microsoft's research department.
如果你没有被这个复杂的技术吓住并打算在你的应用程序中使用,你可能需要看一下Detours的源代码 - 一个API拦截库,它被Microsoft's研究部的一个成员开发出来.
Breaking address space barriers
By now, you should have a pretty good idea on how to implement an API interceptor capable of redirecting API calls to your own logging code. However, one problem remains unsolved - how do you ensure that the logging code is executed in the right address space? Before we can answer that question, we need to better understand the internal architecture of Windows memory management unit.
到现在,你应该已经有了一个好的思路如何在你的记录代码中拦截API调用的重定向.然而,有一个问题仍然没有解决 - 你怎样确定记录代码在正确的地址空间执行?在我们回答这个问题之前,我们需要很好的理解Windows内存管理机制.
As you probably know, each 32-bit Windows application gets a unique address space to toy with. During a task switch, Windows updates its page tables to reflect the new process's linear to physical memory mapping (also known as the process memory context). As a result, the page table entries that correspond to the private memory area of the process are modified to point to different physical addresses while those that correspond to shared memory regions remain untouched.
你可能知道,每一个Windows32位应用程序有一个唯一的地址空间.当任务发生切换的时候,Windows更新它的页表使新的进程的线性地址映射到物理地址空间(也就是进程上下文).结果,进程私有地址空间的地址被修改指向不同的物理需共享地址空间的地址不变.
Under Windows 9x, the 4GB linear address space is divided into several distinct memory regions, each with its own predefined purpose. MS-DOS and the first portion of the 16-bit global heap occupy the lowest 4MB. The next region spans from 4MB to 2GB and it is where Windows 9x loads each process's code, data and DLLs. Since each process physical addresses are unique, Windows 9x ensures that when a specific process is active, the page table entries corresponding to the 4MB-2GB region are mapped to this process's physical memory pages. The idea is for all processes to share the same linear addresses but not the same physical addresses (which is obviously impossible). Think of it as if the 4MB-2GB linear memory region is a window into the physical address space. This window slides each time a process is scheduled for execution to provide a view of the unique physical memory locations occupied by that process.
在Windows 9x下,4GB的线性地址空间被分成许多不同的内存区域,每一个区域都有特定的目的.MS-DOS和第一个全局堆占用最低4MB.下一个区域的范围是从4M到2G是Windows9X加载进程代码,数据和DLLs的地址.因为每一个进程的物理地址的唯一的,Windows9x确保当一个指定的进程活动时,页表项把4M-2G的线性地址空间映射到进程物理内存页.这个思路就是所有的进程共享同样的线性地址但不共享同样的物理地址(这显然是不可能的).想像这个4MB-2GB的线性地址区域是一个转换到物理地址空间的窗口.这个窗口滑动是在每次一个线程要在它的私有地址空间执行的时候.
So what's the real benefit of this mechanism anyway? Well, since all processes use the same linear addresses, Windows can load them into different physical locations without performing any code fix-ups. This means that the memory representation of a process can be (almost) an exact copy of its on-disk image.
这个机制真正带来的好处是什么?因为所有的进程用同样的线性地址空间,Windows能加载它们到不同的物理地址空间而不用做任何的代码修改.这意味着一个进程的内存请求可能是(几乎就是)磁盘映像的精确拷贝.
Continuing our exploration of the Windows 9x address space reveals that the region spanning from 2GB to 3GB is reserved for the upper portion of the 16-bit global heap. Also in this region are the memory-mapped files and Windows system DLLs (such as USER32, GDI32 and KERNEL32), which are shared among all running processes. This region is extremely useful for API interceptors, since it is visible in all the active address spaces. In fact, APISpy32 loads its spying DLL (APISpy9x.dll) into this area, thus ensuring that its logging code is accessible from any process issuing a call to an intercepted function.
继续我们的Windows9x地址空间2GB-3GB的探索,这个区域是上层16位全局堆.在这个区域还有内存映射文件和系统DLLs(如USER32,GDI32和KERNEL32),这个区域被所有的进程所共享.这个区域对API拦截非常有用,因为它是可视的活动的地址空间.实际上,APISpy32加载它的监视DLL(APISpy9x.dll)到这个区域,确保它的记录代码对任何进程调用拦截函数是有效的.
Under Windows NT/2000, the story is a bit different. There is no documented way of loading a DLL into an area shared by all processes, thus the only way to ensure that the logging code is accessible by the target process is to inject the spying DLL into its address space. One of the ways to accomplish this is by adding the name of the DLL to the following registry key:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_Dlls
在Windows NT/2000下有一点点的不同.没有一个文档化的方法加载DLL到所有进程共享的这块区域,那么为了确保进程能够访问记录代码的方法就是把监视DLL注入到它的地址空间.完成这个任务是一个方法是把DLL的名字加到注册表的这个位置:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_Dlls
This key causes Windows to load your DLL into every address space in the system. Unfortunately, this technique can only be used to inject a DLL into processes that link with user32.dll, meaning that console applications, which do not usually link with this DLL, are not included. Other methods for injecting a DLL into a process's address space are thoroughly described in the article "Load Your 32-bit DLL into Another Process's Address Space Using INJLIB" by Jeffrey Richter, which was published on the May 1994 issue of MSJ. Additional resources are available below:
Knowledge base articles: Q197571, Q125680, Q125691.
这个键值使Windows加载你的DLL到系统中每一个地址空间.不幸的是,这个技术只能用来注入一个与USER32.dll相关联的DLL,这意味着如控制台应用程序不需要经常连接DLL就不能被监视.其它关于注入DLL到进程地址空间的方法在"Load Your 32-bit Dll into Another Process's Address Space Using INJLIB"这篇Jeffrey Richter的文章上,出版在1994年MSJ,还可参考下面的文章:
Knowledge base articles: Q197571, Q125680, Q125691.
Injecting at the right moment
Knowing how to inject a piece of code into another process's address space is one thing, but timing is also a crucial factor when implementing an API interceptor. Inject at the wrong moment and your interceptor might miss calls issued by the target application. This problem really comes to light when implementing a system-wide interceptor. In that case, the interceptor needs to inject its spying DLL into a process's address space immediately after that process is executed, but right before it issues calls to intercepted functions. The best way to accomplish this is by monitoring calls to the CreateProcess function. When such a call is detected, the interceptor's logging routine passes control to the original CreateProcess function with a modified dwCreationFlags parameter, which contains the CREATE_SUSPENDED value. This ensures that the target process is started, but placed in a suspended state. The interceptor can then inject its spying DLL into the target process's address space and activate it using the ResumeThread API function.
懂得如何注入一块代码到另一个进程的地址空间是一件事,但是执行API拦截时间也是一个关键因素.注入在错误的时刻你的拦截器可能错过目标应用程序的调用API.这个问题尤其暴露在拦截系统范围的API时.在那样的情况下,拦截器需要在进程执行之后立刻注入它的监视DLL到进程地址空间,但是在调用拦截器的时候需要权限.最好方法就是监视CreateProcess函数的调用.当这个调用检测出来的时候,拦截器的记录例程用修改的dwCreateionFlags参数传递控制到原来的CreateProcess函数,修改的参数的值是CREATE_SUSPENDED.这样确保目标进程开始了,但是处于挂起的状态.拦截器这时能用监视DLL注入目标进程地址空间并且用ResumeThread API函数让目标进程运行起来.
Other ways for detecting the execution of processes under Windows are presented in the following section. Unfortunately, due to their asynchronous nature, they are less suited for detecting the appropriate time for injecting a spying DLL into another process's address space.
其他在Windows下监视进程执行的方法在下面的段中.不幸的是,因为他们是异步类型,所以它们不容易在合适的时间注入监视代码到进程地址空间.
Detecting Process Execution
As you saw earlier, it is often necessary for an application to be able to detect the execution of new processes. One possibility, which was previously mentioned, is to hook the CreateProcess function and monitor calls made to that function from different parts of the system. However, implementing a system-wide API interceptor for the sole purpose of hooking a single function often does not justify the effort.
正如你前面看到的,应用程序必须能监视新进程的执行.其中一个可能,这是以前的方法,在系统不同的段中监视CreateProcess.然而,执行系统范围的API拦截器去监视一个函数是不明智的.
Fortunately, there is a simpler way to accomplish that under Windows 95 (OSR 2 and above), 98 and NT/2000, without requiring the support of a full-fledged system-wide API interceptor. Under Windows 9x, it is possible for a virtual device driver to respond to the CREATE_PROCESS message sent by VWIN32 whenever a new process is executed. Windows NT/2000 offers similar functionality through the use of the undocumented PsSetCreateProcessNotifyRoutine function exported from NTOSKRNL. This function allows a device driver to register a callback function that receives notifications from the operating system whenever a new process is started. If you are well versed in device driver development, you should have no trouble deciphering these interfaces yourself using the following examples as your guide:
幸运的是,在Windows95 Windows98和WindowsNT/2000下有一个简单的方法完成这些任务,不需要一个系统范围的API拦截器的支持.在Windows9x下,当一个新进程执行的时候VWIN32发出的CREATE_PROCESS消息是可以被虚拟设备驱动发现的.Windows NT/2000简单的用没有文档化的NTOSKRNL输出的PsSetCreateProcessNotifyRoutine例程.这个函数允许驱动注册一个回调例程接收当一个新进程运行时候由系统发送过来的通知.如果你精通设备驱动开发,通过下面的指导在使用这个接口的时候你应该没有什么麻烦:
The Nerditorium column by Jim Finnegan, which was published on the January 1999 issue of MSJ, presented a utility that detects execution of processes under Windows NT/2000.
The ProcSpy32 utility available on my web site detects execution of processes under Windows 9x using a combination of a device driver and an OCX component.
有能力的人看英文,我翻译的太次,而且没有全部翻译.
翻译的目的是为了自己理解.
Yariv Kaplan
API spying utilities are among the most powerful tools for exploring the inner structure of applications and operating systems. Unfortunately, neither the SDK nor the DDK provide any documentation or examples demonstrating a way for implementing such a utility. This article will attempt to shed some light on this subject by presenting several techniques for hooking API calls issued by Windows applications.
API检测技术是探索应用程序和操作系统内部结构最有效的工具。不幸的是,SDK和DDK没有提供任何文档或例子来演示这种技术。这篇文章将试图对这个线索演示一些挂载应用程序请求的API技术。
There are several things you'll need to consider before sitting down to write your own API spying utility. First, you'll need to decide whether you want to spy upon a single application or set up a system-wide API interceptor. Each approach can be useful in different situations. For example, assume that you need to write an application that blocks the execution of specific processes according to rules set by an administrator. Obviously, you will need a way to monitor the execution of new processes and terminate the ones, which are marked as restricted. One way to accomplish that would be to establish a system-wide API interceptor that monitors calls made to the CreateProcess function (actually, to both the Ansi and Unicode versions of this function). Whenever an application issues a call to one of these functions in order to create a new process, your interceptor will gain control and perform whatever processing is necessary.
在你坐在电脑前写你自己的API检测工具的时候有很多事情需要事先考虑一下。首先,你要决定一下你是想在一个单一的API上进行检测还是要拦截系统范围内的全部API函数。在不同的情形下每一个步骤都是有用的。例如,假设你要写一个应用程序阻塞一个指定的Aminidtrator进程。显然,你需要一个方法去监视新进程的执行并终止它,这个方法是要受到限制的。一种思路是建立一个系统范围的API拦截机监视CreateProcess函数(Ansi和Unicode版本都是这个函数)。每当应用程序发出调用这个函数创建一个新进程的时候,你的拦截机将获得控制并做一些必要的操作。
Other types of applications might require a simpler API interceptor capable of monitoring just one application at a time. A good example is BoundsChecker from NuMega - a tool capable of analyzing API calls in order to detect memory leaks and other bugs lurking inside Windows applications.
另一种是可能一次监视一个应用程序可能请求简单的API。一个很好的例子是NuMega公司的BoundsChecker--一个可以分析API调用为了检测内存泄漏和其它应用程序可能潜伏的BUG。
Whether you decide to employ a system-wide solution or opt for a simpler one, you'll still have to choose one among several API hooking techniques. The following sections will explore the various ways available for implementing an API interceptor for Windows, focusing on the advantages and disadvantages of each technique.
不管你决定是用一个系统范围还是简单的单个应用程序的监视技术,你都不得不选择多个API挂载技术之一。下面的段落将探讨不同的API拦截器的技术,集中讨论这些技术的有利一面和不利的方面。
Proxy DLL
This is by far the easiest technique for hooking API calls under Windows. As an example for a use of this technique, consider an anti-virus application that scans incoming email messages for viruses. An obvious requirement for such an application is the ability to hook Winsock's I/O functions in order to analyze data transfers between email clients and remote mail servers.
代理DLL
这是目前为止在Windows下挂载API最简单的技术。一个这种技术的例子是,考虑一个扫描邮件病毒的反病毒应用程序。一个显然的需求就是这种应用程序需要挂载Winsock's的I/O函数为了分析邮件客户端和服务器的数据传输。
This can be easily accomplished by creating a proxy DLL, which contains a stub for each of the functions exported from the Winsock library. If the proxy DLL is named exactly like the original Winsock library (i.e. wsock32.dll) and placed in the same directory where the target email application resides, then the interception occurs automatically. When the target application attempts to load the original Winsock library, the proxy DLL is loaded instead. All the calls made to Winsock's functions are routed to the exported stubs in the proxy. After performing the necessary processing, the proxy DLL simply routes the calls to Winsock and returns control back to its caller.
可以通过创建一个代理DLL来完成这项任务,它包含每一个在Winsock库中的输出函数的一个stub。如果代码DLL和Winsock库中的函数(i.e. wsock32.dll)一样命名严谨并且和邮件应用程序放在同一个目录下,那么这个拦截机可以自动运行。每当应用程序试图加载原始Winsock库时,替代的就会加载这个代理DLL。所有的Winsock's函数调用都被发送到代理DLL的输出stubs。经过一些特殊的操作后代理DLL简单的把请求发送给Winsock并返回控制结果给调用者。
Lest you be afraid that the above method of API interception is too simple, there is a catch. Although simple to implement, this technique has one major drawback - hooking a single function located in a DLL that exports 200 functions would require creating a stub for each of these functions in your proxy DLL. This could be rather tedious and also impossible at times, when some of the functions are not fully documented.
If you wish to see a working example of this technique, consult Matt Pietrek's WinInet utility, which was published on the Dec 94 issue of MSJ.
虽然执行简单,但这个技术有一个主要的缺点 - 在DLL里监视一个简单的本地函数这样的操作需要在你的代理DLL中请求创建200个输出函数stub.这是非常单调并且也不可能一次完成的且其中的一些函数还没有足够的文档说明。
如果你想看一下这方面技术的例子,可以看看Matt Pietrek's的WinInet工具,出版在1994年12月的MSJ专栏。
Patch those calls
When thinking about ways for intercepting an API call, there are two locations where you can intervene - either at the source of the call (the application code) or at the destination (the target function). This technique relies on the first option.
For each API function that you wish to intercept, you patch all the locations in the target application where calls to this function are issued. The modification can be done either on disk (to the executable file itself) or in memory (after the executable is already loaded). The tough part is pin pointing the exact locations where patching is necessary. In order to accomplish that, you'll need to implement a disassembler capable of analyzing assembly instructions. Obviously, writing a disassembler is far from being a trivial task, making this API interception technique one of the least
当考虑拦截一个API调用时,有两个位置你能插入 - 调用的源文件(应用程序代码)或是调用的目标(目的函数).这种技术依靠第一个选项。
对于你想拦截的API函数,你patch目标应用程序调用这个函数的所有位置。这个修改可以在磁盘上(可执行文件本身)或是在内存中(可执行文件加载之后).困难的部分是Patch所要指定的那些精确的位置。为了完成这些,你需要反汇编并且分析汇编指令。显然,写一个反汇编有点浪费时间,使用这种API拦截技术的很少。
IAT Patching
If you have ever looked into API hooking before, then you probably heard about Import Address Table patching. The numerous advantages of this technique make it the most elegant and common way of hooking API functions under Windows.
如果你曾经观察过API挂载,那么你可能听说过输入地址表。许多这方面的优势使得这项技术挂载API非常流行和普遍。
The foundation of this technique relies on the fact that 32-bit Windows executable files and DLLs are built upon the Portable Executable (PE) file format. Files based on this specification are composed of several logical chunks known as sections. Each section contains a specific type of content. For example, the .text section holds the compiled code of the application while the .rsrc section serves as a repository for resources such as dialog boxes, bitmaps and toolbars.
这个技术的基础是依靠建立在PE文件格式之下32位Windows可执行文件和DLL。在这个规格下文件是由许多段组成的。例如,.text段保存应用程序编译后的代码而.rsrc段提供对话框,位图,工具栏这样的资源。
Among all of the sections present in a Windows executable file, the .idata section is particularly useful for those who wish to implement an API interceptor. A special table located in this section (known as the Import Address Table) holds file-relative offsets to the names of imported functions referenced by the executable's code. Whenever Windows loads an executable into memory, it patches these offsets with the correct addresses of the imported functions.
在PE文件的所有这些段中,.idata段对于那些想拦截API的人非常有用。在这个段中有一个指定的表(众所周知是输入地址表)保存文件偏移到被执行代码引用的输入函数名。当Windows加载一个可执行文件到内存时,它patch这些偏移用输入函数正确的地址。
Why does Windows go into the trouble of patching the IAT in the first place? Well, as you probably know, Windows executable files and DLLs are often relocated (due to collisions) after they are loaded into memory. This makes it impossible to set in advance the target addresses of calls made to imported functions in the executable code. In order to ensure that these calls successfully reach their destination, it would have been necessary for Windows to locate and patch every single call made to an imported function after an executable image is loaded into memory. Obviously, such a large amount of processing during initializing of new processes and DLLs would have slowed down the system, giving the user the notion as if Windows is a slow and unresponsive operating system ;-).
为什么Windows Patching IAT陷入困难之中?你可能知道,Windows可执行文件和DLL加载到内存后经常重新部署(因为冲突)。这使得不可能在可执行代码输入函数中预先设置目标调用的地址。为了确保调用成功到达目标,在一个可执行映像加载进内存后需要Windows定位和patch每一个调用到输入函数。显然,如此大量的工作在新进程和DLL初始化的时候要做会减慢系统速度,能使用者一个概念是否Windows是一个反应迟钝的操作系统;
Fortunately, the designers of Windows were quite resourceful when they addressed this issue. In the current implementation of Windows executables and DLLs, calls made to imported functions are routed through the IAT using an indirect JMP instruction. The fact that imported function calls are "drained" through one location saves Windows the trouble of traversing the executable image in memory, looking for call instructions that are destined for patching.
目前Windows的可执行文件和DLLs调用输入函数传递到IAT用一个间接的JMP指令。事实是入口函数调用是“消耗”,通过一个位置节省了Windows在内存传输可执行文件映像的麻烦,寻找patching的调用指令。
What all of this got to do with API spying? Well, it seems that Windows IAT redirection mechanism offers a perfect way for intercepting API calls. By overwriting a specific IAT entry with the address of a logging routine, an API interceptor can gain control before the original function gets a chance to be executed by the processor.
API监视怎么开始呢?Windows IAT重定向机制提供了一个很好的方法拦截API调用。通过用记录例程指定的地址重写一个指定的IAT入口,一个API拦截器能获得控制在原函数被处理机执行之前。
Obviously, there are other issues involved in the implementation of this technique, such as the requirement for the logging code to be executed in the memory context of the intercepted application. These issues are discussed in the following resources:
显然,这还有其他的棘手的问题,比如拦截器的请求记录代码在内存中被执行。这些问题在下面的资源中有过讨论。
Matt Pietrek's excellent book: "Windows 95 System Programming Secrets" contains the source code of an API interception utility called APISpy32. This utility was initially published as part of an article written by Matt for the Dec 1995 issue of MSJ. A newer version of APISpy32 is available and can be ordered through Matt's web site at 505K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4N6Z5k6h3q4@1P5g2)9J5k6h3&6W2N6q4)9J5k6g2)9J5y4X3&6T1M7%4m8Q4x3@1t1`.
Spying on COM Objects by Dmitri Leman, WDJ, July 1999.
Matt Pietrek's的书"Windows 95 System Programming Secrets"包含了一个APISpy32的拦截器的源代码。这个工具最初出版是在1995年12月MSJ作为文件的一部分。
Patch the API
This is my favorite technique for hooking API functions. It has the inherent advantage of being able to trace API calls issued from different parts of an application while requiring a modification only to a single location - the API function itself.
这是我最喜爱的挂接API函数的技术。它有天生的优点在于能够跟踪来自一个应用程序不同的位置API调用,同时要求修改只是一个单一的位置-API函数本身。
There are several approaches that can be used here. One option is to replace the first byte of target API with a breakpoint instruction (Int 3). Any call issued to that function would generate an exception, which would be reported to your API interceptor in case it serves as a debugger of the target process. Unfortunately, there are several problems with this approach. First, the poor performance of Windows exception handling mechanism would considerably slow down the system. A second problem is related to the implementation of Windows debugging API. As soon as a debugger shuts down, it terminates all the applications that were under its control. Obviously, such a behavior is completely unacceptable in case you're implementing a system-wide interceptor, which must be able to terminate itself before its target applications cease executing.
有很多方法可以用在这。一个方法就是重定位目标API的第一个字节用断点(int3).任何调用这个函数将会产生一个异常,如果它作为一个目标进程的调试模块将会报告给你的拦截器。不幸的是,这个方法有很多问题存在。首先,Windows异常将会降低系统性能。其次的问题是涉及到Windows调用API。当一个调用器关闭的时候,它会关闭在它控制之下的所有应用程序。显然,上述行为完全不能满足你的系统范围的拦截器的需求,它必须能终止它自己在目标程序结束之前。
Another possibility is to patch the target function with one of the control-transfer instructions of the CPU (i.e. either a CALL or a JMP). Once again, there are several problems with this approach. First, it is possible that the patching would overrun the end of the intercepted function. This can occur in cases where the target API is shorter than 5 bytes (CALL and JMP are each 5 bytes long). Another issue is the need to constantly switch between the patched and "unpatched" versions of the intercepted function. This means that once your logging routine receives control from the CPU, it must restore the intercepted function to its previous unhooked state. This is required to allow the API interceptor to route the call to the original function without generating an infinite loop of calls back to the logging routine. Note that during the time the CPU is executing the original function, other calls to that function might be issued from different parts of the system. Since the function is in the unhooked state at that stage, the API interceptor will miss those calls. A more sophisticated API interceptor might utilize a better technique in order to overcome this limitation. Take a look at the following figure to get a better idea how this could be accomplished.
另一个可能的方法是patch目标应用程序用一个CPU控制传输指定(i.e.either a CALL or a JMP).再一次,这有很多的问题有解决。首先,这个Patching可能溢出在指令函数的结尾。如果目标API小于5个字节的话这就会发生(CALL和JMP都是5個字節长),另一个是需要经常在Patch和"Unpatched"之间切换拦截函数的版本。这意味着当你记录例程受到CPU控制的时候,必须保存拦截器到它先前未挂载的状态。这个请求允许API拦截发送调用到最初的函数没有记录例程产生循环。注意在CPU执行以前函数的这段时间,其他调用这个函数的例程可以从系统的其他地方传送过来。因为函数在这个期间处于无挂载状态,所以API拦截器将不会拦截这些调用。一个好的API拦截器可以利用更好的技术克服这些限制。注意下面的是些内容得到实现这个技术的更好方法。
(这有个图,不会传)
In this case, the API interceptor places a JMP instruction at the beginning of the target function, but not before saving the first 5 bytes of the function to a pre-allocated buffer in memory (a stub). The exact number of bytes to be copied to the stub may change depending on the instructions present at the head of the function. It cases where 5 bytes do not fall within instruction boundary, it is necessary to copy additional bytes until there is enough space for the JMP instruction to be inserted. Note that relative control-transfer instructions (i.e. JMPs and CALLs) need to be modified during copying to ensure that they transfer control to the right location in memory when executed from the stub. Obviously, performing such an analysis of assembly instructions requires the assistance of a disassembler, which, as I have mentioned before, is not very easy to implement.
在这种情况下,API拦截器在目标函数开始的位置放置一个JMP跳转指令,但没有在函数中设置5个字节作为内存缓冲(a stub).精确的字节数被拷贝到stub可能改变通过函数头出现的指令.5个字节没有在指令边界之内,这有必要拷贝附加的字节直到有足够的空间为Jmp指令插入.注意控制传输指定(i.e. JMPs and CALLs)需要在拷贝期间被修改以确保它们在stub执行的时候传输控制内存的正确位置.显然,执行这样一个汇编分析要反汇编的软件,前面我已经提及了,这不是一件简单的事.
If you are not intimidated by the complexity of this technique and wish to use it with your own applications, you might want to have a look at the source code of Detours - an API interception library, which was developed by one of the members in Microsoft's research department.
如果你没有被这个复杂的技术吓住并打算在你的应用程序中使用,你可能需要看一下Detours的源代码 - 一个API拦截库,它被Microsoft's研究部的一个成员开发出来.
Breaking address space barriers
By now, you should have a pretty good idea on how to implement an API interceptor capable of redirecting API calls to your own logging code. However, one problem remains unsolved - how do you ensure that the logging code is executed in the right address space? Before we can answer that question, we need to better understand the internal architecture of Windows memory management unit.
到现在,你应该已经有了一个好的思路如何在你的记录代码中拦截API调用的重定向.然而,有一个问题仍然没有解决 - 你怎样确定记录代码在正确的地址空间执行?在我们回答这个问题之前,我们需要很好的理解Windows内存管理机制.
As you probably know, each 32-bit Windows application gets a unique address space to toy with. During a task switch, Windows updates its page tables to reflect the new process's linear to physical memory mapping (also known as the process memory context). As a result, the page table entries that correspond to the private memory area of the process are modified to point to different physical addresses while those that correspond to shared memory regions remain untouched.
你可能知道,每一个Windows32位应用程序有一个唯一的地址空间.当任务发生切换的时候,Windows更新它的页表使新的进程的线性地址映射到物理地址空间(也就是进程上下文).结果,进程私有地址空间的地址被修改指向不同的物理需共享地址空间的地址不变.
Under Windows 9x, the 4GB linear address space is divided into several distinct memory regions, each with its own predefined purpose. MS-DOS and the first portion of the 16-bit global heap occupy the lowest 4MB. The next region spans from 4MB to 2GB and it is where Windows 9x loads each process's code, data and DLLs. Since each process physical addresses are unique, Windows 9x ensures that when a specific process is active, the page table entries corresponding to the 4MB-2GB region are mapped to this process's physical memory pages. The idea is for all processes to share the same linear addresses but not the same physical addresses (which is obviously impossible). Think of it as if the 4MB-2GB linear memory region is a window into the physical address space. This window slides each time a process is scheduled for execution to provide a view of the unique physical memory locations occupied by that process.
在Windows 9x下,4GB的线性地址空间被分成许多不同的内存区域,每一个区域都有特定的目的.MS-DOS和第一个全局堆占用最低4MB.下一个区域的范围是从4M到2G是Windows9X加载进程代码,数据和DLLs的地址.因为每一个进程的物理地址的唯一的,Windows9x确保当一个指定的进程活动时,页表项把4M-2G的线性地址空间映射到进程物理内存页.这个思路就是所有的进程共享同样的线性地址但不共享同样的物理地址(这显然是不可能的).想像这个4MB-2GB的线性地址区域是一个转换到物理地址空间的窗口.这个窗口滑动是在每次一个线程要在它的私有地址空间执行的时候.
So what's the real benefit of this mechanism anyway? Well, since all processes use the same linear addresses, Windows can load them into different physical locations without performing any code fix-ups. This means that the memory representation of a process can be (almost) an exact copy of its on-disk image.
这个机制真正带来的好处是什么?因为所有的进程用同样的线性地址空间,Windows能加载它们到不同的物理地址空间而不用做任何的代码修改.这意味着一个进程的内存请求可能是(几乎就是)磁盘映像的精确拷贝.
Continuing our exploration of the Windows 9x address space reveals that the region spanning from 2GB to 3GB is reserved for the upper portion of the 16-bit global heap. Also in this region are the memory-mapped files and Windows system DLLs (such as USER32, GDI32 and KERNEL32), which are shared among all running processes. This region is extremely useful for API interceptors, since it is visible in all the active address spaces. In fact, APISpy32 loads its spying DLL (APISpy9x.dll) into this area, thus ensuring that its logging code is accessible from any process issuing a call to an intercepted function.
继续我们的Windows9x地址空间2GB-3GB的探索,这个区域是上层16位全局堆.在这个区域还有内存映射文件和系统DLLs(如USER32,GDI32和KERNEL32),这个区域被所有的进程所共享.这个区域对API拦截非常有用,因为它是可视的活动的地址空间.实际上,APISpy32加载它的监视DLL(APISpy9x.dll)到这个区域,确保它的记录代码对任何进程调用拦截函数是有效的.
Under Windows NT/2000, the story is a bit different. There is no documented way of loading a DLL into an area shared by all processes, thus the only way to ensure that the logging code is accessible by the target process is to inject the spying DLL into its address space. One of the ways to accomplish this is by adding the name of the DLL to the following registry key:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_Dlls
在Windows NT/2000下有一点点的不同.没有一个文档化的方法加载DLL到所有进程共享的这块区域,那么为了确保进程能够访问记录代码的方法就是把监视DLL注入到它的地址空间.完成这个任务是一个方法是把DLL的名字加到注册表的这个位置:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_Dlls
This key causes Windows to load your DLL into every address space in the system. Unfortunately, this technique can only be used to inject a DLL into processes that link with user32.dll, meaning that console applications, which do not usually link with this DLL, are not included. Other methods for injecting a DLL into a process's address space are thoroughly described in the article "Load Your 32-bit DLL into Another Process's Address Space Using INJLIB" by Jeffrey Richter, which was published on the May 1994 issue of MSJ. Additional resources are available below:
Knowledge base articles: Q197571, Q125680, Q125691.
这个键值使Windows加载你的DLL到系统中每一个地址空间.不幸的是,这个技术只能用来注入一个与USER32.dll相关联的DLL,这意味着如控制台应用程序不需要经常连接DLL就不能被监视.其它关于注入DLL到进程地址空间的方法在"Load Your 32-bit Dll into Another Process's Address Space Using INJLIB"这篇Jeffrey Richter的文章上,出版在1994年MSJ,还可参考下面的文章:
Knowledge base articles: Q197571, Q125680, Q125691.
Injecting at the right moment
Knowing how to inject a piece of code into another process's address space is one thing, but timing is also a crucial factor when implementing an API interceptor. Inject at the wrong moment and your interceptor might miss calls issued by the target application. This problem really comes to light when implementing a system-wide interceptor. In that case, the interceptor needs to inject its spying DLL into a process's address space immediately after that process is executed, but right before it issues calls to intercepted functions. The best way to accomplish this is by monitoring calls to the CreateProcess function. When such a call is detected, the interceptor's logging routine passes control to the original CreateProcess function with a modified dwCreationFlags parameter, which contains the CREATE_SUSPENDED value. This ensures that the target process is started, but placed in a suspended state. The interceptor can then inject its spying DLL into the target process's address space and activate it using the ResumeThread API function.
懂得如何注入一块代码到另一个进程的地址空间是一件事,但是执行API拦截时间也是一个关键因素.注入在错误的时刻你的拦截器可能错过目标应用程序的调用API.这个问题尤其暴露在拦截系统范围的API时.在那样的情况下,拦截器需要在进程执行之后立刻注入它的监视DLL到进程地址空间,但是在调用拦截器的时候需要权限.最好方法就是监视CreateProcess函数的调用.当这个调用检测出来的时候,拦截器的记录例程用修改的dwCreateionFlags参数传递控制到原来的CreateProcess函数,修改的参数的值是CREATE_SUSPENDED.这样确保目标进程开始了,但是处于挂起的状态.拦截器这时能用监视DLL注入目标进程地址空间并且用ResumeThread API函数让目标进程运行起来.
Other ways for detecting the execution of processes under Windows are presented in the following section. Unfortunately, due to their asynchronous nature, they are less suited for detecting the appropriate time for injecting a spying DLL into another process's address space.
其他在Windows下监视进程执行的方法在下面的段中.不幸的是,因为他们是异步类型,所以它们不容易在合适的时间注入监视代码到进程地址空间.
Detecting Process Execution
As you saw earlier, it is often necessary for an application to be able to detect the execution of new processes. One possibility, which was previously mentioned, is to hook the CreateProcess function and monitor calls made to that function from different parts of the system. However, implementing a system-wide API interceptor for the sole purpose of hooking a single function often does not justify the effort.
正如你前面看到的,应用程序必须能监视新进程的执行.其中一个可能,这是以前的方法,在系统不同的段中监视CreateProcess.然而,执行系统范围的API拦截器去监视一个函数是不明智的.
Fortunately, there is a simpler way to accomplish that under Windows 95 (OSR 2 and above), 98 and NT/2000, without requiring the support of a full-fledged system-wide API interceptor. Under Windows 9x, it is possible for a virtual device driver to respond to the CREATE_PROCESS message sent by VWIN32 whenever a new process is executed. Windows NT/2000 offers similar functionality through the use of the undocumented PsSetCreateProcessNotifyRoutine function exported from NTOSKRNL. This function allows a device driver to register a callback function that receives notifications from the operating system whenever a new process is started. If you are well versed in device driver development, you should have no trouble deciphering these interfaces yourself using the following examples as your guide:
幸运的是,在Windows95 Windows98和WindowsNT/2000下有一个简单的方法完成这些任务,不需要一个系统范围的API拦截器的支持.在Windows9x下,当一个新进程执行的时候VWIN32发出的CREATE_PROCESS消息是可以被虚拟设备驱动发现的.Windows NT/2000简单的用没有文档化的NTOSKRNL输出的PsSetCreateProcessNotifyRoutine例程.这个函数允许驱动注册一个回调例程接收当一个新进程运行时候由系统发送过来的通知.如果你精通设备驱动开发,通过下面的指导在使用这个接口的时候你应该没有什么麻烦:
The Nerditorium column by Jim Finnegan, which was published on the January 1999 issue of MSJ, presented a utility that detects execution of processes under Windows NT/2000.
The ProcSpy32 utility available on my web site detects execution of processes under Windows 9x using a combination of a device driver and an OCX component.
有能力的人看英文,我翻译的太次,而且没有全部翻译.
翻译的目的是为了自己理解.
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
他的文章
赞赏
雪币:
留言: