分享
 
 
 

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

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

第五章 监控Native API调用

翻译:Kendiv( fcczj@263.net )

更新:Tuesday, April 12, 2005

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

管理句柄

要特别注意的是:SpyHookProtocol()函数仅在它内部包含SpyWriteFilter()函数的条件语句返回TRUE时,才会记录API函数调用。这种行为是为了减少Hook协议中的无用信息。例如,移动鼠标将触发一系列NtReadFile()函数调用。其他产生无用信息的源头和物理实验中的测量有些相似:比如,你在实验环境下测量某种物理效果时,测量方法本身也会对要测量的物理效果产生影响,这将导致测量结果的“失真”。这同样发生在记录API时。注意:NtDeviceIoControlFile()函数也包含在列表5-6中的格式化字符串数组中。不过,Spy设备的客户端采用Device I/O Control调用来读取API Hook协议。这意味着客户端将在协议数据中发现它自己的NtDeviceIoControlFile()调用。根据IOCTL事务的频率,你所期望的数据可能会淹没在它自己的函数调用记录中(译注:此处是指,如果客户端频繁的使用IOCTL,那么记录下的IOCTL函数调用中,仅有少量是我们所期望关注的,而大部分都是客户端自己产生的),Spy设备通过记录安装API Hook的那个线程的ID,并忽略来自该线程的API调用来解决这一问题。

对于所有产生句柄,并且产生的句柄未被记录的API调用,SpyWriteFilter()都将忽略,以消除无用信息。如果Spy设备发现一个句柄已经关闭或已返回给系统,那么任何使用该句柄的函数序列都将被忽略。更有效的是,如果API调用包含由系统或其他在启动API Hook协议之前就存在的进程创建的句柄(这种句柄的生命期都很长),那么该API调用将会被禁止。当然,客户端可以通过IOCTL禁止或允许这种过滤。你可以使用本章稍后将介绍的一个简单的客户端程序来来测试这种过滤机制。你将会很惊讶这个简单的“噪音过滤”功能的强大。

在列表5-6中,产生句柄的函数有:NtCreateFile()、NtCreateKey()、NtOpenFile()、NtOpenKey()、NtOpenProcess()和NtOpenThread()。所有这些函数的格式字符串中都包含一个%+控制符,该控制符用于表示表5-2中的“句柄(注册)”。对于关闭或释放句柄的函数:NtClose()和NtDeleteKey(),在二者的格式字符串中均包含一个%-控制符,对应表5-2中的“句柄(撤销注册)”。其他函数只是简单的使用句柄(这些函数并不会创建获释放这些句柄),在它们的格式字符串中包含%!。基本上,句柄是在进程的上下文中,用于唯一标识一个对象的数字。当一个API函数产生一个新的句柄时,客户端通常必须传入一个OBJECT_ATTRIBUTES结构来存放该句柄以及其他的一些东西,对于对象的名字来说,它应该是可以访问的。稍后,将不再需要该名称,因为系统可以使用对象句柄在句柄表中查寻其对应的对象的属性。这对于API Spy的用户来说却不是好消息,因为它必需在大量的协议项中用毫无疑义的数字代替符号名称。因此,我的Spy设备将注册所有的对象名称及其对应的句柄和该句柄所有者进程的ID,每当一个新的句柄出现,就会更新这一列表。当一个注册的句柄/进程对在稍后出现时,API记录者将会从列表中检索出其对应的原始符号名称,并将其加入的协议中。

已注册的句柄在被某个API函数显示关闭前,都是有效的。如果在某个可产生新的句柄的API调用中,再次出现已注册句柄,那么该句柄将不再处于已注册状态。对于Windows 2000,我频繁的观测系统有时会返回相同的句柄值,尽管在此之前,协议里没有包含任何关闭该句柄的调用。我不记得在Windows NT 4.0中是否也存在类似的情况。一个已注册的句柄,若再次代表不同的对象属性出现时,会被显示的由某种方式关闭掉,因此必须要撤销此种句柄的注册。否则,Spy设备的句柄目录将最终进入溢出状态。

SpyHookPrtocol()调用的SpyWriteFilter()函数(在列表5-7中给出)是句柄跟踪机制的一个基本组成部分。任何被hook的API函数在被调用时,都会经过该函数。列表5-8给出了该函数的实现方式。

BOOL SpyWriteFilter (PSPY_PROTOCOL psp,

PBYTE pbFormat,

PVOID pParameters,

DWORD dParameters)

{

PHANDLE phObject = NULL;

HANDLE hObject = NULL;

POBJECT_ATTRIBUTES poa = NULL;

PDWORD pdNext;

DWORD i, j;

pdNext = pParameters;

i = j = 0;

while (pbFormat [i])

{

while (pbFormat [i] && (pbFormat [i] != '%')) i++;

if (pbFormat [i] && pbFormat [++i])

{

j++;

switch (pbFormat [i++])

{

case 'b':

case 'a':

case 'w':

case 'u':

case 'n':

case 'l':

case 's':

case 'i':

case 'c':

case 'd':

case 'p':

{

break;

}

case 'o':

{

if (poa == NULL)

{

poa = (POBJECT_ATTRIBUTES) *pdNext;

}

break;

}

case '+':

{

if (phObject == NULL)

{

phObject = (PHANDLE) *pdNext;

}

break;

}

case '!':

case '-':

{

if (hObject == NULL)

{

hObject = (HANDLE) *pdNext;

}

break;

}

default:

{

j--;

break;

}

}

pdNext++;

}

}

return // number of arguments ok

(j == dParameters)

&&

// no handles involved

(((phObject == NULL) && (hObject == NULL))

||

// new handle, successfully registered

((phObject != NULL) &&

SpyHandleRegister (psp, PsGetCurrentProcessId (),

*phObject, OBJECT_NAME (poa)))

||

// registered handle

SpyHandleSlot (psp, PsGetCurrentProcessId (), hObject)

||

// filter disabled

(!gfSpyHookFilter));

}

列表5-8. SpyWriteFilter()将从协议中去除未注册的API调用

SpyWriteFilter()主要通过扫描协议格式字符串来查找%o(对象属性)、%+(新的句柄)、%!(打开句柄)和%-(关闭句柄),并对其中某几个控制ID的组合采取特殊的动作,如下所示:

l 如果没有涉及句柄,所有的API调用都将被记录。这涉及所有格式字符串中不包含格式控制ID%+、%!和%-的API函数。

l 如果格式字符串中包含%+,这表示该API函数将分配一个新的句柄,将使用帮助函数SpyHandleRegister()来注册该句柄,并且将该句柄与格式字符串中第一个%o项的名字关联起来。如果这样的名字不存在,则该句柄将以一个空字符串注册。如果注册成功,此API调用将被记录。

l 如果格式字符串中包含%!或%-,则被调用的函数将使用或关闭一个句柄。这种情况下,SpyWriteFilter()将使用SpyHandleSlot()函数来查询该句柄的索引,以测试该句柄是否已注册。如果测试成功,该API调用将被记录。

l 对于所有其他的情况,仅当过滤机制关闭时,函数调用才被记录。过滤机制是否开启由全局逻辑变量gfSpyHookFilter表示。

句柄目录是SPY_PROTOCOL结构的一部分,包含在Spy设备(w2k_spy.sys)的全局DEVICE_CONTEXT结构中,列表5-9给出了它的定义以及SPY_HEADER子结构的定义。在这些定义之后的是四个句柄管理函数(SpyHandleSlot()、SpyHandleName()、SpyHandleUnregister()和SpyHandleRegister())的源代码。通过将句柄的值加入到ahObjets[]数组的末尾来注册该句柄。同时,拥有该句柄的进程ID将被保存到ahprocesses[]数组中,对象名将被复制到awNames[]缓冲区中,名字的起始偏移地址将被保存到adNames[]数组中。当注销一个句柄时,过程与上述类似,这可能需要移动数组中的其它元素以保证数组中不存在“空洞”。列表5-9顶部定义的常量定义了句柄目录的大小:它最多可存放4,096个句柄,名称的大小被限制为1,048,576个Unicode字符(2MB),协议缓冲区的大小为1MB。

#define SPY_HANDLES 0x00001000 // max number of handles

#define SPY_NAME_BUFFER 0x00100000 // object name buffer size

#define SPY_DATA_BUFFER 0x00100000 // protocol data buffer size

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

typedef struct _SPY_HEADER

{

LARGE_INTEGER liStart; // start time

DWORD dRead; // read data index

DWORD dWrite; // write data index

DWORD dCalls; // api usage count

DWORD dHandles; // handle count

DWORD dName; // object name index

}

SPY_HEADER, *PSPY_HEADER, **PPSPY_HEADER;

#define SPY_HEADER_ sizeof (SPY_HEADER)

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

typedef struct _SPY_PROTOCOL

{

SPY_HEADER sh; // protocol header

HANDLE ahProcesses [SPY_HANDLES]; // process id array

HANDLE ahObjects [SPY_HANDLES]; // handle array

DWORD adNames [SPY_HANDLES]; // name offsets

WORD awNames [SPY_NAME_BUFFER]; // name strings

BYTE abData [SPY_DATA_BUFFER]; // protocol data

}

SPY_PROTOCOL, *PSPY_PROTOCOL, **PPSPY_PROTOCOL;

#define SPY_PROTOCOL_ sizeof (SPY_PROTOCOL)

// =================================================================

// HANDLE MANAGEMENT

// =================================================================

DWORD SpyHandleSlot (PSPY_PROTOCOL psp,

HANDLE hProcess,

HANDLE hObject)

{

DWORD dSlot = 0;

if (hObject != NULL)

{

while ((dSlot < psp->sh.dHandles)

&&

((psp->ahProcesses [dSlot] != hProcess) ||

(psp->ahObjects [dSlot] != hObject ))) dSlot++;

dSlot = (dSlot < psp->sh.dHandles ? dSlot+1 : 0);

}

return dSlot;

}

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

DWORD SpyHandleName (PSPY_PROTOCOL psp,

HANDLE hProcess,

HANDLE hObject,

PWORD pwName,

DWORD dName)

{

WORD w;

DWORD i;

DWORD dSlot = SpyHandleSlot (psp, hProcess, hObject);

if ((pwName != NULL) && dName)

{

i = 0;

if (dSlot)

{

while ((i+1 < dName) &&

(w = psp->awNames [psp->adNames [dSlot-1] + i]))

{

pwName [i++] = w;

}

}

pwName [i] = 0;

}

return dSlot;

}

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

DWORD SpyHandleUnregister (PSPY_PROTOCOL psp,

HANDLE hProcess,

HANDLE hObject,

PWORD pwName,

DWORD dName)

{

DWORD i, j;

DWORD dSlot = SpyHandleName (psp, hProcess, hObject,

pwName, dName);

if (dSlot)

{

if (dSlot == psp->sh.dHandles)

{

// remove last name entry

psp->sh.dName = psp->adNames [dSlot-1];

}

else

{

i = psp->adNames [dSlot-1];

j = psp->adNames [dSlot ];

// shift left all remaining name entries

while (j < psp->sh.dName)

{

psp->awNames [i++] = psp->awNames [j++];

}

j -= (psp->sh.dName = i);

// shift left all remaining handles and name offsets

for (i = dSlot; i < psp->sh.dHandles; i++)

{

psp->ahProcesses [i-1] = psp->ahProcesses [i];

psp->ahObjects [i-1] = psp->ahObjects [i];

psp->adNames [i-1] = psp->adNames [i] - j;

}

}

psp->sh.dHandles--;

}

return dSlot;

}

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

DWORD SpyHandleRegister (PSPY_PROTOCOL psp,

HANDLE hProcess,

HANDLE hObject,

PUNICODE_STRING puName)

{

PWORD pwName;

DWORD dName;

DWORD i;

DWORD dSlot = 0;

if (hObject != NULL)

{

// unregister old handle with same value

SpyHandleUnregister (psp, hProcess, hObject, NULL, 0);

if (psp->sh.dHandles == SPY_HANDLES)

{

// unregister oldest handle if overflow

SpyHandleUnregister (psp, psp->ahProcesses [0],

psp->ahObjects [0], NULL, 0);

}

pwName = ((puName != NULL) && SpyMemoryTestAddress (puName)

? puName->Buffer

: NULL);

dName = ((pwName != NULL) && SpyMemoryTestAddress (pwName)

? puName->Length / WORD_

: 0);

if (dName + 1 <= SPY_NAME_BUFFER - psp->sh.dName)

{

// append object to end of list

psp->ahProcesses [psp->sh.dHandles] = hProcess;

psp->ahObjects [psp->sh.dHandles] = hObject;

psp->adNames [psp->sh.dHandles] = psp->sh.dName;

for (i = 0; i < dName; i++)

{

psp->awNames [psp->sh.dName++] = pwName [i];

}

psp->awNames [psp->sh.dName++] = 0;

psp->sh.dHandles++;

dSlot = psp->sh.dHandles;

}

}

return dSlot;

}

列表5-9. 句柄管理相关的结构和函数

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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有