首页
社区
课程
招聘
[原创]win7调试机制简单分析
发表于: 2013-9-23 18:48 20119

[原创]win7调试机制简单分析

2013-9-23 18:48
20119

最近接触很多人都爱上了调试(虽然之前就知道有很多)。
不过要好好的调试一个程序一般也会困难重重的,因为很多程序有反调试功能。
如果针对不同的反调试采用不同的对策实在很麻烦,这个特别考验一个人对内核的了解程度。
所以现在很多人都爱上了自分发异常。这确实是个办法,而且是个很不错的办法。基本网上看到的好像都是xp,2k3的;因为这些可以直接看wrk抄起来也方便嘛。但是现在流行的是win7,xp已经渐渐逝去了,所以我就想着来反汇编下win7的调试机制,看看win7和之前的有什么不同。

废话完了,接入正文。
要分析它的调试机制,那么肯定要从调试原理来下手了。那我首先从用户层的调试函数DebugActiveProcess着手看看。

DebugActiveProcess函数定义如下:
bool DebugActiveProcess(ulong ProcessId);
很简单的操作,直接传递一个想调试进程的id。如果附加成功了就返回true,反之返回false。
我们看看它的汇编代码:
76D5738C        MOV EDI,EDI
76D5738E        PUSH EBP
76D5738F        MOV EBP,ESP
76D57391        CALL <JMP.&ntdll.DbgUiConnectToDbg>        //开头便调用该函数,实际是创建调试对象
76D57396        TEST EAX,EAX
76D57398        JGE SHORT kernel32.76D573A4
76D5739A        PUSH EAX
76D5739B        CALL kernel32.BaseSetLastNTError        //如果调试对象创建失败,就设置该线程最后出错的错误代码(GetLastError)
76D573A0        XOR EAX,EAX
76D573A2        JMP SHORT kernel32.76D573D6                //错误就直接退出了
76D573A4        PUSH ESI
76D573A5        PUSH DWORD PTR SS:[EBP+8]                //push ProcessId
76D573A8        CALL kernel32.76D57326                        //call 76D57326        实际这个call就是打开目标进程得到一个句柄而已..
76D573AD        MOV ESI,EAX                                //是够得到了需要调试进程的handle
76D573AF        TEST ESI,ESI
76D573B1        JE SHORT kernel32.76D573D5                //退出.....
76D573B3        PUSH EDI
76D573B4        PUSH ESI                                //push 被调试进程的handle
76D573B5        CALL <JMP.&ntdll.DbgUiDebugActiveProcess>        //这个就是调试的精华部分了
76D573BA        PUSH ESI
76D573BB        MOV EDI,EAX
76D573BD        CALL DWORD PTR DS:[<&ntdll.NtClose>]        //不管附加成功与否,都关闭被调试进程的句柄
76D573C3        TEST EDI,EDI
76D573C5        JGE SHORT kernel32.76D573D1                //这里再比较上面DbgUiDebugActiveProcess是否执行成功
76D573C7        PUSH EDI
76D573C8        CALL kernel32.BaseSetLastNTError
76D573CD        XOR EAX,EAX
76D573CF        JMP SHORT kernel32.76D573D4
76D573D1        XOR EAX,EAX
76D573D3        INC EAX
76D573D4        POP EDI
76D573D5        POP ESI
76D573D6        POP EBP
76D573D7        RETN 4

DebugActiveProcess函数很简单,可以总结它只干了三件事情,按顺序分别是:
1,创建一个调试对象。
2,得到被调试进程的handle。
3,附加指定的进程。


那么既然是分析,就有必要看看如何创建一个调试对象,如何得到被调试进程的handle和如何附加指定进程的;首先我们看第一个。
创建调试对象是调用了DbgUiConnectToDbg函数,这个函数没有参数,返回值是NTSTATUS;那么我们可以给定义下:
NTSTATUS DbgUiConnectToDbg(void);
看看它的汇编代码:
7759F0C4        MOV EDI,EDI
7759F0C6        PUSH EBP
7759F0C7        MOV EBP,ESP
7759F0C9        MOV ECX,DWORD PTR FS:[18]        //每个线程都有个TEB结构,TEB结构是被fs指向的,而+0x18偏移处是指向TEB自身
7759F0D0        XOR EAX,EAX
7759F0D2        SUB ESP,18
7759F0D5        CMP DWORD PTR DS:[ECX+F24],EAX        //那么既然ecx是指向了本线程的TEB,+F24是TEB的DbgSsReserved(数组)成员的第二个元素
7759F0DB        JNZ SHORT ntdll.7759F10F
7759F0DD        PUSH 1                                //毫无疑问,这是个参数.....要知道该参数的意义,那么我们必须知道参数是给谁用的,那首先避开参数不看,我们看看下面使用它的函数ZwCreateDebugObject,这个跑到内核中是NtCreateDebugObject,恰好wrk中有这个东西的定义,直接复制过来看就好啦(U_U),如下:
NTSTATUS
NtCreateDebugObject (
    OUT PHANDLE DebugObjectHandle,
    IN ACCESS_MASK DesiredAccess,
    IN POBJECT_ATTRIBUTES ObjectAttributes,
    IN ULONG Flags
    );
那么push 1实际就是Flags,一个标记。关于这个标记是什么意思看来我们只能反汇编内核代码才能晓得了
7759F0DF        MOV DWORD PTR SS:[EBP-14],EAX
7759F0E2        MOV DWORD PTR SS:[EBP-C],EAX
7759F0E5        MOV DWORD PTR SS:[EBP-10],EAX
7759F0E8        MOV DWORD PTR SS:[EBP-8],EAX
7759F0EB        MOV DWORD PTR SS:[EBP-4],EAX        //以上都是清零操作,感觉这样清零是不是麻烦了点儿....
7759F0EE        MOV EAX,DWORD PTR FS:[18]
7759F0F4        LEA ECX,DWORD PTR SS:[EBP-18]
7759F0F7        PUSH ECX                        //这个很明显是参数ObjectAttributes
7759F0F8        PUSH 1F000F                        //DesiredAccess是创建的权限
7759F0FD        ADD EAX,0F24
7759F102        PUSH EAX                        //这个就是TEB->DbgSsReserved[1]了
7759F103        MOV DWORD PTR SS:[EBP-18],18        //ObjectAttributes长度
7759F10A        CALL ntdll.ZwCreateDebugObject        //call ZwCreateDebugObject
7759F10F        LEAVE
7759F110        RETN

ZwCreateDebugObject函数也甚是简单,填充好一些参数直接调用ZwCreateDebugObject就完事儿了,注意这里创建调试对象成功后,输出的句柄是保存在TEB->DbgSsReserved[1]中。那么关于它具体是如何创建一个内核调试对象,我们后面再做分析。

上面DebugActiveProcess函数所做的第一个任务完成了,我们看看第二个得到目标进程句柄,是调用76D57326地址处的函数,此函数只有一个参数,参数是目标进程的id,返回值是目标进程的句柄,我们可以给定义下:
HANDLE GetTargProcess(ulong ProcessId);
汇编代码如下:

76D57326        MOV EDI,EDI
76D57328        PUSH EBP
76D57329        MOV EBP,ESP
76D5732B        MOV EAX,DWORD PTR SS:[EBP+8]
76D5732E        SUB ESP,20
76D57331        CMP EAX,-1                                        //判断传入id是够等于-1
76D57334        JNZ SHORT kernel32.76D5733C
76D57336        CALL DWORD PTR DS:[<&ntdll.CsrGetProcessId>]        //那么调用CsrGetProcessId获取一个进程id,这个进程我看了下是csrss.exe进程的id
76D5733C        PUSH ESI
76D5733D        MOV DWORD PTR SS:[EBP-8],EAX
76D57340        LEA EAX,DWORD PTR SS:[EBP-8]
76D57343        PUSH EAX
76D57344        LEA EAX,DWORD PTR SS:[EBP-20]
76D57347        PUSH EAX
76D57348        XOR ESI,ESI
76D5734A        PUSH 0C3A
76D5734F        LEA EAX,DWORD PTR SS:[EBP+8]
76D57352        PUSH EAX
76D57353        MOV DWORD PTR SS:[EBP-4],ESI
76D57356        MOV DWORD PTR SS:[EBP-20],18
76D5735D        MOV DWORD PTR SS:[EBP-1C],ESI
76D57360        MOV DWORD PTR SS:[EBP-14],ESI
76D57363        MOV DWORD PTR SS:[EBP-18],ESI
76D57366        MOV DWORD PTR SS:[EBP-10],ESI
76D57369        MOV DWORD PTR SS:[EBP-C],ESI
76D5736C        CALL DWORD PTR DS:[<&ntdll.NtOpenProcess>]        //调用NtOpenProcess得到指定id所对应的进程句柄。。。
76D57372        CMP EAX,ESI
76D57374        JGE SHORT kernel32.76D5737F
76D57376        PUSH EAX        
76D57377        CALL kernel32.BaseSetLastNTError
76D5737C        MOV DWORD PTR SS:[EBP+8],ESI
76D5737F        MOV EAX,DWORD PTR SS:[EBP+8]
76D57382        POP ESI
76D57383        LEAVE
76D57384        RETN 4

GetTargProcess函数更加简单,首先判断你传入的参数,如果是-1就的话,然后调用CsrGetProcessId获取csrss.exe的本进程id;这里CsrGetProcessId只用了一行代码:MOV EAX,DWORD PTR DS:[775D715C],大家自己可以去看看。最后调用NtOpenProcess来得到进程句柄,这个就更加不用说了,大家太熟悉此函数了。
那么DebugActiveProcess函数所干的第二件事情就如此简单的结束了。

我们看最为关键的第三件事情;它是调用DbgUiDebugActiveProcess函数完成的,我试着写出这个函数的原型:
NTSTATUS DbgUiDebugActiveProcess(HANDLE hProcess);
看看它的汇编代码:

7759F253        MOV EDI,EDI
7759F255        PUSH EBP
7759F256        MOV EBP,ESP
7759F258        MOV EAX,DWORD PTR FS:[18]
7759F25E        PUSH ESI
7759F25F        PUSH DWORD PTR DS:[EAX+F24]                //TEB->DbgSsReserved[1]
7759F265        PUSH DWORD PTR SS:[EBP+8]                //hProcess
7759F268        CALL ntdll.ZwDebugActiveProcess                //ZwDebugActiveProcess从名字上看就是调试的意思,这个只能等到我们分析内核的时候才能看它的具体实现了。
7759F26D        MOV ESI,EAX
7759F26F        TEST ESI,ESI
7759F271        JL SHORT ntdll.7759F289                        //ZwDebugActiveProcess不成功就退出了
7759F273        PUSH DWORD PTR SS:[EBP+8]                //hProcess
7759F276        CALL ntdll.DbgUiIssueRemoteBreakin        //DbgUiIssueRemoteBreakin此函数是创建一条线程到被调试进程中,等下我们看看这个函数。
7759F27B        MOV ESI,EAX
7759F27D        TEST ESI,ESI
7759F27F        JGE SHORT ntdll.7759F289
7759F281        PUSH DWORD PTR SS:[EBP+8]
7759F284        CALL ntdll.DbgUiStopDebugging                //给目标进程创建的线程失败了,就调用DbgUiStopDebugging停止调试。那么可见上面创建远程线程的工作是多么的重要呀.....
7759F289        MOV EAX,ESI
7759F28B        POP ESI
7759F28C        POP EBP
7759F28D        RETN 4
DbgUiDebugActiveProcess函数也十分简单,首先就是直接附加目标进程,这个具体细节我们只能在内核里面去看了。下面分两步操作:
1,附加目标进程成功,就创建一条远程线程到目标进程里面去。
        <1>如果创建远程线程成功,那就说明DbgUiDebugActiveProcess函数算是真正的执行成功了。
        <2>如果创建远程线程不成功,那就调用DbgUiStopDebugging函数解除附加操作,然后退出了。
2,附加目标进程不成功,直接退出。

因为附加进程的具体细节在内核代码里,而创建远程线程的细节就在用户层。所以呀,我们来分析一下这个创建的远程线程操作。首先我来猜测下它的定义:
NTSTATUS DbgUiIssueRemoteBreakin(HANDLE hProcess);
下面是它的汇编代码:

7759F20C        MOV EDI,EDI
7759F20E        PUSH EBP
7759F20F        MOV EBP,ESP
7759F211        PUSH ECX
7759F212        PUSH ECX
7759F213        PUSH ESI
7759F214        PUSH EDI
7759F215        LEA EAX,DWORD PTR SS:[EBP-8]
7759F218        PUSH EAX
7759F219        XOR ESI,ESI
7759F21B        LEA EAX,DWORD PTR SS:[EBP+8]
7759F21E        PUSH EAX
7759F21F        PUSH ESI
7759F220        PUSH ntdll.DbgUiRemoteBreakin        //关键我们看这个地方,这个就是远程线程执行的函数地址,那么这个函数有啥用,我们不妨在下面分析一下。
7759F225        PUSH ESI
7759F226        PUSH 4000
7759F22B        PUSH ESI
7759F22C        PUSH ESI
7759F22D        PUSH 2
7759F22F        PUSH ESI
7759F230        PUSH DWORD PTR SS:[EBP+8]
7759F233        CALL ntdll.77527C92                //这里面经过一系列逻辑最终会调用NtCreateThreadEx
7759F238        MOV EDI,EAX
7759F23A        CMP EDI,ESI
7759F23C        JL SHORT ntdll.7759F246
7759F23E        PUSH DWORD PTR SS:[EBP+8]
7759F241        CALL ntdll.ZwClose
7759F246        MOV EAX,EDI
7759F248        POP EDI
7759F249        POP ESI
7759F24A        LEAVE
7759F24B        RETN 4
DbgUiIssueRemoteBreakin函数重要的东西就是那个创建远程线程的执行地址:DbgUiRemoteBreakin,我试着写出这个函数的声明:
void DbgUiRemoteBreakin(void);
下面是它的汇编代码:

7759F1B3        PUSH 8
7759F1B5        PUSH ntdll.775507E8
7759F1BA        CALL ntdll.77552C0C
7759F1BF        MOV EAX,DWORD PTR FS:[18]                //TEB
7759F1C5        MOV EAX,DWORD PTR DS:[EAX+30]                //EAX = PEB
7759F1C8        CMP BYTE PTR DS:[EAX+2],0                //PEB->BeingDebugged是否等于零,这个成员就是判断是否被调试
7759F1CC        JNZ SHORT ntdll.7759F1D7                //不等于零说明被调试了,跳
7759F1CE        TEST BYTE PTR DS:[7FFE02D4],2                //7FFE02D4我也不晓得啥东西......
7759F1D5        JE SHORT ntdll.7759F1FF
7759F1D7        MOV EAX,DWORD PTR FS:[18]
7759F1DD        TEST BYTE PTR DS:[EAX+FCA],20                //teb+0xFCA处是个联合,这里表示的信息很丰富。0x20二进制就是100000b,这里就比较RanProcessInit是否存在了
7759F1E4        JNZ SHORT ntdll.7759F1FF                //RanProcessInit为1就跳了....
7759F1E6        AND DWORD PTR SS:[EBP-4],0
7759F1EA        CALL ntdll.DbgBreakPoint                //能到这里来才是关键呀,这个DbgBreakPoint十分简单,直接下个int 3断点,因为如果该进程被调试的话,走到int 3的话就会被调试器发现,就会立马中断到这里。那么这个线程的主要任务就是下int 3断点,把被调试进程中断到调试器中去。所以我们OD调试某个进程的时候总会附加后就中断下来原理就是这里。
7759F1EF        JMP SHORT ntdll.7759F1F8
7759F1F1        XOR EAX,EAX
7759F1F3        INC EAX
7759F1F4        RETN
7759F1F5        MOV ESP,DWORD PTR SS:[EBP-18]
7759F1F8        MOV DWORD PTR SS:[EBP-4],-2
7759F1FF        PUSH 0
7759F201        CALL ntdll.RtlExitUserThread                //这里远程线程的使命就结束了。。。
DbgUiRemoteBreakin函数也是十分简单的,我总结出,它的唯一目的就是下int 3断点,把被调试程序中断到调试器。那么这条线程你没必要用系统写的,你自己写个然后创建个远程线程就好了,因为用系统的不安全呀,被调试进程只要对这个函数做个手脚,hook了这个函数;因为这个函数只有当被附加的时候才会调用,普通情况是不会被调用的。所以写个anti debug是很简单的,只要发现调用该函数,直接退出进程,就能达到反调试的目的了。OD的一些插件就是因为这个函数不安去,所以做法是自己创建一个远程线程,这样就不怕anti了。

DebugActiveProcess函数的第三步我们也分析完了,那么它用户层实际是很简单的,看来精华部分全部在内核了。

打个广告,本人创建的编程交流论坛:梦织未来(www.mengwuji.net)
希望大家多多支持一下!


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

收藏
免费 5
支持
分享
最新回复 (36)
雪    币: 61
活跃值: (56)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
2
上一篇介绍了下DebugActiveProcess函数,通过分析得出它就做了三件事儿,其中第一件就是创建调试对象了。那么本篇就看看操作系统是如何创建一个调试对象的。

首先看看NtCreateDebugObject的声明:
NTSTATUS
NtCreateDebugObject (
    OUT PHANDLE DebugObjectHandle,
    IN ACCESS_MASK DesiredAccess,
    IN POBJECT_ATTRIBUTES ObjectAttributes,
    IN ULONG Flags
    );

下面主要分析它的汇编代码:

nt!NtCreateDebugObject:
820c5ff5        push    20h
820c5ff7        push    offset nt! ?? ::FNODOBFM::`string'+0x2b88 (81e6dd28)
820c5ffc        call    nt!_SEH_prolog4 (81e97240)
820c6001        mov     eax,dword ptr fs:[00000124h] //fs在用户层是指向了TEB,在内核是指向了KPCR结构,+124h是当前线程的KTHREAD指针
820c6007        mov     al,byte ptr [eax+13Ah] //al = kthread->PreviousMode,看看是用户模式调用还是内核模式调用
820c600d        mov     byte ptr [ebp-1Ch],al
820c6010        xor     ebx,ebx
820c6012        mov     dword ptr [ebp-4],ebx
820c6015        mov     edi,dword ptr [ebp+8]    //edi = DebugObjectHandle
820c6018        cmp     al,bl
820c601a        je      nt!NtCreateDebugObject+0x38 (820c602d)  //比较一下看看是不是内核模式访问,内核模式的话直接跳到下面去
820c601c        mov     ecx,edi                 //ecx = edi = DebugObjectHandle
820c601e        mov     eax,dword ptr [nt!MmUserProbeAddress (81f81850)] //81f81850里面存放的是用户空间可访问的最大地址,也就是可以认为是用户空间地址的边界线
820c6023        cmp     edi,eax                //因为DebugObjectHandle是用户层的,比较此指针是否超过用户层地址边界
820c6025        jb      nt!NtCreateDebugObject+0x34 (820c6029)        //不超过的话就跳
820c6027        mov     ecx,eax                                //超过用户层地址边界的话,ecx赋值为边界值
820c6029        mov     eax,dword ptr [ecx]                //这个就相当于读DebugObjectHandle指针的值
820c602b        mov     dword ptr [ecx],eax                //这个就相当于写值到DebugObjectHandle指针,实际这两个就是判断是否可读可写,否则的话,就会在这里引发异常,而该异常就会被开头设置的异常处理程序接管,然后处理之.....
820c602d        mov     dword ptr [edi],ebx                //把DebugObjectHandle地址处的值清零
820c602f        push    0FFFFFFFEh
820c6031        pop     eax                                //eax = 0FFFFFFFEh
820c6032        mov     dword ptr [ebp-4],eax    //[ebp-4] = 0FFFFFFFEh
820c6035        test    dword ptr [ebp+14h],eax   //Flags & 0FFFFFFFEh
820c6038        je      nt!NtCreateDebugObject+0x4f (820c6044)  //前文可知,用户层传递的Flags是2,那么这里毫无疑问就会跳了
820c603a        mov     eax,0C000000Dh             //到这里就代表出错了,eax接收了错误代码
820c603f        jmp     nt!NtCreateDebugObject+0x106 (820c60fb)    //直接退出
//下面就是调用ObCreateObject函数了,这个函数和2k3系统是一样的,所以我就直接抄了wrk中的定义:
NTSTATUS
ObCreateObject (
    __in KPROCESSOR_MODE ProbeMode,
    __in POBJECT_TYPE ObjectType,
    __in POBJECT_ATTRIBUTES ObjectAttributes,
    __in KPROCESSOR_MODE OwnershipMode,
    __inout_opt PVOID ParseContext,
    __in ULONG ObjectBodySize,
    __in ULONG PagedPoolCharge,
    __in ULONG NonPagedPoolCharge,
    __out PVOID *Object
    );
820c6044        lea     eax,[ebp-20h]
820c6047        push    eax                                //Object
820c6048        push    ebx                                //NonPagedPoolCharge
820c6049        push    ebx                                //PagedPoolCharge
820c604a        push    3Ch                                //ObjectBodySize
820c604c        push    ebx                                //ParseContext
820c604d        push    dword ptr [ebp-1Ch]                //OwnershipMode
820c6050        push    dword ptr [ebp+10h]                //ObjectAttributes
820c6053        push    dword ptr [nt!DbgkDebugObjectType (81f4fb34)]        //ObjectType
820c6059        push    dword ptr [ebp-1Ch]                //ProbeMode
820c605c        call    nt!ObCreateObject (8203d403)        //call ObCreateObject
//上面调用ObCreateObject创建一个调试(DbgkDebugObjectType)对象。这个函数可以理解成申请一块儿内存,然后根据参数给这块儿内存填充数据,如果一切都正常的话,就把这块内存的地址放到最后一个参数中,也就是Object,在汇编里面是放到了[ebp-20h]这里。可惜win7下面的调试对象和以往的不同了,所以这个结构还要自己分析此调试对象的各个成员,通过ObjectBodySize可知此对象体有0x3C的大小

820c6061        mov     edx,dword ptr [ebp-20h]        //edx = object
820c6064        mov     dword ptr [ebp-30h],edx        //[ebp-0x30] = object
820c6067        cmp     eax,ebx                                //比较ObCreateObject返回值是否小于零,因为内核错误代码都是小于零的
820c6069        jl      nt!NtCreateDebugObject+0x106 (820c60fb)        //出错的话就退出
820c606f        xor     esi,esi
820c6071        inc     esi                                         //esi = 1
820c6072        mov     dword ptr [edx+10h],esi        //这里的edx是创建的object对象,+0x10赋值为1
820c6075        mov     dword ptr [edx+14h],ebx       //[object+0x14] = 0
820c6078        mov     dword ptr [edx+18h],ebx       //[object+0x18] = 0
820c607b        push    ebx                                      //false
820c607c        push    esi                                       //SynchronizationEvent
820c607d        lea     eax,[edx+1Ch]                       //eax = edx+0x1c  这里很关键,有助于我们分析object结构成员含义
820c6080        push    eax
820c6081        call    nt!KeInitializeEvent (81e4efbc)        //call KeInitializeEvent
820c6086        lea     eax,[edx+30h]                        //这里也很重要,通过下面的几步操作就可知object+0x30是个链表
820c6089        mov     dword ptr [eax+4],eax
820c608c        mov     dword ptr [eax],eax
820c608e        push    ebx
820c608f        push    ebx
820c6090        push    edx                                       //注意这里,说明object的第一个成员就是KEVENT结构(注意不是PRKEVENT)
820c6091        call    nt!KeInitializeEvent (81e4efbc)  //call KeInitializeEvent
820c6096        movzx   eax,byte ptr [ebp+14h]         //eax = Flags
820c609a        and     eax,esi
820c609c        add     eax,eax                                 //用户层传入的Flags = 2,所以这些操作后eax = 0
820c609e        mov     dword ptr [edx+38h],eax       //注意,这里的object+0x38应该是个flags
//下面是调用ObInsertObject函数,此函数和2k3相同,所以我直接抄wrk的定义:
NTSTATUS
ObInsertObject (
    __in PVOID Object,
    __in_opt PACCESS_STATE AccessState,
    __in_opt ACCESS_MASK DesiredAccess,
    __in ULONG ObjectPointerBias,
    __out_opt PVOID *NewObject,
    __out_opt PHANDLE Handle
    );
820c60a1        lea     eax,[ebp-24h]
820c60a4        push    eax                                //Handle
820c60a5        push    ebx                                //NewObject
820c60a6        push    ebx                                //ObjectPointerBias
820c60a7        push    dword ptr [ebp+0Ch]                //DesiredAccess
820c60aa        push    ebx                                //AccessState
820c60ab        push    edx                                //Object
820c60ac        call    nt!ObInsertObject (8203c349)        //call ObInsertObject
//ObInsertObject这个函数的作用是把object插入到当前进程的句柄表,也就是把调试对象插入到调试器的句柄表,并且返回一个句柄,这个句柄保存在最后一个参数中(Handle)。

820c60b1        cmp     eax,ebx                                //比较结果是否成功了
820c60b3        jl      nt!NtCreateDebugObject+0x106 (820c60fb)        //失败的话就退出吧,一般来说此处执行ObInsertObject不太可能失败,除非句柄表满了。但是句柄表是很大的,从退出操作可以看出微软也没有在失败后释放掉调试对象内存,那么证明微软也认为这里失败的几率太小了.....
820c60b5        mov     dword ptr [ebp-4],esi                //[ebp-4] = 1
820c60b8        mov     ecx,dword ptr [ebp-24h]                //ecx = Handle
820c60bb        mov     dword ptr [edi],ecx                //[DebugObjectHandle] = Handle 上述操作都顺利执行后,那么就把插入后的句柄复制给用户层提供的参数。然后基本完成了.....
820c60bd        jmp     nt!NtCreateDebugObject+0xe0 (820c60d5)
820c60bf        mov     eax,dword ptr [ebp-14h]
820c60c2        mov     eax,dword ptr [eax]
820c60c4        mov     eax,dword ptr [eax]
820c60c6        mov     dword ptr [ebp-28h],eax
820c60c9        call    nt!ExSystemExceptionFilter (8201fe8e)        //异常处理
820c60ce        ret
820c60cf        mov     esp,dword ptr [ebp-18h]
820c60d2        mov     eax,dword ptr [ebp-28h]
820c60d5        mov     dword ptr [ebp-4],0FFFFFFFEh
820c60dc        jmp     nt!NtCreateDebugObject+0x106 (820c60fb)
820c60de        mov     eax,dword ptr [ebp-14h]
820c60e1        mov     eax,dword ptr [eax]
820c60e3        mov     eax,dword ptr [eax]
820c60e5        mov     dword ptr [ebp-2Ch],eax
820c60e8        call    nt!ExSystemExceptionFilter (8201fe8e)        //异常处理
820c60ed        ret
820c60ee        mov     esp,dword ptr [ebp-18h]
820c60f1        mov     dword ptr [ebp-4],0FFFFFFFEh
820c60f8        mov     eax,dword ptr [ebp-2Ch]
820c60fb        call    nt!_SEH_epilog4 (81e97285)
820c6100        ret     10h
NtCreateDebugObject函数的流程也比较简单,我总结一下:
1,创建一个调试对象。
2,初始化调试对象。
3,把调试对象插入调试器的句柄表。


其中1和3简单明了,看我注释部分理解毫无障碍。就是这个2比较折磨人,原因在于调试对象的结构我们无从得知,抄袭wrk的定义显然不对。通过创建时传入的对象体(注意:不包含对象头)大小可以看出这个结构有0x3c大小,通过一些初始化操作可以看出一些内容,我先在这里总结下通过上述分析得到的一些成员意义。

typedef struct _DEBUG_OBJECT{
        KEVENT                Event1;                 //+0x00
        ULONG                 Unkown1;              //+0x10
        ULONG                 Unkown2;              //+0x14
        ULONG                 Unkown3;              //+0x18
        KEVENT                Event2;                 //+0x1c
        ULONG                 Unkown4;              //+0x2c
        LIST_ENTRY         List;                      //+0x30
        ULONG                 Flags;                   //+0x38
}DEBUG_OBJECT,*PDEBUG_OBJECT;
这是依据目前情况所得到的调试对象结构,那么我们只能在以后的分析过程中慢慢完善它的定义了。
很多人可能不知道这个调试对象是什么东西,在这里我说明一下。它实际就是很多驱动保护中清除的那个DebugPort。。。

这里的分析也比较简单,后面的工作才是艰难的过程。。。。
打个广告,本人创建的编程交流论坛:梦织未来(www.mengwuji.net)
希望大家多多支持一下!
2013-9-23 18:51
0
雪    币: 61
活跃值: (56)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
3
上一篇分析了下内核如何创建调试对象的,此篇我们看看用户层DebugActiveProcess那三步中的第三步,也是最为复杂的一步——附加调试。
用户层DebugActiveProcess的最后一步是调用了ntdll中的ZwDebugActiveProcess函数,此函数对应在内核中声明如下:
NTSTATUS NtDebugActiveProcess(
        HANDLE ProcessHandle,                        //需要调试的进程句柄
        HANDLE DebugObjectHandle                //调试对象句柄,这个就是NtCreateDebugObject函数创建的那个调试对象
        );

汇编代码如下:

820b5eb2        mov     edi,edi
820b5eb4        push    ebp
820b5eb5        mov     ebp,esp
820b5eb7        sub     esp,10h
820b5eba        mov     eax,dword ptr fs:[00000124h]                                //eax = kthread
820b5ec0        push    ebx
820b5ec1        mov     bl,byte ptr [eax+13Ah]                                        //bl = PreviousMode
820b5ec7        push    edi
820b5ec8        push    0                                                        //push HandleInformation
820b5eca        lea     eax,[ebp-4]
820b5ecd        push    eax                                                        //push Object
820b5ece        mov     byte ptr [ebp-0Ch],bl                                        //[ebp=0xc] = PreviousMode
820b5ed1        push    dword ptr [ebp-0Ch]                                        //push        PreviousMode
820b5ed4        mov     edi,800h
820b5ed9        push    dword ptr [nt!PsProcessType (81f70104)]                        //push *PsProcessType
820b5edf        push    edi                                                        //push DesiredAccess
820b5ee0        push    dword ptr [ebp+8]                                        //push ProcessHandle
820b5ee3        call    nt!ObReferenceObjectByHandle (8202903c)                        //call ObReferenceObjectByHandle
820b5ee8        test    eax,eax
820b5eea        jl      nt!NtDebugActiveProcess+0x13b (820b5fed)                //失败就退出
820b5ef0        mov     eax,dword ptr fs:[00000124h]
820b5ef6        push    esi
820b5ef7        mov     esi,dword ptr [ebp-4]                                        //[ebp-0x4]是得到的Object
820b5efa        cmp     esi,dword ptr [eax+50h]                                        //判断是否调试的是进程自己
820b5efd        je      nt!NtDebugActiveProcess+0x12e (820b5fe0)                //是的话就退出
820b5f03        cmp     esi,dword ptr [nt!PsInitialSystemProcess (81f70114)]        //比较是否是PsInitialSystemProcess进程
820b5f09        je      nt!NtDebugActiveProcess+0x12e (820b5fe0)                //是的话就退出
820b5f0f        cmp     bl,1
820b5f12        jne     nt!NtDebugActiveProcess+0x8f (820b5f41)                        //比较是否用户态调用,不是的话跳
820b5f14        mov     eax,dword ptr fs:[00000124h]
820b5f1a        mov     eax,dword ptr [eax+50h]                                        //eax = 当前进程的Object
820b5f1d        mov     esi,dword ptr [ebp-4]                                        //esi = 通过第一个参数得到的Object
820b5f20        test    dword ptr [eax+26Ch],edi                                //测试当前进程是否受到保护ProtectedProcess
820b5f26        jne     nt!NtDebugActiveProcess+0x8f (820b5f41)                        //ProtectedProcess存在就跳
820b5f28        test    dword ptr [esi+26Ch],edi                                //被调试进程ProtectedProcess为真
820b5f2e        je      nt!NtDebugActiveProcess+0x8f (820b5f41)                        //ProtectedProcess==0 跳
820b5f30        mov     ecx,esi
820b5f32        call    nt!ObfDereferenceObject (81e7f163)                        //ObfDereferenceObject(Object);
820b5f37        mov     eax,0C0000712h                                                //设置错误码
820b5f3c        jmp     nt!NtDebugActiveProcess+0x13a (820b5fec)                //退出
820b5f41        push    0                                                        //push HandleInformation
820b5f43        lea     eax,[ebp-8]
820b5f46        push    eax                                                        //push DebugObject
820b5f47        push    dword ptr [ebp-0Ch]                                        //push PreviousMode
820b5f4a        push    dword ptr [nt!DbgkDebugObjectType (81f3eb34)]                //push        *DbgkDebugObjectType
820b5f50        push    2                                                        //push DesiredAccess
820b5f52        push    dword ptr [ebp+0Ch]                                        //push DebugObjectHandle
820b5f55        call    nt!ObReferenceObjectByHandle (8202903c)                        //call ObReferenceObjectByHandle
820b5f5a        mov     edi,eax
820b5f5c        test    edi,edi
820b5f5e        jl      nt!NtDebugActiveProcess+0x123 (820b5fd5)                //失败退出
820b5f60        add     esi,0B0h
820b5f66        mov     ecx,dword ptr [esi]                                        //ecx = RundownProtect(被调试进程的)
820b5f68        and     ecx,0FFFFFFFEh                                                //ecx & 0FFFFFFFEh
820b5f6b        lea     eax,[ecx+2]                                                //eax = ecx + 2

820b5f6e        mov     edx,eax                                                        //Exchange
820b5f70        mov     edi,esi                                                        //Destination
820b5f72        mov     eax,ecx                                                        //Comperand
820b5f74        lock cmpxchg dword ptr [edi],edx                                //这四条指令是执行InterlockedCompareExchange

820b5f78        cmp     eax,ecx
820b5f7a        je      nt!NtDebugActiveProcess+0xd5 (820b5f87)
820b5f7c        mov     ecx,esi
820b5f7e        call    nt!ExfAcquireRundownProtection (81e9fb46)                //call ExfAcquireRundownProtection
820b5f83        test    al,al
820b5f85        je      nt!NtDebugActiveProcess+0x113 (820b5fc5)                //请求失败退出
820b5f87        lea     eax,[ebp-10h]
820b5f8a        push    eax                                                        //可能是LastThread
820b5f8b        push    dword ptr [ebp-8]                                        //DebugObj
820b5f8e        push    dword ptr [ebp-4]                                        //ProcessObj
820b5f91        call    nt!DbgkpPostFakeProcessCreateMessages (820b5e3f)        //call DbgkpPostFakeProcessCreateMessages
820b5f96        push    dword ptr [ebp-10h]                                        //LastThread
820b5f99        push    dword ptr [ebp-8]                                        //DebugObj
820b5f9c        push    dword ptr [ebp-4]                                        //ProcessObj
820b5f9f        call    nt!DbgkpSetProcessDebugObject (820b5628)                //call DbgkpSetProcessDebugObject

820b5fa4        mov     ecx,dword ptr [esi]
820b5fa6        and     ecx,0FFFFFFFEh
820b5fa9        mov     edi,eax
820b5fab        lea     eax,[ecx-2]
820b5fae        mov     edx,eax
820b5fb0        mov     ebx,esi
820b5fb2        mov     eax,ecx
820b5fb4        lock cmpxchg dword ptr [ebx],edx
820b5fb8        cmp     eax,ecx
820b5fba        je      nt!NtDebugActiveProcess+0x118 (820b5fca)
820b5fbc        mov     ecx,esi
820b5fbe        call    nt!ExfReleaseRundownProtection (81e9fc03)
820b5fc3        jmp     nt!NtDebugActiveProcess+0x118 (820b5fca)                //可以认为释放rundown

820b5fc5        mov     edi,0C000010Ah
820b5fca        mov     ecx,dword ptr [ebp-8]
820b5fcd        call    nt!ObfDereferenceObject (81e7f163)
820b5fd2        mov     esi,dword ptr [ebp-4]
820b5fd5        mov     ecx,esi
820b5fd7        call    nt!ObfDereferenceObject (81e7f163)
820b5fdc        mov     eax,edi
820b5fde        jmp     nt!NtDebugActiveProcess+0x13a (820b5fec)
820b5fe0        mov     ecx,esi
820b5fe2        call    nt!ObfDereferenceObject (81e7f163)
820b5fe7        mov     eax,0C0000022h
820b5fec        pop     esi
820b5fed        pop     edi
820b5fee        pop     ebx
820b5fef        leave
820b5ff0        ret     8

代码也比较简单,主要的地方只有两处。为了以后方便回头查看,我试着写成c语言的代码瞧瞧。
首先把这个函数中的一个辅助函数写出来吧,这里是抄袭wrk的,为了方便,我还是改造了一下。虽然wdk里面ExAcquireRundownProtection和ExReleaseRundownProtection是导出的,但是既然汇编里面有出现的话,我还是试着写出一下。

#define        EX_RUNDOWN_COUNT_INC 0x2

BOOLEAN
ExAcquireRundownProtection (
     __inout PEX_RUNDOWN_REF RunRef
     )
{
    ULONG_PTR Value, NewValue;

    Value = RunRef->Count;

    NewValue = Value + EX_RUNDOWN_COUNT_INC;

    NewValue = (ULONG_PTR) InterlockedCompareExchangePointer (&RunRef->Ptr,
                                                                  (PVOID) NewValue,
                                                                  (PVOID) (Value & 0FFFFFFFEh));
    if (NewValue == (Value & 0FFFFFFFEh)) {
        return TRUE;
    }

        return        ExfAcquireRundownProtection(RunRef);

}

VOID
ExReleaseRundownProtection (
     __inout PEX_RUNDOWN_REF RunRef
     )
{
        ULONG_PTR Value, NewValue;

    Value = RunRef->Count;

    NewValue = Value - EX_RUNDOWN_COUNT_INC;

    NewValue = (ULONG_PTR) InterlockedCompareExchangePointer (&RunRef->Ptr,
                                                                  (PVOID) NewValue,
                                                                  (PVOID) (Value & 0FFFFFFFEh));
    if (NewValue == (Value & 0FFFFFFFEh)) {
        return;
    }

        ExfReleaseRundownProtection(RunRef);
}

//下面才是NtDebugActiveProcess的c语言代码。
NTSTATUS NtDebugActiveProcess(
        HANDLE ProcessHandle,
        HANDLE DebugObjectHandle
        )
{
        NTSTATUS Status;
    KPROCESSOR_MODE PreviousMode;
        PDEBUG_OBJECT DebugObject;
    PEPROCESS Process,CurrentProcess;
    PETHREAD LastThread;
        
        PreviousMode = KeGetPreviousMode();
        
        //得到被调试进程的eprocess
        Status = ObReferenceObjectByHandle (ProcessHandle,
                                        0x800,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);
    if (!NT_SUCCESS (Status)) {
        return Status;
    }
        
        //判断被调试进程是否自己或者被调试进程是否PsInitialSystemProcess进程,是的话退出
        if (Process == PsGetCurrentProcess () || Process == PsInitialSystemProcess) {
        ObDereferenceObject (Process);
        return STATUS_ACCESS_DENIED;
    }

        CurrentProcess = PsGetCurrentProcess();

        //判断下模式、当前进程的ProtectedProcess和被调试进程的ProtectedProcess
        if(PreviousMode==UserMode &&
                CurrentProcess->ProtectedProcess==0 &&
                Process->ProtectedProcess)
        {
                //这里很奇怪,如果当前进程被保护的那么就到不了这里了。
                //那说明当前进程是受保护的就可以忽视目标进程是否受保护了。
                ObfDereferenceObject(Process);
                return STATUS_PROCESS_IS_PROTECTED;
        }
        
        //得到调试句柄关联的调试对象(DebugObject)
        Status = ObReferenceObjectByHandle (DebugObjectHandle,
                                        0x2,
                                        DbgkDebugObjectType,
                                        PreviousMode,
                                        &DebugObject,
                                        NULL);
        
        if (NT_SUCCESS (Status)) {
                //进程退出可不好办了,所以在这里还是先调用ExAcquireRundownProtection吧,安全一点儿
        if(ExAcquireRundownProtection(&Process->Rundownprotect))
                {
                        //发送一个虚拟的进程创建消息....从字面理解是这样的,实际它要达到的效果也是如此
                        Status = DbgkpPostFakeProcessCreateMessages(Process,DebugObject,&LastThread);
                        
                        //注意,DbgkpSetProcessDebugObject函数有个参数是寄存器传参,不分析还很难看出来,
                        //其中一个参数是DbgkpPostFakeProcessCreateMessages函数的返回值,而此参数是通过
                        //eax传递进去的,为了保持和windows的代码一致,我也写成wrk一样的吧。
                        //设置调试对象给被调试的进程
                        Status = DbgkpSetProcessDebugObject(Process,DebugObject,Status,LastThread);

                        ExReleaseRundownProtection(&Process->Rundownprotect);
                }else{
                        Status = STATUS_PROCESS_IS_TERMINATING;
                }

                ObDereferenceObject(DebugObject);
    }
    ObDereferenceObject (Process);

    return Status;
}

NtDebugActiveProcess在我看来所做的工作只有两点:
1,提交一个进程被附加的消息,通过DbgkpPostFakeProcessCreateMessages函数。
2,把调试对象和被调试进程关联起来,通过DbgkpSetProcessDebugObject。

好吧,这里分析的差不多了,后面就看DbgkpPostFakeProcessCreateMessages和DbgkpSetProcessDebugObject怎么工作的了。

为了以后能把win7的调试机制完整的整理出来,以后每研究一个函数都尽力写成c代码方便后期整理时阅读。
打个广告,本人创建的编程交流论坛:梦织未来(www.mengwuji.net)
希望大家多多支持一下!
2013-9-23 18:53
0
雪    币: 61
活跃值: (56)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
4
现在来分析下DbgkpPostFakeProcessCreateMessages这个函数。

此函数是在NtDebugActiveProcess里面调用的,函数原型是:
NTSTATUS
DbgkpPostFakeProcessCreateMessages (
    IN PEPROCESS Process,
    IN PDEBUG_OBJECT DebugObject,
    IN PETHREAD *pLastThread
    );

现在它的具体作用还不知道,先看看汇编代码吧:

820b5e3f        mov     edi,edi
820b5e41        push    ebp
820b5e42        mov     ebp,esp
820b5e44        and     esp,0FFFFFFF8h
820b5e47        sub     esp,20h
820b5e4a        lea     eax,[esp+4]
820b5e4e        push    eax                        //输出的参数
820b5e4f        lea     eax,[esp+4]
820b5e53        push    eax                        //输出的参数
820b5e54        push    dword ptr [ebp+0Ch]        //DebugObject
820b5e57        xor     ecx,ecx
820b5e59        push    dword ptr [ebp+8]        //Process
820b5e5c        call    nt!DbgkpPostFakeThreadMessages (820b59af)        //应该是发送假的线程创建消息
820b5e61        test    eax,eax                        //失败的话退出
820b5e63        jl      nt!DbgkpPostFakeProcessCreateMessages+0x5c (820b5e9b)
820b5e65        lea     eax,[esp+8]
820b5e69        push    eax                        //输出的参数----因为提供给KeStackAttachProcess函数,所以它是KAPC_STATE类型指针
820b5e6a        push    dword ptr [ebp+8]        //Process
820b5e6d        call    nt!KeStackAttachProcess (81ea20ef)        //附载到被调试进程
820b5e72        push    dword ptr [ebp+0Ch]        //DebugObject
820b5e75        mov     ecx,dword ptr [ebp+8]        //寄存器传参----Process
820b5e78        push    dword ptr [esp+4]        //这个是DbgkpPostFakeThreadMessages函数的第三个参数
820b5e7c        call    nt!DbgkpPostModuleMessages (820b5c9c)        //call DbgkpPostModuleMessages
820b5e81        lea     eax,[esp+8]
820b5e85        push    eax                        //KeStackAttachProcess的第二个参数
820b5e86        call    nt!KeUnstackDetachProcess (81e9d732)        //解除附载
820b5e8b        mov     ecx,dword ptr [esp]        //DbgkpPostFakeThreadMessages的第三个参数
820b5e8e        call    nt!ObfDereferenceObject (81e7f163)        //说明DbgkpPostFakeThreadMessages函数的第三个参数是个内核对象
820b5e93        mov     ecx,dword ptr [esp+4]
820b5e97        xor     eax,eax
820b5e99        jmp     nt!DbgkpPostFakeProcessCreateMessages+0x5e (820b5e9d)
820b5e9b        xor     ecx,ecx
820b5e9d        mov     edx,dword ptr [ebp+10h]
820b5ea0        mov     dword ptr [edx],ecx        //注意,这里的ecx是DbgkpPostFakeThreadMessages的第四个参数,edx是pLastThread,那说明DbgkpPostFakeThreadMessages函数的第四个参数是PETHREAD结构变量
820b5ea2        mov     esp,ebp
820b5ea4        pop     ebp
820b5ea5        ret     0Ch

好吧,汇编看起来很头痛,试着写出c代码看看。
首先写一下DbgkpPostFakeThreadMessages函数的声明,这个函数有五个参数,其中一个是由ecx传参的。
NTSTATUS
DbgkpPostFakeThreadMessages(
        ULONG                Unkown1,
        PEPROCESS        Process,
        PDEBUG_OBJECT        DebugObject,
        ULONG                *Unkown2,
        PETHREAD        *pLastThread
        );
DbgkpPostFakeThreadMessages函数有两个参数是现在分析不出来的,那么后面慢慢补充。
下面看看DbgkpPostModuleMessages函数的声明:
VOID DbgkpPostModuleMessages(
        PEPROCESS        Process,
        ULONG                Unkown1,
        PDEBUG_OBJECT        DebugObject
        );
DbgkpPostModuleMessages参数的Unkown1实际就是DbgkpPostFakeThreadMessages函数的Unkown2里面的内容。

好了,现在来写出DbgkpPostFakeProcessCreateMessages的c语言代码。

NTSTATUS
DbgkpPostFakeProcessCreateMessages (
    IN PEPROCESS Process,
    IN PDEBUG_OBJECT DebugObject,
    IN PETHREAD *pLastThread
    )
{
        NTSTATUS        Status;
        KAPC_STATE        ApcState;
        ULONG                UnKown1,Unkown2;
        PETHREAD        LastThread;
        
        UnKown1 = 0;
        Status = DbgkpPostFakeThreadMessages(UnKown1,
                                             Process,
                                             DebugObject,
                                             &Unkown2,
                                             &LastThread);
        
        if(NT_SUCCESS(Status))
        {
                KeStackAttachProcess(Process,&ApcState);

                DbgkpPostModuleMessages(Process,Unkown2,DebugObject);

                KeUnstackDetachProcess(&ApcState);

                ObfDereferenceObject(Unkown2);
        }else{
                LastThread = 0;
        }
        

        *pLastThread = LastThread;

        return        Status;
}

DbgkpPostFakeProcessCreateMessages函数的代码也很简单,它主要做两件事情:
1,调用DbgkpPostFakeThreadMessages函数来收集被调试进程的线程信息。
2,附载到被调试进程,调用DbgkpPostModuleMessages收集被调试进程的模块信息。


至于DbgkpPostFakeThreadMessages和DbgkpPostModuleMessages分别实现什么功能,要等到后面慢慢分析出来才晓得。
我们下一篇就分析DbgkpPostFakeThreadMessages吧。
打个广告,本人创建的编程交流论坛:梦织未来(www.mengwuji.net)
希望大家多多支持一下!  
2013-9-23 18:56
0
雪    币: 61
活跃值: (56)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
5
这篇我们来看看DbgkpPostFakeThreadMessages函数。

下面看看此函数的汇编代码:

NTSTATUS
DbgkpPostFakeThreadMessages(
        ULONG                Unkown1,        //此参数为寄存器传参(ecx)
        PEPROCESS        Process,
        PDEBUG_OBJECT        DebugObject,
        ULONG                *Unkown2,
        PETHREAD        *pLastThread
        );
820b59af        push    0E8h
820b59b4        push    offset nt! ?? ::FNODOBFM::`string'+0x74d8 (81e61678)
820b59b9        call    nt!_SEH_prolog4 (81e86240)
820b59be        mov     edi,ecx                                                //DbgkpPostFakeThreadMessages第一个参数
820b59c0        xor     esi,esi
820b59c2        mov     dword ptr [ebp-28h],esi
820b59c5        mov     eax,dword ptr fs:[00000124h]
820b59cb        mov     dword ptr [ebp-34h],eax                                //[ebp-0x34h] = 当前线程ethread
820b59ce        mov     dword ptr [ebp-24h],0C0000001h                        //[ebp-24h]存放的是错误代码
820b59d5        cmp     edi,esi                                                //提供的第一个参数是否为零
820b59d7        je      nt!DbgkpPostFakeThreadMessages+0x38 (820b59e7)        //是的话跳转
820b59d9        mov     byte ptr [ebp-19h],0
820b59dd        mov     dword ptr [ebp-28h],edi
820b59e0        call    nt!ObfReferenceObject (81e80d74)                //说明第一个参数是内核对象
820b59e5        jmp     nt!DbgkpPostFakeThreadMessages+0x47 (820b59f6)        //跳

820b59e7        push    esi                                                //esi = 0
820b59e8        mov     eax,dword ptr [ebp+8]                                //Process
820b59eb        call    nt!PsGetNextProcessThread (8203b449)                //获取Process的第一个线程
820b59f0        mov     edi,eax                                                //edi = PETHREAD
820b59f2        mov     byte ptr [ebp-19h],1
820b59f6        mov     dword ptr [ebp-30h],edi                                //[ebp-0x30] = PETHREAD

820b59f9        test    edi,edi
820b59fb        je      nt!DbgkpPostFakeThreadMessages+0x1c4 (820b5b73)        //edi是零的话退出循环
820b5a01        test    esi,esi                                                //esi判断不是零的话,就减少引用计数
820b5a03        je      nt!DbgkpPostFakeThreadMessages+0x5d (820b5a0c)
820b5a05        mov     ecx,esi
820b5a07        call    nt!ObfDereferenceObject (81e7f163)                //ObfDereferenceObject(esi)
820b5a0c        mov     dword ptr [ebp-2Ch],edi
820b5a0f        mov     ecx,edi
820b5a11        call    nt!ObfReferenceObject (81e80d74)                //增加PsGetNextProcessThread返回线程对象的引用计数
820b5a16        test    dword ptr [edi+3Ch],2000h                        //判断是否是系统线程
820b5a1d        jne     nt!DbgkpPostFakeThreadMessages+0x29d (820b5c4c)        //是系统线程就重新再获取一条线程
820b5a23        lea     ebx,[edi+280h]
820b5a29        test    byte ptr [ebx],2                                //thread->ThreadInserted==0 ?
820b5a2c        jne     nt!DbgkpPostFakeThreadMessages+0x92 (820b5a41)        //不为零跳转
820b5a2e        mov     esi,dword ptr [ebp-34h]                                //esi = 当前线程ethread
820b5a31        mov     eax,edi                                                //eax = thread
820b5a33        call    nt!PsSynchronizeWithThreadInsertion (820e431c)        //线程插入同步?不太明白是什么作用
820b5a38        test    byte ptr [ebx],2                                //如果还是thread->ThreadInserted值没变
820b5a3b        je      nt!DbgkpPostFakeThreadMessages+0x29d (820b5c4c)        //那么我们就重新获取下条线程

820b5a41        lea     ecx,[edi+270h]
820b5a47        mov     edx,dword ptr [ecx]
820b5a49        and     edx,0FFFFFFFEh
820b5a4c        lea     eax,[edx+2]
820b5a4f        mov     esi,eax
820b5a51        mov     ebx,ecx
820b5a53        mov     eax,edx
820b5a55        lock cmpxchg dword ptr [ebx],esi
820b5a59        cmp     eax,edx
820b5a5b        je      nt!DbgkpPostFakeThreadMessages+0xb7 (820b5a66)
820b5a5d        call    nt!ExfAcquireRundownProtection (81e9fb46)
//820b5a41到820b5a5d是执行ExAcquireRundownProtection函数

820b5a62        test    al,al
820b5a64        je      nt!DbgkpPostFakeThreadMessages+0xd3 (820b5a82)        //ExAcquireRundownProtection失败,就跳

820b5a66        mov     dword ptr [ebp-20h],0Ah                                //这个应该是个标记,在后面还会用到
820b5a6d        push    0
820b5a6f        push    edi
820b5a70        call    nt!PsSuspendThread (8209d273)                        //暂停线程
820b5a75        test    eax,eax
820b5a77        jl      nt!DbgkpPostFakeThreadMessages+0xda (820b5a89)        //没有暂停成功那就跳
820b5a79        mov     dword ptr [ebp-20h],2Ah                                //设置标记
820b5a80        jmp     nt!DbgkpPostFakeThreadMessages+0xda (820b5a89)        //暂停成功跳

820b5a82        mov     dword ptr [ebp-20h],12h                                //请求线程停止保护失败就设置标记说明
820b5a89        push    0A8h
820b5a8e        xor     ebx,ebx
820b5a90        push    ebx
820b5a91        lea     eax,[ebp-0E0h]
820b5a97        push    eax
820b5a98        call    nt!memset (81e404c0)                                //清空一片缓冲区,大小是0A8h
820b5a9d        add     esp,0Ch
//根据下面分析,[ebp-19h] = 1 应该表示是第一次执行PsGetNextProcessThread且PsGetNextProcessThread第二个参数为零,那么这样分析的话,该变量实际就是代表了是否是被调试进程的第一个线程
820b5aa0        cmp     byte ptr [ebp-19h],bl
820b5aa3        je      nt!DbgkpPostFakeThreadMessages+0x12b (820b5ada)        //不是第一个线程的话跳转
820b5aa5        test    byte ptr [ebp-20h],10h                                //分析了发现只有当执行ExAcquireRundownProtection失败才会到达此处
820b5aa9        jne     nt!DbgkpPostFakeThreadMessages+0x12b (820b5ada)        //ExAcquireRundownProtection失败跳转
820b5aab        mov     byte ptr [ebp-1Ah],1                                //变量设置true
820b5aaf        mov dword ptr [ebp-0C8h],2                                //这也是一个标记
820b5ab9        mov     esi,dword ptr [ebp+8]                                //esi = Process
820b5abc        mov     ecx,dword ptr [esi+128h]                        //Process->SectionObject
820b5ac2        cmp     ecx,ebx                                                //是否为零
820b5ac4        je      nt!DbgkpPostFakeThreadMessages+0x1ee (820b5b9d)        //为零跳转
820b5aca        call    nt!DbgkpSectionToFileHandle (820b6993)                //通过进程可执行内存区对象获得一个模块句柄
820b5acf        mov     dword ptr [ebp-0BCh],eax                        //[ebp-0xBC]就是模块句柄
820b5ad5        jmp     nt!DbgkpPostFakeThreadMessages+0x1f4 (820b5ba3)        //跳

820b5ada        mov     byte ptr [ebp-1Ah],0                                //可能表示不是第一个线程
820b5ade        mov dword ptr [ebp-0C8h],1                                //改变标记的值
820b5ae8        mov     eax,dword ptr [edi+260h]                        //eax = thread->Win32StartAddress
820b5aee        mov     dword ptr [ebp-0BCh],eax                        //这时候[ebp-0BCh]保存了Win32StartAddress
820b5af4        mov     esi,dword ptr [ebp+8]                                //esi = Process

820b5af7        push    dword ptr [ebp+0Ch]                                //DebugObject
820b5afa        push    dword ptr [ebp-20h]
//这个明显是一个结构,ebp-0E0h到ebp-038h范围都是这个结构的成员
820b5afd        lea     eax,[ebp-0E0h]
820b5b03        push    eax
820b5b04        push    edi                                                //线程
820b5b05        push    esi                                                //进程
820b5b06        call    nt!DbgkpQueueMessage (820b5156)                        //call DbgkpQueueMessage
820b5b0b        mov     dword ptr [ebp-24h],eax                                //保存下返回值
820b5b0e        test    eax,eax
820b5b10        jge     nt!DbgkpPostFakeThreadMessages+0x279 (820b5c28)        //成功的话跳
820b5b16        test    byte ptr [ebp-20h],20h                                //看看是否暂停过线程
820b5b1a        je      nt!DbgkpPostFakeThreadMessages+0x174 (820b5b23)        //没有暂停就跳转
820b5b1c        mov     eax,edi
820b5b1e        call    nt!KeResumeThread (81ea8dca)                        //暂停过的话就恢复该线程
820b5b23        test    byte ptr [ebp-20h],8                                //看看是否申请过停止保护
820b5b27        je      nt!DbgkpPostFakeThreadMessages+0x19b (820b5b4a)        //没申请过的话就跳转

820b5b29        lea     ecx,[edi+270h]
820b5b2f        mov     edx,dword ptr [ecx]
820b5b31        and     edx,0FFFFFFFEh
820b5b34        lea     eax,[edx-2]
820b5b37        mov     esi,eax
820b5b39        mov     ebx,ecx
820b5b3b        mov     eax,edx
820b5b3d        lock cmpxchg dword ptr [ebx],esi
820b5b41        cmp     eax,edx
820b5b43        je      nt!DbgkpPostFakeThreadMessages+0x19b (820b5b4a)
820b5b45        call    nt!ExfReleaseRundownProtection (81e9fc03)
//上面的820b5b29到820b5b45是执行ExReleaseRundownProtection函数

820b5b4a        cmp     dword ptr [ebp-0C8h],2                                //这是个标记,应该表示的是哪种事件,通过分析发现它表示的意义应该是:要是进程刚被附加时,这个值就是2,要是附加后创建的是条线程的话,这个值就是1
820b5b51        jne     nt!DbgkpPostFakeThreadMessages+0x1ba (820b5b69)        //如果是刚刚被调试的话
//当[ebp-0C8h]是2的时候[ebp-0BCh]代表的是被调试进程可执行模块关联的一个句柄
820b5b53        cmp     dword ptr [ebp-0BCh],0
820b5b5a        je      nt!DbgkpPostFakeThreadMessages+0x1ba (820b5b69)        //如果这个句柄值是零,跳转
820b5b5c        push    0
820b5b5e        push    dword ptr [ebp-0BCh]
820b5b64        call    nt!ObCloseHandle (82048b62)                        //句柄值不是零就关闭此句柄
820b5b69        mov     ecx,edi
820b5b6b        call    nt!ObfDereferenceObject (81e7f163)                //减少对线程的引用计数
820b5b70        mov     esi,dword ptr [ebp-2Ch]

820b5b73        cmp     dword ptr [ebp-24h],0                                //看看错误代码
820b5b77        jge     nt!DbgkpPostFakeThreadMessages+0x2b3 (820b5c62)        //没有错误跳转
820b5b7d        mov     ecx,dword ptr [ebp-28h]
820b5b80        test    ecx,ecx
820b5b82        je      nt!DbgkpPostFakeThreadMessages+0x1da (820b5b89)
820b5b84        call    nt!ObfDereferenceObject (81e7f163)                //减少对传入参数的引用
820b5b89        test    esi,esi
820b5b8b        je      nt!DbgkpPostFakeThreadMessages+0x2d8 (820b5c87)
820b5b91        mov     ecx,esi
820b5b93        call    nt!ObfDereferenceObject (81e7f163)                //减少对获取到的线程的引用
820b5b98        jmp     nt!DbgkpPostFakeThreadMessages+0x2d8 (820b5c87)        //退出

820b5b9d        mov     dword ptr [ebp-0BCh],ebx                        //模块句柄为空

820b5ba3        mov     eax,dword ptr [esi+12Ch]
820b5ba9        mov     dword ptr [ebp-0B8h],eax                        //[ebp-0B8h] = Process->SectionBaseAddress
820b5baf        lea     eax,[ebp-0F8h]
820b5bb5        push    eax
820b5bb6        push    esi
820b5bb7        call    nt!KeStackAttachProcess (81ea20ef)                //附载到被调试进程
820b5bbc        mov     dword ptr [ebp-4],ebx
820b5bbf        push    dword ptr [esi+12Ch]
820b5bc5        call    nt!RtlImageNtHeader (81eaed7d)                        //得到PIMAGE_NT_HEADER
820b5bca        cmp     eax,ebx
820b5bcc        je      nt!DbgkpPostFakeThreadMessages+0x237 (820b5be6)        //如果没有得到的话跳转
820b5bce        mov     dword ptr [ebp-0A8h],ebx                        //这是在上面那个A8大小的结构成员
820b5bd4        mov     ecx,dword ptr [eax+0Ch]                                //image_file_header里面PointerToSymbolTable
820b5bd7        mov     dword ptr [ebp-0B4h],ecx                        //给A8大小的那个结构成员
820b5bdd        mov     eax,dword ptr [eax+10h]                                //image_file_header里面NumberOfSymbols
820b5be0        mov     dword ptr [ebp-0B0h],eax                        //给A8大小的那个结构成员

820b5be6        mov     dword ptr [ebp-4],0FFFFFFFEh
820b5bed        jmp     nt!DbgkpPostFakeThreadMessages+0x268 (820b5c17)        //跳

820b5bef        xor     eax,eax
820b5bf1        inc     eax
820b5bf2        ret
820b5bf3        mov     esp,dword ptr [ebp-18h]
820b5bf6        xor     eax,eax
820b5bf8        mov     dword ptr [ebp-0A8h],eax
820b5bfe        mov     dword ptr [ebp-0B4h],eax
820b5c04        mov     dword ptr [ebp-0B0h],eax
820b5c0a        mov     dword ptr [ebp-4],0FFFFFFFEh
820b5c11        mov     edi,dword ptr [ebp-30h]
820b5c14        mov     esi,dword ptr [ebp+8]
//820b5bef到820b5c14是个异常处理

820b5c17        lea     eax,[ebp-0F8h]
820b5c1d        push    eax
820b5c1e        call    nt!KeUnstackDetachProcess (81e9d732)                //解除附载
820b5c23        jmp     nt!DbgkpPostFakeThreadMessages+0x148 (820b5af7)

820b5c28        cmp     byte ptr [ebp-1Ah],0
820b5c2c        je      nt!DbgkpPostFakeThreadMessages+0x29d (820b5c4c)
820b5c2e        mov     byte ptr [ebp-19h],0
820b5c32        mov     ecx,edi
820b5c34        call    nt!ObfReferenceObject (81e80d74)
820b5c39        mov     dword ptr [ebp-28h],edi
820b5c3c        lea     eax,[ebp-0E0h]
820b5c42        push    eax
820b5c43        push    dword ptr [ebp+0Ch]
820b5c46        push    edi
820b5c47        call    nt!DbgkSendSystemDllMessages (820b5417)                //目前不知道它干什么的,等以后慢慢分析
820b5c4c        push    edi
820b5c4d        mov     eax,dword ptr [ebp+8]
820b5c50        call    nt!PsGetNextProcessThread (8203b449)                //获取下一个,实际就是个循环
820b5c55        mov     edi,eax
820b5c57        mov     dword ptr [ebp-30h],eax
820b5c5a        mov     esi,dword ptr [ebp-2Ch]
820b5c5d        jmp     nt!DbgkpPostFakeThreadMessages+0x4a (820b59f9)

820b5c62        mov     eax,dword ptr [ebp-28h]
820b5c65        test    eax,eax
820b5c67        je      nt!DbgkpPostFakeThreadMessages+0x2c6 (820b5c75)        //为零的话
820b5c69        mov     ecx,dword ptr [ebp+10h]
//*Unkown2 = 一个线程,这个线程应该是FirstThread,这个要在后面写出c代码看起来才方便
820b5c6c        mov     dword ptr [ecx],eax
820b5c6e        mov     eax,dword ptr [ebp+14h]
//*pLastThread = LastThread,这个倒是很好理解了
820b5c71        mov     dword ptr [eax],esi
820b5c73        jmp     nt!DbgkpPostFakeThreadMessages+0x2d8 (820b5c87)

820b5c75        test    esi,esi
820b5c77        je      nt!DbgkpPostFakeThreadMessages+0x2d1 (820b5c80)
820b5c79        mov     ecx,esi
820b5c7b        call    nt!ObfDereferenceObject (81e7f163)                //减少对象引用计数
820b5c80        mov     dword ptr [ebp-24h],0C0000001h                        //设置错误码
820b5c87        mov     eax,dword ptr [ebp-24h]
820b5c8a        call    nt!_SEH_epilog4 (81e86285)
820b5c8f        ret     10h
这个函数有些复杂,主要是逻辑判断纵横交错,幸亏有ida的帮助。
汇编代码的注释只是为了自己看而已,我们学习的话最好还是搞成c代码好看些。在弄成c代码之前,我补充下DbgkpPostFakeThreadMessages未知的两个参数,这两个参数通过上面代码可以分析出来大概是什么东西。

NTSTATUS
DbgkpPostFakeThreadMessages(
        PETHREAD        StartThread,        //此参数为寄存器传参(ecx)
        PEPROCESS        Process,
        PDEBUG_OBJECT        DebugObject,
        PETHREAD        *pFirstThread,
        PETHREAD        *pLastThread
        );
上面是完整的声明,那么下面就写出该函数的c代码,首先定义一些有用的结构,方便后续工作。

#define IS_SYSTEM_THREAD(Thread)  (((Thread)->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_SYSTEM) != 0)

//枚举类型,指定是哪种事件
typedef enum _DBGKM_APINUMBER {
    DbgKmExceptionApi,
    DbgKmCreateThreadApi,
    DbgKmCreateProcessApi,
    DbgKmExitThreadApi,
    DbgKmExitProcessApi,
    DbgKmLoadDllApi,
    DbgKmUnloadDllApi,
    DbgKmMaxApiNumber
} DBGKM_APINUMBER;

//异常消息
typedef struct _DBGKM_EXCEPTION {
    EXCEPTION_RECORD ExceptionRecord;
    ULONG FirstChance;
} DBGKM_EXCEPTION, *PDBGKM_EXCEPTION;

//创建线程消息
typedef struct _DBGKM_CREATE_THREAD {
    ULONG SubSystemKey;
    PVOID StartAddress;
} DBGKM_CREATE_THREAD, *PDBGKM_CREATE_THREAD;

//创建进程消息
typedef struct _DBGKM_CREATE_PROCESS {
    ULONG SubSystemKey;
    HANDLE FileHandle;
    PVOID BaseOfImage;
    ULONG DebugInfoFileOffset;
    ULONG DebugInfoSize;
    DBGKM_CREATE_THREAD InitialThread;
} DBGKM_CREATE_PROCESS, *PDBGKM_CREATE_PROCESS;

//退出线程消息
typedef struct _DBGKM_EXIT_THREAD {
    NTSTATUS ExitStatus;
} DBGKM_EXIT_THREAD, *PDBGKM_EXIT_THREAD;

//退出进程消息
typedef struct _DBGKM_EXIT_PROCESS {
    NTSTATUS ExitStatus;
} DBGKM_EXIT_PROCESS, *PDBGKM_EXIT_PROCESS;

//加载模块消息
typedef struct _DBGKM_LOAD_DLL {
    HANDLE FileHandle;
    PVOID BaseOfDll;
    ULONG DebugInfoFileOffset;
    ULONG DebugInfoSize;
} DBGKM_LOAD_DLL, *PDBGKM_LOAD_DLL;

//卸载模块消息
typedef struct _DBGKM_UNLOAD_DLL {
    PVOID BaseAddress;
} DBGKM_UNLOAD_DLL, *PDBGKM_UNLOAD_DLL;

//PORT_MESSAGE结构
typedef struct _PORT_MESSAGE
{
     ULONG u1;
     ULONG u2;
     union
     {
          CLIENT_ID ClientId;
          Float DoNotUseThisField;
     };
     ULONG MessageId;
     union
     {
          ULONG ClientViewSize;
          ULONG CallbackId;
     };
} PORT_MESSAGE, *PPORT_MESSAGE;

//消息结构
typedef struct _DBGKM_APIMSG {
    PORT_MESSAGE h;                                        //+0x0
    DBGKM_APINUMBER ApiNumber;                                //+0x18
    NTSTATUS ReturnedStatus;                                //+0x1c
    union {
        DBGKM_EXCEPTION Exception;
        DBGKM_CREATE_THREAD CreateThread;
        DBGKM_CREATE_PROCESS CreateProcessInfo;
        DBGKM_EXIT_THREAD ExitThread;
        DBGKM_EXIT_PROCESS ExitProcess;
        DBGKM_LOAD_DLL LoadDll;
        DBGKM_UNLOAD_DLL UnloadDll;
    } u;                                                //0x20
} DBGKM_APIMSG, *PDBGKM_APIMSG;
以上结构除PORT_MESSAGE外基本出自2k系统源码,可能有很大出入,但是这些结构比较复杂(联合太多,分析很有难度),所以我找个现成的。分析了下xp发现了它的DBGKM_APIMSG也是同样的大小,而且看起来里面的成员结构也没改变。可是这个结构明显没有A8大小,既然和xp一样那我就从ReactOS源码中找相关定义。惊奇的是,ReactOS里面居然也是这样的定义......看来问题只能在后面慢慢揭晓。

NTSTATUS
DbgkpPostFakeThreadMessages(
        PETHREAD        StartThread,        //此参数为寄存器传参(ecx)
        PEPROCESS        Process,
        PDEBUG_OBJECT        DebugObject,
        PETHREAD        *pFirstThread,
        PETHREAD        *pLastThread
        )
{
        NTSTATUS Status;
        PETHREAD Thread, FirstThread, LastThread, CurrentThread;
        DBGKM_APIMSG ApiMsg;        //上面分析的一个未知的结构体,应该就是DBGKM_APIMSG类型的结构
        BOOLEAN First = TRUE;
        BOOLEAN IsFirstThread;
        PIMAGE_NT_HEADERS NtHeaders;
        ULONG Flags;
        NTSTATUS Status;
        KAPC_STATE ApcState;

        Status = STATUS_UNSUCCESSFUL;

        CurrentThread = PsGetCurrentThread();

        if(StartThread==0)
        {
                StartThread = PsGetNextProcessThread(Process,0);
                First = TRUE;
        }else{
                First = FALSE;
                FirstThread = StartThread;
                ObfReferenceObject(StartThread);
        }

        for(        Thread = StartThread;
                Thread != NULL;
                Thread = PsGetNextProcessThread (Process, Thread))
        {
                if(LastThread!=0)
                {
                        ObfDereferenceObject(LastThread);
                }
               
                LastThread = Thread;
                ObfReferenceObject(LastThread);

                if(IS_SYSTEM_THREAD(Thread))
                {        continue;        }
               
                if(Thread->ThreadInserted==0)
                {
                        PsSynchronizeWithThreadInsertion(Thread,CurrentThread);
                        if(thread->ThreadInserted==0)
                        {        continue;        }
                }
               
                if(ExAcquireRundownProtection (&Thread->RundownProtect))
                {
                        Flags = 0xA;
                        Status = PsSuspendThread(Thread,0);
                        if(NT_SUCCESS(Status))
                        {
                                Flags = 0x2A;
                        }
                }else{
                        Flags = 0x12;
                }
               
                //每次构造一个DBGKM_APIMSG结构
                memset(&ApiMsg,0,sizeof(DBGKM_APIMSG));

                if(First && (Flags&0x10==0))
                {
                        //进程的第一个线程才会到这里
                        IsFirstThread = TURE;
                        ApiMsg.ApiNumber = DbgKmCreateProcessApi;
                        if(Process->SectionObject)
                        {
                                //DbgkpSectionToFileHandle函数是返回一个模块的句柄
                                ApiMsg.u.CreateProcessInfo.FileHandle = DbgkpSectionToFileHandle(Process->SectionObject);
                        }else{
                                ApiMsg.u.CreateProcessInfo.FileHandle = NULL;
                        }
                        ApiMsg.u.CreateProcessInfo.BaseOfImage = Process->SectionBaseAddress;

                        KeStackAttachProcess(Process,&ApcState);
                        
                        __try{
                                NtHeaders = RtlImageNtHeader(Process->SectionBaseAddress);
                                if(NtHeaders)
                                {
                                        ApiMsg.u.CreateProcessInfo.InitialThread.StartAddress = NULL;
                                        ApiMsg.u.CreateProcessInfo.DebugInfoFileOffset = NtHeaders->FileHeader.PointerToSymbolTable;
                                        ApiMsg.u.CreateProcessInfo.DebugInfoSize       = NtHeaders->FileHeader.NumberOfSymbols;
                                }
                        }except(EXCEPTION_EXECUTE_HANDLER){
                                ApiMsg.u.CreateProcessInfo.InitialThread.StartAddress = NULL;
                                ApiMsg.u.CreateProcessInfo.DebugInfoFileOffset = 0;
                                ApiMsg.u.CreateProcessInfo.DebugInfoSize = 0;
                        }

                        KeUnstackDetachProcess(&ApcState);
                }else{
                        IsFirstThread = FALSE;
                        ApiMsg.ApiNumber = DbgKmCreateThreadApi;
                        ApiMsg.u.CreateThread.StartAddress = Thread->StartAddress;
                }

                Status = DbgkpQueueMessage (        Process,
                                                Thread,
                                                &ApiMsg,
                                                Flags,
                                                DebugObject);

                if(!NT_SUCCESS(Status))
                {
                        if(Flags & 0x20)
                        {
                                KeResumeThread(Thread);
                        }

                        if(Flags & 0x8)
                        {
                                ExReleaseRundownProtection(&Thread->RundownProtect);
                        }

                        if(ApiMsg.ApiNumber == DbgKmCreateProcessApi && ApiMsg.u.CreateProcessInfo.FileHandle != NULL)
                        {
                                ObCloseHandle (ApiMsg.u.CreateProcessInfo.FileHandle, KernelMode);
                        }

                        ObfDereferenceObject(Thread);
                        break;

                }else if(IsFirstThread){
                        First = FALSE;
                        ObReferenceObject (Thread);
                        FirstThread = Thread;

                        DbgkSendSystemDllMessages(Thread,DebugObject,&ApiMsg);
                }
        }

        if (!NT_SUCCESS (Status)) {
                if (FirstThread)
                {
                        ObDereferenceObject (FirstThread);
                }
                if (LastThread != NULL)
                {
                        ObDereferenceObject (LastThread);
                }
        } else {
                if (FirstThread) {
                        *pFirstThread = FirstThread;
                        *pLastThread = LastThread;
                } else {

                        if (LastThread != NULL)
                        {
                                ObDereferenceObject (LastThread);
                        }
                        Status = STATUS_UNSUCCESSFUL;
                }
        }
        return Status;
}
这个函数的逻辑有点多,但做的事情不是很多,总结下它做了哪几件事:
1,设置每个被调试进程为暂停状态和申请线程停止保护。
2,为每个线程调用DbgkpQueueMessage函数,如果该函数调用失败则做退出工作。
3,如果是创建一个进程的话,当得到第一个线程时,要调用DbgkSendSystemDllMessages函数。

还有其他工作若干,只是显得不太重要所以也懒得总结了。实际这个函数主要就干两件事情——暂停被调试进程的线程并且为每个线程调用DbgkpQueueMessage函数。关于暂停线程十分好理解,那么为什么要调用DbgkpQueueMessage函数呢?这个要等分析完该函数才知道答案。
注意此结构中涉及到一个DBGKM_APIMSG结构,这个结构相当重要。我目前还不敢确定上面对这个结构的定义是正确的,只是现在分析的太少,等后面慢慢分析多点就大概知道是做什么的了,这里只能说明它可能是表示一个消息种类和消息携带的信息。

函数中有调用到三个未知函数:
1,DbgkpSectionToFileHandle;
2,DbgkSendSystemDllMessages;
3,DbgkpQueueMessage.

前两个为辅助函数,第三个才是重点。后一篇以介绍第三个函数为主,但是前两个函数在恰当的时候我就会分析出来的。
打个广告,本人创建的编程交流论坛:梦织未来(www.mengwuji.net)
希望大家多多支持一下!   
2013-9-23 19:00
0
雪    币: 11
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
似乎xp和win7差别不大……
关于xp的调试机制……看wrk即可……不用这么麻烦
2013-9-23 19:00
0
雪    币: 458
活跃值: (306)
能力值: ( LV12,RANK:400 )
在线值:
发帖
回帖
粉丝
7
无极终于把这份资料拿出来了,感谢 分享。
2013-9-23 19:04
0
雪    币: 61
活跃值: (56)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
8
恩,虽说差别不大,但是也不能直接拿来使用,其中的还是有一些差别的。这些差别不处理估计有难以预料的结果。但是要知道这些差别只有慢慢分析了,别无它法......
2013-9-23 19:04
0
雪    币: 61
活跃值: (56)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
9
哎,为了论坛发展,只能做苦力了,话说我最讨厌汇编了.....
2013-9-23 19:05
0
雪    币: 541
活跃值: (654)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
10
厉害,顶楼主
2013-9-23 19:07
0
雪    币: 623
活跃值: (40)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
11
mark
2013-9-23 19:18
0
雪    币: 61
活跃值: (56)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
12
一只菜鸟而已.....
2013-9-23 19:26
0
雪    币: 82
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
非常有深度,菜鸟不懂啊。
2013-9-23 19:35
0
雪    币: 127
活跃值: (2803)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
占楼出租
2013-9-23 20:49
0
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
膜拜大神
2013-9-23 21:03
0
雪    币: 11
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
32位就是好……用idaf5一下就好……然后再对照原汇编码修改
2013-9-23 21:14
0
雪    币: 1392
活跃值: (5212)
能力值: ( LV13,RANK:240 )
在线值:
发帖
回帖
粉丝
17
mArk make
2013-9-23 21:16
0
雪    币: 61
活跃值: (56)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
18
额,我可不敢用f5,那种代码比汇编还难读
2013-9-23 21:47
0
雪    币: 458
活跃值: (306)
能力值: ( LV12,RANK:400 )
在线值:
发帖
回帖
粉丝
19
你各种大牛啊。。
2013-9-24 09:33
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
表示看不懂 但还是非常感谢楼主的分享
2013-9-24 10:00
0
雪    币: 225
活跃值: (107)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
无极妹子,我来顶你
2013-9-24 10:01
0
雪    币: 211
活跃值: (118)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
LZ辛苦了,顶一下。
2013-9-24 10:12
0
雪    币: 88
活跃值: (335)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
23
好文,顶
2013-9-24 15:31
0
雪    币: 81
活跃值: (40)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
24
前排广告位出租
2013-9-25 02:27
0
雪    币: 371
活跃值: (72)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
25
前排广告位出租
2013-9-25 08:51
0
游客
登录 | 注册 方可回帖
返回
//