首页 >> 操作系统 >> Windows 2000

《Undocumented Windows 2000 Secrets》翻译 --- 第六章(4)

作者:Kendiv

第六章  在用户模式下调用内核API函数

翻译:Kendiv( fcczj@263.net )

更新:Friday, May 06, 2005

 

声明:转载请注明出处,并保证文章的完整性,本人保留译文的所有权利。

 

通往用户模式的桥梁

现在,内核调用接口的演化已经缓慢的到达了终点----至少已经涉及内核模式(kernel-mode)。让我们总结一下到目前为止,我们已经获得了什么:

l         名为SpyCallEx()的函数(见列表6-3)将收到一个SPY_CALL_INPUT控制块,该控制块中包含目标地址和一些函数所需的参数。SpyCallEx()调用指定的地址,并且通过一个SPY_CALL_OUTPUT控制块将结果返回。

 

l         一种按名字查找导出的系统函数和变量的方法,该方法由SpyModuleSymbolEx()函数实现(见列表6-11)。

 

现在,最后一个问题是:“我们如何让用户模式下的应用程序访问这些资源?”回答当然是:“通过设备I/O控制(Device I/O Control)”。到现在为止,Spy device提供了一组IOCTL函数,6-1列出了这些函数。该表是第四章的4-2的摘要,4-2包含w2k_spy.sys提供的所有IOCTL函数。列表6-12给出了与SpyDispatcher()函数相关的部分,第四章的列表4-7给出了SpyDispatcher()函数的具体细节。

 

6-1最后一行中,名为SPY_IO_CALL的函数将作为通向用户模式的桥梁。我相信一旦Spy device可以访问这些极具价值的信息,它将很容易使用户模式的应用程序获取这些数据。就像在第四章和第五章中一样,下面我们将对新引入的IOCTL函数作一个简短的介绍。

 

6-1.   与内核调用接口相关的IOCTL函数

函数名

ID

IOCTL编码

   

SPY_IO_MODULE_INFO

19

0x8000604C

返回有关已加载的系统模块的信息

SPY_IO_PE_HEADER

20

0x80006050

返回IMAGE_NT_HEADERS数据

SPY_IO_PE_EXPORT

21

0x80006054

返回IMAGE_EXPORT_DIRECTORY数据

SPY_IO_PE_SYMBOL

22

0x80006058

返回一个导出符号的地址

SPY_IO_CALL

23

0x8000E05C

调用一个位于模块(已加载)内部的函数

 

 

NTSTATUS SpyDispatcher (PDEVICE_CONTEXT pDeviceContext,

                        DWORD           dCode,

                        PVOID           pInput,

                        DWORD           dInput,

                        PVOID           pOutput,

                        DWORD           dOutput,

                        PDWORD          pdInfo)

    {

    SPY_MEMORY_BLOCK smb;

    SPY_PAGE_ENTRY   spe;

    SPY_CALL_INPUT   sci;

    PHYSICAL_ADDRESS pa;

    DWORD            dValue, dCount;

    BOOL             fReset, fPause, fFilter, fLine;

    PVOID            pAddress;

    PBYTE            pbName;

    HANDLE           hObject;

    NTSTATUS         ns = STATUS_INVALID_PARAMETER;

 

    MUTEX_WAIT (pDeviceContext->kmDispatch);

 

    *pdInfo = 0;

 

    switch (dCode)

        {

        // unrelated IOCTL functions ommitted (cf. Listing 4-7)

        case SPY_IO_MODULE_INFO:

            {

            if ((ns = SpyInputPointer (&pbName,

                                       pInput, dInput))

                == STATUS_SUCCESS)

                {

                ns = SpyOutputModuleInfo (pbName,

                                          pOutput, dOutput, pdInfo);

                }

            break;

            }

        case SPY_IO_PE_HEADER:

            {

            if ((ns = SpyInputPointer (&pAddress,

                                       pInput, dInput))

                == STATUS_SUCCESS)

                {

                ns = SpyOutputPeHeader (pAddress,

                                        pOutput, dOutput, pdInfo);

                }

            break;

            }

        case SPY_IO_PE_EXPORT:

            {

            if ((ns = SpyInputPointer (&pAddress,

                                       pInput, dInput))

                == STATUS_SUCCESS)

                {

                ns = SpyOutputPeExport (pAddress,

                                        pOutput, dOutput, pdInfo);

                }

            break;

            }

        case SPY_IO_PE_SYMBOL:

            {

            if ((ns = SpyInputPointer (&pbName,

                                       pInput, dInput))

                == STATUS_SUCCESS)

                {

                ns = SpyOutputPeSymbol (pbName,

                                        pOutput, dOutput, pdInfo);

                }

            break;

            }

        case SPY_IO_CALL:

            {

            if ((ns = SpyInputBinary (&sci, SPY_CALL_INPUT_,

                                      pInput, dInput))

                == STATUS_SUCCESS)

                {

                ns = SpyOutputCall (&sci,

                                    pOutput, dOutput, pdInfo);

                }

            break;

            }

        }

    MUTEX_RELEASE (pDeviceContext->kmDispatch);

    return ns;

    }

列表6-12.  Spy driverHook Command Dispatcher(摘录)

 

IOCTL函数SPY_IO_MODULE_INFO

IOCTL函数SPY_IO_MODULE_INFO接收一个模块基地址,并返回一个SPY_MODULE_INFO结构(如果该地址指向了一个有效的PE Image)。列表6-13给出了该结构的定义以及与其相关的SpyOutputModuleInfo()帮助函数(列表6-12中的SpyDispatcher()将调用该函数)。SpyOutputModuleInfo()基于SpyModuleFind()函数(参见列表6-9),SpyModuleFind()函数返回它从ZwQuerySystemInformation()获取的MODULE_INFO数据。MODULE_INFO数据将被转换为SPY_MODULE_INFO格式后发送给调用者。

 

typedef struct _SPY_MODULE_INFO

    {

    PVOID pBase;

    DWORD dSize;

    DWORD dFlags;

    DWORD dIndex;

    DWORD dLoadCount;

    DWORD dNameOffset;

    BYTE  abPath [MAXIMUM_FILENAME_LENGTH];

    }

    SPY_MODULE_INFO, *PSPY_MODULE_INFO, **PPSPY_MODULE_INFO;

 

#define SPY_MODULE_INFO_ sizeof (SPY_MODULE_INFO)

 

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

NTSTATUS SpyOutputModuleInfo (PBYTE  pbModule,

                              PVOID  pOutput,

                              DWORD  dOutput,

                              PDWORD pdInfo)

    {

    SPY_MODULE_INFO smi;

    PMODULE_LIST    pml;

    PMODULE_INFO    pmi;

    DWORD           dIndex;

    NTSTATUS        ns = STATUS_INVALID_PARAMETER;

 

    if ((pbModule != NULL) && SpyMemoryTestAddress (pbModule) &&

        ((pml = SpyModuleFind (pbModule, &dIndex, &ns)) != NULL))

        {

        pmi = pml->aModules + dIndex;

 

        smi.pBase       = pmi->pBase;

        smi.dSize       = pmi->dSize;

        smi.dFlags      = pmi->dFlags;

        smi.dIndex      = pmi->wIndex;

        smi.dLoadCount  = pmi->wLoadCount;

        smi.dNameOffset = pmi->wNameOffset;

 

        strcpyn (smi.abPath, pmi->abPath, MAXIMUM_FILENAME_LENGTH);

 

        ns = SpyOutputBinary (&smi, SPY_MODULE_INFO_,

                              pOutput, dOutput, pdInfo);

 

        SpyMemoryDestroy (pml);

        }

    return ns;

    }

列表6-13.   SPY_IO_MODULE_INFO的实现方式

 

IOCTL函数SPY_IO_PE_HEADER

IOCTL函数SPY_IO_PE_HEADER只是一个简单的IOCTL外包函数,其核心部分是ntoskrnl.exe导出的RtlImageNtHeader()函数,如列表6-14所示。和SPY_IO_MODULE_INFO类似,SPY_IO_PE_HEADER也需要一个模块基地址。返回的数据是模块的IMAGE_NT_HEADER结构。

 

NTSTATUS SpyOutputPeHeader (PVOID  pBase,

                            PVOID  pOutput,

                            DWORD  dOutput,

                            PDWORD pdInfo)

    {

    PIMAGE_NT_HEADERS pinh;

    NTSTATUS          ns = STATUS_INVALID_PARAMETER;

 

    if ((pBase != NULL) && SpyMemoryTestAddress (pBase) &&

        ((pinh = RtlImageNtHeader (pBase)) != NULL))

        {

        ns = SpyOutputBinary (pinh, IMAGE_NT_HEADERS_,

                              pOutput, dOutput, pdInfo);

        }

    return ns;

    }

列表6-14.  SPY_IO_PE_HEADER的实现方式

 

IOCTL函数SPY_IO_PE_EXPORT

这个函数比上一个IOCTL函数有趣多了。该函数返回与调用者提供的模块基地址相关的IMAGE_EXPORT_DIRECTORY结构。仔细观察列表6-15给出的该函数的实现方式,你会发现它和列表6-10中的SpyModuleExport()极其相似。不过,SpyOutputPeExport()需要完成了更多的工作。这是因为IMAGE_EXPORT_DIRECTORY包含相对地址的缘故,这一点前面已经解释过。在数据被复制到独立的缓冲区之后,调用者还是无法使用这些偏移量,这是因为与这些偏移量相关的基地址已经改变了。在PE表头中没有其他附加的地址信息,因此不可能计算出一个新的与之匹配的基地址。为了减少调用者的工作,SpyOutputPeExport()将所有指向导出节内部的偏移量转换为相对于导出节起始位置的偏移量,这是通过减去它们在IMAGE_DATA_DIRECTORY结构中的VirtualAddress而得到的。地址数组中的数据项必须采用不同的方法进行处理,因为它们引用的是PE Image中的其他节区。因此,SpyOutputPeExport()将它们加上Image的基地址,从而将它们转换为绝对线性地址。