ObQueryNameString源码解读
ObQueryNameString返回指定内核对象的名称。该函数只返回命名对象名称和拥有查询函数的对象名称,其他所有的对象都返回空结构。
下一步是函数的步骤:
第一步.先检查内核对象是否有专门的名称查询函数QueryNameProcedure函数,有则调用此函数并返回。WIXP中只有KEY和FILE对象
有专门的名称查询函数。
第二步.检查是否为非命名对象或者命名对象的名称为NULL, 若是检查缓冲区大小后则返回空结构(ObjectNameInfo.Name为{0,0,0})。
第三步.对于命名对象,追根溯源直到根目录,获得需要的缓冲区的大小。
第四步.若用户提供的缓冲区大小不够,则返回STATUS_INFO_LENGTH_MISMATCH,并返回实际需要的缓冲区大小。
第五步.再次追根溯源到根目录,将名称拷贝至用户缓冲区中。
NTSTATUS
ObQueryNameString (
IN PVOID Object,
OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
IN ULONG Length,
OUT PULONG ReturnLength
)
/*++
Routine description:
This routine processes a query of an object's name information
Arguments:
Object - Supplies the object being queried,内核对象的指针,该值不能为NULL.
ObjectNameInfo - Supplies the buffer to store the name string nformation
一个由用户提供的存放返回值得缓冲区,若不知大小则可以为NULL,由ReturnLength返回需要的缓冲区大小。
Length - Specifies the length, in bytes, of the original object name info buffer.
缓冲区的字节数.该值必须包括OBJECT_NAME_INFORMATION结构和对象名称的长度。根据DDK上推荐该值为1024。
ReturnLength - Contains the number of bytes already used up in the object name info. On return this receives an updatedbyte count. 返回的数据的大小。此值包括OBJECT_NAME_INFORMATION结构和对象名称的长度。
(Length minus ReturnLength) is really now many bytes are left in the output buffer. The buffer supplied to this call mayactually be offset within the original users buffer
Return Value:
An appropriate status value
--*/
{
NTSTATUS Status;
POBJECT_HEADER ObjectHeader;
POBJECT_HEADER_NAME_INFO NameInfo;
POBJECT_HEADER ObjectDirectoryHeader;
POBJECT_DIRECTORY ObjectDirectory;
ULONG NameInfoSize;
PUNICODE_STRING String;
PWCH StringBuffer;
ULONG NameSize;
PAGED_CODE();
//
// Get the object header and name info record if it exists
//
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object ); //获得Object的头部objectHeader
NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader ); //若ObjectHeader是命名对象,则取对象头部的名称;
// 否则返回NULL.
//
// If the object type has a query name callback routine then that is how we get the name
// 第一步.若对象有专门的名称查询函数,则调用该函数并返回结果。
if (ObjectHeader->Type->TypeInfo.QueryNameProcedure != NULL) {
try {
KIRQL SaveIrql;
ObpBeginTypeSpecificCallOut( SaveIrql );
ObpEndTypeSpecificCallOut( SaveIrql, "Query", ObjectHeader->Type, Object );
Status = (*ObjectHeader->Type->TypeInfo.QueryNameProcedure)( Object,
(BOOLEAN)((NameInfo != NULL) && (NameInfo->Name.Length != 0)),
ObjectNameInfo,
Length,
ReturnLength );
} except( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
}
return( Status );
}
//
// Otherwise, the object type does not specify a query name procedure so we get to do the work. The first thing
// to check is if the object doesn't even have a name. If object doesn't have a name then we'll return anempty
//name info structure. 第二步.若没有名称查询函数,则查看不是命名对象或者名称为空则ObQueryNameString返回空。
//
if ((NameInfo == NULL) || (NameInfo->Name.Buffer == NULL)) {
//
// Compute the length of our return buffer, set the output if necessary and make sure the supplied buffer is large enough
//
NameInfoSize = sizeof( OBJECT_NAME_INFORMATION );
try {
*ReturnLength = NameInfoSize;
} except( EXCEPTION_EXECUTE_HANDLER ) {
return( GetExceptionCode() );
}
if (Length < NameInfoSize) {
return( STATUS_INFO_LENGTH_MISMATCH );
}
//
// Initialize the output buffer to be an empty string and then return to our caller
// 这就是空结构。
try {
ObjectNameInfo->Name.Length = 0;
ObjectNameInfo->Name.MaximumLength = 0;
ObjectNameInfo->Name.Buffer = NULL;
} except( EXCEPTION_EXECUTE_HANDLER ) {
//
// Fall through, since we cannot undo what we have done.
//
// **** This should probably get the exception code and return
// that value
//
}
return( STATUS_SUCCESS );
}
//
// First lock the directory mutex to stop before we chase stuff down
// 第三步.对象为有值得命名对象,则先获取整个名称的长度,即需要的内存大小。
ObpEnterRootDirectoryMutex();
try {
//
// The object does have a name but now see if this is
// just the root directory object in which case the name size
// is only the "/" character
// 先判断对象是否就是根目录对象,则名称的长度为sizeof( OBJ_NAME_PATH_SEPARATOR )
if (Object == ObpRootDirectoryObject) {
NameSize = sizeof( OBJ_NAME_PATH_SEPARATOR );
} else {
//
// The named object is not the root so for every directory working out way up we'll add its size to the name keeping track
// of "/" characters inbetween each component. We first start with the object name itself and then move on to the directories
// 若不是根目录对象,则追根溯源该对象所有的目录对象直到根目录,此过程中取得对象总的长度。
ObjectDirectory = NameInfo->Directory;
NameSize = sizeof( OBJ_NAME_PATH_SEPARATOR ) + NameInfo->Name.Length;
//
// While we are not at the root we'll keep moving up
//
while ((ObjectDirectory != ObpRootDirectoryObject) && (ObjectDirectory)) {
//
// Get the name information for this directory
//
ObjectDirectoryHeader = OBJECT_TO_OBJECT_HEADER( ObjectDirectory );
NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectDirectoryHeader );
if ((NameInfo != NULL) && (NameInfo->Directory != NULL)) {
//
// This directory has a name so add it to the accomulated size and move up the tree
//
NameSize += sizeof( OBJ_NAME_PATH_SEPARATOR ) + NameInfo->Name.Length;
ObjectDirectory = NameInfo->Directory;
} else {
//
// This directory does not have a name so we'll give it the "..." name and stop the loop
// 若目录对象没有名称,则设它的名称为"..."并终止循环。
NameSize += sizeof( OBJ_NAME_PATH_SEPARATOR ) + OBP_MISSING_NAME_LITERAL_SIZE;
break;
}
}
}
//
// At this point NameSize is the number of bytes we need to store the name of the object from the root down. The total buffer
// size we are going to need will include this size, plus object name information structure, plus an ending null character
// 现在知道对象名称的长度,由此知道需要缓冲区的大小了。
NameInfoSize = NameSize + sizeof( OBJECT_NAME_INFORMATION ) + sizeof( UNICODE_NULL );
//
// Set the output size and make sure the supplied buffer is large enough to hold the information
//第四步. 置返回长度的大小,若用户提供的缓冲区的大小小于此值,则返回错误STATUS_INFO_LENGTH_MISMATCH。
// 注意了,用户提供的缓冲区的大小是有函数参数Length指定的,
// 而不是有参数ObjectNameInfo.Name.MaximumLength
// 或者ObjectNameInfo.Name.Length指定的。(其实后面两个值大小无所谓)
try {
*ReturnLength = NameInfoSize;
} except( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
leave;
}
if (Length < NameInfoSize) {
Status = STATUS_INFO_LENGTH_MISMATCH;
leave;
}
//
// **** the following IF isn't necessary because name info size is
// already guaranteed to be nonzero from about 23 lines above
//
if (NameInfoSize != 0) {
//
// Set the String buffer to point to the byte right after the last byte in the output string. This following logic actually
// fills in the buffer backwards working from the name back to the root
// 第五步.再次追根溯源直到根目录,将对象名称拷贝到用户缓冲区中。
StringBuffer = (PWCH)ObjectNameInfo;
StringBuffer = (PWCH)((PCH)StringBuffer + NameInfoSize);
//睁大眼睛:ObQueryNameString并没有利用参数ObjectNameInfo.Name.Buffer来指出
//缓冲区的地址,而是简单地认为名称紧跟着OBJECT_NAME_INFORMATION结构。
//这个问题我在博文中已经指出了。
NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
try {
//
// Terminate the string with a null and backup one unicode character
//
*--StringBuffer = UNICODE_NULL;
//
// If the object in question is not the root directory then we are going to put its name in the string buffer
// When we finally reach the root directory we'll append on the final "/"
//
if (Object != ObpRootDirectoryObject) {
//
// Add in the objects name
//
String = &NameInfo->Name;
StringBuffer = (PWCH)((PCH)StringBuffer - String->Length);
RtlMoveMemory( StringBuffer, String->Buffer, String->Length );
//
// While we are not at the root directory we'll keep moving up
//
ObjectDirectory = NameInfo->Directory;
while ((ObjectDirectory != ObpRootDirectoryObject) && (ObjectDirectory)) {
//
// Get the name information for this directory
//
ObjectDirectoryHeader = OBJECT_TO_OBJECT_HEADER( ObjectDirectory );
NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectDirectoryHeader );
//
// Tack on the "/" between the last name we added and this new name
//
*--StringBuffer = OBJ_NAME_PATH_SEPARATOR;
//
// Preappend the directory name, if it has one, and move up to the next directory.
//
if ((NameInfo != NULL) && (NameInfo->Directory != NULL)) {
String = &NameInfo->Name;
StringBuffer = (PWCH)((PCH)StringBuffer - String->Length);
RtlMoveMemory( StringBuffer, String->Buffer, String->Length );
ObjectDirectory = NameInfo->Directory;
} else {
//
// The directory is nameless so use the "..." for its name and break out of the loop
//
StringBuffer = (PWCH)((PCH)StringBuffer - OBP_MISSING_NAME_LITERAL_SIZE);
RtlMoveMemory( StringBuffer, OBP_MISSING_NAME_LITERAL, OBP_MISSING_NAME_LITERAL_SIZE );
break;
}
}
}
//
// Tack on the "/" for the root directory and then set the output unicode string variable to have the right size
// and point to the right spot.
// 置上根目录
*--StringBuffer = OBJ_NAME_PATH_SEPARATOR;
ObjectNameInfo->Name.Length = (USHORT)NameSize;
ObjectNameInfo->Name.MaximumLength = (USHORT)(NameSize+sizeof( UNICODE_NULL ));
ObjectNameInfo->Name.Buffer = StringBuffer;
} except( EXCEPTION_EXECUTE_HANDLER ) {
//
// Fall through, since we cannot undo what we have done.
//
// **** This should probably get the exception code and return that value
//
}
}
Status = STATUS_SUCCESS;
} finally {
ObpLeaveRootDirectoryMutex();
}
return Status;
}