分享
 
 
 

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

王朝system·作者佚名  2006-01-31
窄屏简体版  字體: |||超大  

第六章 在用户模式下调用内核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 driver的Hook 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的基地址,从而将它们转换为绝对线性地址。

NTSTATUS SpyOutputPeExport (PVOID pBase,

PVOID pOutput,

DWORD dOutput,

PDWORD pdInfo)

{

PIMAGE_NT_HEADERS pinh;

PIMAGE_DATA_DIRECTORY pidd;

PIMAGE_EXPORT_DIRECTORY pied;

PVOID pData;

DWORD dData, dBias, i;

PDWORD pdData;

NTSTATUS ns = STATUS_INVALID_PARAMETER;

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

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

{

pidd = pinh->OptionalHeader.DataDirectory

+ IMAGE_DIRECTORY_ENTRY_EXPORT;

if (pidd->VirtualAddress &&

(pidd->Size >= IMAGE_EXPORT_DIRECTORY_))

{

pData = (PBYTE) pBase + pidd->VirtualAddress;

dData = pidd->Size;

if ((ns = SpyOutputBinary (pData, dData,

pOutput, dOutput, pdInfo))

== STATUS_SUCCESS)

{

pied = pOutput;

dBias = pidd->VirtualAddress;

pied->Name -= dBias;

pied->AddressOfFunctions -= dBias;

pied->AddressOfNames -= dBias;

pied->AddressOfNameOrdinals -= dBias;

pdData = PTR_ADD (pied, pied->AddressOfFunctions);

for (i = 0; i < pied->NumberOfFunctions; i++)

{

pdData [i] += (DWORD) pBase;

}

pdData = PTR_ADD (pied, pied->AddressOfNames);

for (i = 0; i < pied->NumberOfNames; i++)

{

pdData [i] -= dBias;

}

}

}

else

{

ns = STATUS_DATA_ERROR;

}

}

return ns;

}

列表6-15. SPY_IO_PE_EXPORT的实现细节

IOCTL函数SPY_IO_PE_SYMBOL

该函数使得用户模式下的应用程序可以访问内核调用接口的符号查找引擎。列表6-16给出了该函数的实现方式,但看起来并不是很让人兴奋,因为它只是列表6-11中的SpyModuleSymbolEx()的外包函数而已。调用者必须传入一个形如”module!symbol”的字符串或者一个”symbol”(如果该symbol属于ntoskrnl.exe)。如果该函数调用成功,函数将返回与符号相关的线性地址,如果调用者提供的symbol无效或者发生了其他错误,则函数返回NULL。

NTSTATUS SpyOutputPeSymbol (PBYTE pbSymbol,

PVOID pOutput,

DWORD dOutput,

PDWORD pdInfo)

{

PVOID pAddress;

NTSTATUS ns = STATUS_INVALID_PARAMETER;

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

&&

((pAddress = SpyModuleSymbolEx (pbSymbol, NULL, &ns))

!= NULL))

{

ns = SpyOutputPointer (pAddress,

pOutput, dOutput, pdInfo);

}

return ns;

}

列表6-16. SPY_IO_PE_SYMBOL的实现细节

IOCTL函数SPY_IO_CALL

最后是我们等待良久的SPY_IO_CALL函数了。列表6-17提供了该函数的实现细节。如果传入的字符串地址正确,此函数将调用SpyModuleSymbolEx(),如果传入的字符串可以被解析,则继续调用SpyCallEx()。和SPY_IO_PE_SYMBOL类似,此函数期望一个形如”module!symbol”或一个简单的”symbol”作为符号名,符号字符串将作为SPY_CALL_INPUT结构的一部分被初始化。如果成功,SPY_IO_CALL返回一个SPY_CALL_OUTPUT结构,该结构中将包含函数调用的结果(如果传入的符号引用的是一个API函数),或者目标变量的值(如果符号引用的是一个公共变量,如NtBuildNumber或KeServiceDescriptorTable)。

如果SPY_IO_CALL调用失败,则将不会返回任何数据。调用者必须适当的处理这种情况。忽略这一错误意味着从内核调用接口返回的是无效数据。如果将这样的数据传递给其他内核函数,将会出现错误。如果你很走运,则错误将由SpyCallEx()内的异常处理例程捕获。如果你很不走运,则整个进程都将挂起在Spy device的IOCTL调用中。而且,这里还存在着出现蓝屏的可能性。但是不要过于担心,在下一节中,将展示如何在用户模式的应用程序中恰当的使用内核调用接口。

NTSTATUS SpyOutputCall (PSPY_CALL_INPUT psci,

PVOID pOutput,

DWORD dOutput,

PDWORD pdInfo)

{

SPY_CALL_OUTPUT sco;

NTSTATUS ns = STATUS_INVALID_PARAMETER;

if (psci->pbSymbol != NULL)

{

psci->pEntryPoint =

(SpyMemoryTestAddress (psci->pbSymbol)

? SpyModuleSymbolEx (psci->pbSymbol, NULL, &ns)

: NULL);

}

if ((psci->pEntryPoint != NULL) &&

SpyMemoryTestAddress (psci->pEntryPoint) &&

((ns = SpyCallEx (psci, &sco)) == STATUS_SUCCESS))

{

ns = SpyOutputBinary (&sco, SPY_CALL_OUTPUT_,

pOutput, dOutput, pdInfo);

}

return ns;

}

列表6-17. SPY_IO_CALL的实现细节

Next:

在下一节中,将讨论如何将内核调用接口封装到DLL中。

………….待续………..

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有