首页
社区
课程
招聘
[原创]深入分析Win7的对象引用跟踪机制
发表于: 2010-9-12 19:06 16300

[原创]深入分析Win7的对象引用跟踪机制

2010-9-12 19:06
16300

Win7的内核新增了一系列带有Tag参数的对象增加引用(Refrence)/减少引用(Derefrence)函数,更易于找出对象使用中的“泄漏”(即Refrence和Derefrence次数不匹配)。
在Win7中,以下所有不带Tag的函数均使用一个默认的Tag("tlfD")直接调用带Tag参数的函数完成相应功能。相关函数如下:
ObfDereferenceObjectWithTag/ObfReferenceObjectWithTag
ObDereferenceObjectDeferDelete/ObDereferenceObjectDeferDeleteWithTag
ObReferenceObjectByHandle/ObReferenceObjectByHandleWithTag
ObReferenceObjectByPointer/ObReferenceObjectByPointerWithTag
ObfReferenceObject/ObfReferenceObjectWithTag
ObOpenObjectByPointer/ObOpenObjectByPointerWithTag
本文将具体分析该机制的内部实现。由于内容实在太多,有些细节只好略过了。

概述
对象的引用跟踪机制类似于我们所熟悉的PoolTag内存泄漏跟踪机制,不同的是PoolTag跟踪的是内存的申请/释放操作,通过比对内存的申请/释放计数判断是否存在内存泄漏。而对象引用跟踪则跟踪的是对象的增加引用(Refrence)/减少引用(Derefrence)过程,通过比对两个操作的计数判断是否存在对象引用的“泄漏”。Win7内核提供了这样一种跟踪机制,在对象增加引用(Refrence)/减少引用(Derefrence)时插入一个操作,获取当前调用的上下文及引用的对象、引用计数等信息存入全局变量中,通过Windbg的辅助观察,可以很容易找到问题所在,方便程序员快速排查代码问题。

一、如何进行对象跟踪设置?
对象跟踪的相关参数主要有两个:
第一个是拥有哪些Tag的对象的增加引用(Refrence)/减少引用(Derefrence)操作将会被跟踪
第二个是哪些进程的增加引用(Refrence)/减少引用(Derefrence)操作会被跟踪
进程这个可能好理解一点,而这个Tag参数的理解则稍有点困难(我最初因为那些带Tag的内核函数所影响就理解错了)。这个值实际上应该是对象类型中的Key值,即_OBJECT_TYPE->Key。如果你要跟踪某一类型的对象,那么就把这个Tag参数设置成那个对象类型的Key值就行了,也就是申请/释放那个对象内存时使用的PoolTag。这样的Tag最多可以设置16个,也就是说最多可以跟踪16种不同类型的对象的引用操作。

设置对象跟踪的相关参数有两种方法,一种是通过注册表,可以使用gFlags工具来完成设置。具体地参考以下文章:
Configuring Object Reference Tracing(852K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3#2K6k6r3&6Q4x3X3g2E0K9h3y4J5L8%4y4G2k6Y4c8Q4x3X3g2U0L8$3#2Q4x3V1k6W2L8W2)9J5k6s2g2K6i4K6u0r3L8r3W2T1M7X3q4J5P5g2)9J5c8X3k6X3y4e0x3&6x3U0p5@1i4K6t1^5N6W2)9K6c8q4k6e0i4K6u0W2z5o6g2Q4x3U0W2Q4x3X3g2S2M7%4m8^5)
附张图,一目了然:

通过gFlags设置的内容实际上被保存在HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Kernel,如下:

通过注册表设置的内容需要重新启动才能起作用。

另一种方式则是实时设置,只需调用相关函数正确设置就可以立即开始对象跟踪。不过该方式MS似乎并未公开相关的内容。但是通过对内核中相关函数的逆向,我完整得出了这种设置方法的过程。设置对象跟踪参数是通过ZwSetSystemInformation实现的,InfoClass的值为86。

#define  SystemObjectTraceInfoClass  (86)

typedef struct _SYSTEM_OBTRACE_INFORMAION{
    /*off=0x00*/BOOLEAN bTraceOn;//是否开启对象引用跟踪
    /*off=0x01*/BOOLEAN bPermanent;//是否启用Permanent标志
    /*off=0x02*/BOOLEAN bUnknow1;
    /*off=0x03*/BOOLEAN bUnknow2;
    /*off=0x04*/ULONG InputProcessNameLength;//传入的进程名称的长度
    /*off=0x08*/WCHAR *InputProcessName;//进程名称
    /*off=0x0C*/ULONG InputTagsLen;//传入的Tags缓冲的长度
    /*off=0x10*/WCHAR *InputTags;//传入的Tags
}SYSTEM_OBTRACE_INFORMAION,*PSYSTEM_OBTRACE_INFORMAION;//size=0x14

使用该功能需要启用SeDebugPrevilege。具体的代码如下:

    WCHAR ProcessName[MAX_PATH]=L"notepad.exe";//进程名称
    WCHAR TraceTags[100]=L"Proc\0Thri\0File\0";//要跟踪的对象类型,注意格式
    ULONG TagCnt=3;//对象类型个数,本次为3个

    NTSTATUS status;
    SYSTEM_OBTRACE_INFORMAION SystemObTraceInfo={0};
    ZeroMemory(&SystemObTraceInfo,sizeof(SYSTEM_OBTRACE_INFORMAION));
    SystemObTraceInfo.bTraceOn=TRUE;
    SystemObTraceInfo.InputProcessNameLength=wcslen(ProcessName)*sizeof(WCHAR);
    SystemObTraceInfo.InputProcessName=ProcessName;
    SystemObTraceInfo.InputTags=TraceTags;
    SystemObTraceInfo.InputTagsLen=TagCnt*(wcslen(TraceTags)+1)*sizeof(WCHAR);//总长度
    printf("InputProcessNameLen=%d   InputTagsLen=%d\n",SystemObTraceInfo.InputProcessNameLength,SystemObTraceInfo.InputTagsLen);
    if (SetPrivilege()==FALSE)
    {
        printf("无法提升SeDebugPrevilege!\n");
        return 0;
    }
    status=ZwSetSystemInformation((enum _SYSTEM_INFORMATION_CLASS)SystemObjectTraceInfoClass,&SystemObTraceInfo,sizeof(SYSTEM_OBTRACE_INFORMAION));
    //要停止跟踪时,只要设置SystemObTraceInfo.bTraceOn为FALSE再次调用ZwSetSystemInformation就可以了。
    if ( ObpTraceFlags )//检查Trace标志是否有效
    {
        if (ObjectHeader->TraceFlags & 1 )//检查当前对象是否被标记为Trace
        {
            ObpPushStackInfo(ObjectHeader, 1, 1, 'tlfD');
        }
    }
BOOLEAN __stdcall 
 ObpPushStackInfo(
    PVOID ObjectHeader, //对象头
    char bRefrenceOrDefrence, //当前操作是增加引用还是减少引用
    WORD Count,//当前操作增加或减少的计数
    LONG Tag//当前增加或减少引用时所使用的Tag参数
 )
{
    BOOLEAN result; 
    PVOID CallStack[16]; 
    ULONG NextSequence;

    memset(CallStack, 0, 0x40);//初始化为零
    if ( KeAreInterruptsEnabled() )
    {
        if ( KeGetCurrentIrql() <= DISPATCH_LEVEL )
        {
            if ( RtlCaptureStackBackTrace(1, 16, CallStack, 0) >= 1 )//关键,获取调用栈中的所有返回地址
            {
            NextSequence=InterlockedIncrement(ObpStackSequence);
            if ( MmCanThreadFault() == TRUE )
              result = ObpPushRefDerefInfo(ObjectHeader, bRefrenceOrDefrence, Count, NextSequence, CallStack, Tag);//记录调用信息
            else
              result = ObpDeferPushRefDerefInfo(ObjectHeader, bRefrenceOrDefrence, Count, NextSequence, CallStack, Tag);
            }
        }
    }
    return result;
}
BOOLEAN __stdcall 
 ObpPushRefDerefInfo(
    PVOID ObjectHeader,//对象头
    BOOLEAN bRefrenceOrDefrence,//当前操作是增加引用还是减少引用
    WORD Count,//当前操作增加或减少的计数
    ULONG CurrentSequence,//一个序数
    POBJECT_REF_TRACE Stacks,//当前调用栈地址信息
    LONG Tag//当前增加或减少引用时所使用的Tag参数
    )
{
    WORD Index=0;
    WORD NextPos;
    OBJECT_REF_INFO RefInfo={0};
    POBJECT_REF_STACK_INFO pObjRefStackInfo;
    POBJECT_REF_STACK_INFO RefStackInfo,PreRefStackInfo;
    //判断ObpTraceFlag及获取ObpStackTraceLock这个锁,过程略过略过
    if ( NT_SUCCESS(ObpGetObjectRefInfo(ObjectHeader, &RefInfo))) //查找ObpObjectTable获取该Object对应的RefInfo,此時RefInfo->ObjectHeader即查找的目标
    {
        CurRefInfo = RefInfo;
        if ( RefInfo )
        {
            Index = ObpGetTraceIndex(Stacks);//该函数通过计算调用栈地址的Hash值,将其存入ObpStackTable表中,并返回在表中的索引
            if ( Index >= 16381 )  //判断Index是否超过了允许的最大值,若超过则认为溢出
            {
                DbgPrintEx(0, 1, "ObpPushRefDerefInfo - ObpStackTable overflow\n");
            }
            else   //若没有超过最大值,正常处理
            {
                NextPos = RefInfo->NextPos;//取当前可用的位置指针
                while ( NexPos )//若有效
                {
                    RefStackInfo=RefInfo.StackInfo[NextPos];//当前要保存栈信息的位置
                    PreRefStackInfo=RefInfo.StackInfo[NextPos-1];//最后一次保存栈信息的位置
                    if ( CurrentSequence >= PreRefStackInfo->Sequence )//上一序数未超过当前值,则认为正常,跳出循环
                        break;
                    //超出的情况处置
                    RefStackInfo->Sequence=PreRefStackInfo->Sequence;
                    RefStackInfo->Index=PreRefStackInfo->Index;
                    RefStackInfo->NumTraces=PreRefStackInfo->NumTraces;
                    RefStackInfo->Tag=PreRefStackInfo->Tag;
                    NextPos -= 1;//上移一个位置
                }
                pObjRefStackInfo=RefInfo.StackInfo[NextPos];//取当前可用的位置
                pObjRefStackInfo->Index = Index | (WORD)(-(bRefrenceOrDefrence != 0) & 0x8000);//保存Index,并根据是增加引用还是减少引用设置标志位
                pObjRefStackInfo->NumTraces = Count;//保存此次的引用计数
                pObjRefStackInfo->Sequence = NextSequence;
                pObjRefStackInfo->Tag = Tag;
                RefInfo->NextPos+=1;   //NextPos加1,指向下一个可用位置
            }
        }
    }
    //释放锁及其它,略
}


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (16)
雪    币: 1163
活跃值: (137)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
2
沙发!顶achillis
2010-9-12 19:08
0
雪    币: 300
活跃值: (284)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
3
对achillis同学的钻研精神表示敬佩、对微软的设计表示敬佩、对achillis共享的精神表示敬佩!
2010-9-12 20:06
0
雪    币: 58782
活跃值: (21915)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
4
感谢achillis的好文章~
2010-9-12 20:09
0
雪    币: 636
活跃值: (174)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
5
果断膜拜
2010-9-12 20:23
0
雪    币: 88
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
速度过来,围观教主+膜拜教主
2010-9-13 00:10
0
雪    币: 146
活跃值: (182)
能力值: ( LV13,RANK:220 )
在线值:
发帖
回帖
粉丝
7
清晨来膜拜下   崇尚分享精神
2010-9-13 08:23
0
雪    币: 11638
活跃值: (3605)
能力值: ( LV5,RANK:71 )
在线值:
发帖
回帖
粉丝
8
支持下教主!
2010-9-13 08:30
0
雪    币: 185
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
教主V5..
2010-9-13 16:15
0
雪    币: 109
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
教主就是教主,,独一无二的。。
收藏留后再看。
2010-9-13 17:19
0
雪    币: 89
活跃值: (230)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
11
看不懂的说………………
2010-9-13 22:33
0
雪    币: 284
活跃值: (106)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
12
这种文章不顶就太不厚道了……
2010-9-13 23:08
0
雪    币: 215
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
强帖,只能膜拜!
2010-9-15 10:53
0
雪    币: 57
活跃值: (55)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
14
强帖,只能膜拜!
2010-9-20 11:08
0
雪    币: 20
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
仰视中。。。。。
2010-9-20 11:29
0
雪    币: 59
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
16
前来学习.......
2010-9-20 16:29
0
雪    币: 411
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
学习了
2010-9-23 01:10
0
游客
登录 | 注册 方可回帖
返回