忆杰的博客

忆杰的博客

DbgkCreateThread

首先我们需要替换的是 rdmsr, wrmsr替换掉系统的sysenter跳转地址. 这样整个SSDT表函数都处于被我们的监控当中.

一个新的进程创建线程的时候就会调用到DbgkCreateThread.DbgkCreateThread可以发出两种消息, 一种进程创建,和线程创建消息.

当然, ntdll.dll的消息也在此列.DbgkCreateThread函数内部主要是判断进程是否有PSF_CREATE_REPORTED_BIT标记, 如果有那么就发送进程创建消息, 如果没有那么就发送线程创建消息, 他们都会调用到 DbgkpSendApiMessage 函数.

DbgkpSendApiMessage函数内部做的事情也不多. 主要是挂起线程和恢复线程一类的事情. 然后调用了更底层的DbgkpQueueMessage函数.

DbgkpQueueMessage函数内部大体逻辑就是将消息封装好, 插入队列, 等待R3过来取. 差不多就是这些.下面的代码是ReactOS的代码.

VOID
NTAPI
DbgkCreateThread(IN PETHREAD Thread,
                 IN PVOID StartAddress)
{
    PEPROCESS Process = PsGetCurrentProcess();
    ULONG ProcessFlags;
    IMAGE_INFO ImageInfo;
    PIMAGE_NT_HEADERS NtHeader;
    POBJECT_NAME_INFORMATION ModuleName;
    UNICODE_STRING NtDllName;
    NTSTATUS Status;
    PVOID DebugPort;
    DBGKM_MSG ApiMessage;
    PDBGKM_CREATE_THREAD CreateThread = &ApiMessage.CreateThread;
    PDBGKM_CREATE_PROCESS CreateProcess = &ApiMessage.CreateProcess;
    PDBGKM_LOAD_DLL LoadDll = &ApiMessage.LoadDll;
    OBJECT_ATTRIBUTES ObjectAttributes;
    IO_STATUS_BLOCK IoStatusBlock;
    PTEB Teb;
    PAGED_CODE();

    /* Sanity check */
    ASSERT(Thread == PsGetCurrentThread());

	//
   	// 获取是否要调用LoadImageNotifyRoutines标记
   	//
    ProcessFlags = PspSetProcessFlag(Process,
                                     PSF_CREATE_REPORTED_BIT |
                                     PSF_IMAGE_NOTIFY_DONE_BIT);

	//
    // 如果进程标记中有设置PSF_IMAGE_NOTIFY_DONE_BIT 并且系统中开启了LoadImageNotifyRoutines服务
    // 这个if 和调试没有关系, 不管. 
    //
    if (!(ProcessFlags & PSF_IMAGE_NOTIFY_DONE_BIT) && (PsImageNotifyEnabled))
    {
        /* It hasn't.. set up the image info for the process */
        ImageInfo.Properties = 0;
        ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
        ImageInfo.ImageBase = Process->SectionBaseAddress;
        ImageInfo.ImageSize = 0;
        ImageInfo.ImageSelector = 0;
        ImageInfo.ImageSectionNumber = 0;

        /* Get the NT Headers */
        NtHeader = RtlImageNtHeader(Process->SectionBaseAddress);
        if (NtHeader)
        {
            /* Set image size */
            ImageInfo.ImageSize = NtHeader->OptionalHeader.SizeOfImage;
        }

        /* Get the image name */
        Status = MmGetFileNameForSection(Process->SectionObject, &ModuleName);
        if (NT_SUCCESS(Status))
        {
            /* Call the notify routines and free the name */
            PspRunLoadImageNotifyRoutines(&ModuleName->Name,
                                          Process->UniqueProcessId,
                                          &ImageInfo);
            ExFreePool(ModuleName);
        }
        else
        {
            /* Call the notify routines */
            PspRunLoadImageNotifyRoutines(NULL,
                                          Process->UniqueProcessId,
                                          &ImageInfo);
        }

        /* Setup the info for ntdll.dll */
        ImageInfo.Properties = 0;
        ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
        ImageInfo.ImageBase = PspSystemDllBase;
        ImageInfo.ImageSize = 0;
        ImageInfo.ImageSelector = 0;
        ImageInfo.ImageSectionNumber = 0;

        /* Get the NT Headers */
        NtHeader = RtlImageNtHeader(PspSystemDllBase);
        if (NtHeader)
        {
            /* Set image size */
            ImageInfo.ImageSize = NtHeader->OptionalHeader.SizeOfImage;
        }

        /* Call the notify routines */
        RtlInitUnicodeString(&NtDllName,
                             L"\\SystemRoot\\System32\\ntdll.dll");
        PspRunLoadImageNotifyRoutines(&NtDllName,
                                      Process->UniqueProcessId,
                                      &ImageInfo);
    }

	//
   	// 这里才真的关于调试部分的代码
   	//
    DebugPort = Process->DebugPort;
    if (!DebugPort) return;

	//
   	// 如果调试设置了创建进程通知消息
   	//
    if (!(ProcessFlags & PSF_CREATE_REPORTED_BIT))
    {
    	//
        // CreateProcess 是R0这边传递调试消息的主要结构. 既然是创建进程
        // 那么线程当然都是空了
        //
        CreateProcess->InitialThread.SubSystemKey = 0;
        CreateProcess->InitialThread.StartAddress = NULL;

		//
       	// 填写新进程的句柄
       	//
        CreateProcess->SubSystemKey = 0;
        CreateProcess->FileHandle = DbgkpSectionToFileHandle(Process->
                                                             SectionObject);
      	//
      	// 其他一些信息
      	//
        CreateProcess->BaseOfImage = Process->SectionBaseAddress;
        CreateProcess->DebugInfoFileOffset = 0;
        CreateProcess->DebugInfoSize = 0;

		//
       	// 这些信息其实也不重要, 程序入口, 符号表的地址和数量
       	//
        NtHeader = RtlImageNtHeader(Process->SectionBaseAddress);
        if (NtHeader)
        {
        	//
            // 根据PE信息获取入口
            //
            CreateProcess->InitialThread.StartAddress =
                (PVOID)((ULONG_PTR)NtHeader->OptionalHeader.ImageBase +
                        NtHeader->OptionalHeader.AddressOfEntryPoint);

            //
            // 获取调试信息表地址
            //
            CreateProcess->DebugInfoFileOffset = NtHeader->FileHeader.
                                                 PointerToSymbolTable;

            //                                   
            // 获取调试信息的数量
            //
            CreateProcess->DebugInfoSize = NtHeader->FileHeader.
                                           NumberOfSymbols;
        }

        // 这边开始设置
        ApiMessage.h.u1.Length = sizeof(DBGKM_MSG) << 16 |
                                 (8 + sizeof(DBGKM_CREATE_PROCESS));
        ApiMessage.h.u2.ZeroInit = 0;
        ApiMessage.h.u2.s2.Type = LPC_DEBUG_EVENT;
        ApiMessage.ApiNumber = DbgKmCreateProcessApi;

		//
        // 这个函数是内核里面分发调试消息的主要函数了, 后面一个参数表示
        // 是否要冻结进程的所有线程
        //
        DbgkpSendApiMessage(&ApiMessage, FALSE);

		//
       	// 内核这边其实会给R3关闭句柄, 所以R3那边其实不用操心的
       	//
        ObCloseHandle(CreateProcess->FileHandle, KernelMode);

		//
        // 我们看到, 其实创建进程的时候, 默认就会发送nt.dll的LoadDll消息
        //
        LoadDll->BaseOfDll = PspSystemDllBase;
        LoadDll->DebugInfoFileOffset = 0;
        LoadDll->DebugInfoSize = 0;
        LoadDll->NamePointer = NULL;

		//
        // 获取nt.dll的调试符号偏移和大小. PspSystemDllBase真神奇
        //
        NtHeader = RtlImageNtHeader(PspSystemDllBase);
        if (NtHeader)
        {
            /* Fill out debug information */
            LoadDll->DebugInfoFileOffset = NtHeader->
                                           FileHeader.PointerToSymbolTable;
            LoadDll->DebugInfoSize = NtHeader->FileHeader.NumberOfSymbols;
        }

		//
       	// 保存一点nt.dll的信息
       	//
        Teb = Thread->Tcb.Teb;
        if (Teb)
        {
            /* Copy the system library name and link to it */
            wcsncpy(Teb->StaticUnicodeBuffer,
                    L"ntdll.dll",
                    sizeof(Teb->StaticUnicodeBuffer) / sizeof(WCHAR));

            Teb->NtTib.ArbitraryUserPointer = Teb->StaticUnicodeBuffer;

            /* Return it in the debug event as well */
            LoadDll->NamePointer = &Teb->NtTib.ArbitraryUserPointer;
        }

		//
        // 获取nt.dll的句柄
        //
        InitializeObjectAttributes(&ObjectAttributes,
                                   &PsNtDllPathName,
                                   OBJ_CASE_INSENSITIVE |
                                   OBJ_KERNEL_HANDLE |
                                   OBJ_FORCE_ACCESS_CHECK,
                                   NULL,
                                   NULL);

        Status = ZwOpenFile(&LoadDll->FileHandle,
                            GENERIC_READ | SYNCHRONIZE,
                            &ObjectAttributes,
                            &IoStatusBlock,
                            FILE_SHARE_DELETE |
                            FILE_SHARE_READ |
                            FILE_SHARE_WRITE,
                            FILE_SYNCHRONOUS_IO_NONALERT);
        if (NT_SUCCESS(Status))
        {
        	//
           	// 句柄也有了. 有酒有肉了. 可以发送DLL加载消息了
           	//
            ApiMessage.h.u1.Length = sizeof(DBGKM_MSG) << 16 |
                                     (8 + sizeof(DBGKM_LOAD_DLL));
            ApiMessage.h.u2.ZeroInit = 0;
            ApiMessage.h.u2.s2.Type = LPC_DEBUG_EVENT;
            ApiMessage.ApiNumber = DbgKmLoadDllApi;

            /* Send the message */
            DbgkpSendApiMessage(&ApiMessage, TRUE);

			//
            // 同样的, R3不需要关闭句柄, 以前真是纠结啊. 
            //
            ObCloseHandle(LoadDll->FileHandle, KernelMode);
        }
    }
    else
    {
    	//
        // 如果不是首次创建进程, 那么也好办, 直接发送线程创建消息就可以了
        //
        CreateThread->SubSystemKey = 0;
        CreateThread->StartAddress = StartAddress;

        /* Setup the API Message */
        ApiMessage.h.u1.Length = sizeof(DBGKM_MSG) << 16 |
                                 (8 + sizeof(DBGKM_CREATE_THREAD));
        ApiMessage.h.u2.ZeroInit = 0;
        ApiMessage.h.u2.s2.Type = LPC_DEBUG_EVENT;
        ApiMessage.ApiNumber = DbgKmCreateThreadApi;

        /* Send the message */
        DbgkpSendApiMessage(&ApiMessage, TRUE);
    }
}

NTSTATUS
NTAPI
DbgkpSendApiMessage(IN OUT PDBGKM_MSG ApiMsg,
                    IN BOOLEAN SuspendProcess)
{
    NTSTATUS Status;
    BOOLEAN Suspended = FALSE;
    PAGED_CODE();

   	// 看看是否需要挂起整个线程. 
    if (SuspendProcess)
    {
    	Suspended = DbgkpSuspendProcess();
    }

	//
    // 将调试包设置成忙状态
    //
    ApiMsg->ReturnedStatus = STATUS_PENDING;

	//
    // 设置进程的PSF_CREATE_REPORTED_BIT位
    //
    PspSetProcessFlag(PsGetCurrentProcess(), PSF_CREATE_REPORTED_BIT);

	//
    // 将调试消息插入队列中
    //
    Status = DbgkpQueueMessage(PsGetCurrentProcess(),
                               PsGetCurrentThread(),
                               ApiMsg,
                               0,
                               NULL);

	//
    // 刷新指令缓存
    //
    ZwFlushInstructionCache(NtCurrentProcess(), NULL, 0);

	//
    // 如果挂起了线程, 那么还需要重新恢复线程运行
    //
    if (Suspended)
    {
    	DbgkpResumeProcess();
    }
    return Status;
}

NTSTATUS
NTAPI
DbgkpQueueMessage(IN PEPROCESS Process,
                  IN PETHREAD Thread,
                  IN PDBGKM_MSG Message,
                  IN ULONG Flags,
                  IN PDEBUG_OBJECT TargetObject OPTIONAL)
{
    PDEBUG_EVENT DebugEvent;
    DEBUG_EVENT LocalDebugEvent;
    PDEBUG_OBJECT DebugObject;
    NTSTATUS Status;
    BOOLEAN NewEvent;
    PAGED_CODE();

    //
    // 检测是否需要分配调试事件句柄, 根据调试, 从来就没有进来过. 忽略
    //
    NewEvent = (Flags & DEBUG_EVENT_NOWAIT) ? TRUE : FALSE;
    if (NewEvent)
    {
        /* Allocate it */
        DebugEvent = ExAllocatePoolWithTag(NonPagedPool,
                                           sizeof(DEBUG_EVENT),
                                           'EgbD');
        if (!DebugEvent) return STATUS_INSUFFICIENT_RESOURCES;

        /* Set flags */
        DebugEvent->Flags = Flags | DEBUG_EVENT_INACTIVE;

        /* Reference the thread and process */
        ObReferenceObject(Thread);
        ObReferenceObject(Process);

        /* Set the current thread */
        DebugEvent->BackoutThread = PsGetCurrentThread();

        /* Set the debug object */
        DebugObject = TargetObject;
    }
    else
    {
        /* Use the debug event on the stack */
        DebugEvent = &LocalDebugEvent;
        DebugEvent->Flags = Flags;

		//
        // 获取调试消息发送自旋锁
        //
        ExAcquireFastMutex(&DbgkpProcessDebugPortMutex);

       	// 调试对象
        DebugObject = Process->DebugPort;

		//
       	// 这里检测是否要跳过某些消息, 显然是没有什么用的.
       	//
        switch (Message->ApiNumber)
        {
            /* Process or thread creation */
            case DbgKmCreateThreadApi:
            case DbgKmCreateProcessApi:

                /* Make sure we're not skipping creation messages */
                if (Thread->SkipCreationMsg) DebugObject = NULL;
                break;

            /* Process or thread exit */
            case DbgKmExitThreadApi:
            case DbgKmExitProcessApi:

                /* Make sure we're not skipping exit messages */
                if (Thread->SkipTerminationMsg) DebugObject = NULL;

            /* No special handling for other messages */
            default:
                break;
        }
    }

	//
    // 初始化调试信息发送完成事件
    //
    KeInitializeEvent(&DebugEvent->ContinueEvent, SynchronizationEvent, FALSE);
    DebugEvent->Process = Process;
    DebugEvent->Thread = Thread;
    DebugEvent->ApiMsg = *Message;
    DebugEvent->ClientId = Thread->Cid;

	//
    // 检测调试端口是否正常
    //
    if (!DebugObject)
    {
        /* Fail */
        Status = STATUS_PORT_NOT_SET;
    }
    else
    {
    	//
        // 这是进程内调试对象获取的自旋锁
        //
        ExAcquireFastMutex(&DebugObject->Mutex);

		//
        // 检测调试器目前是否激活
        //
        if (!DebugObject->DebuggerInactive)
        {
        	//
           	// 插入进程的调试端口事件链表
           	//
            InsertTailList(&DebugObject->EventList, &DebugEvent->EventList);

			//
            // 事件置位, 这是干什么用的事件呢?
            //
            if (!NewEvent)
            {
                /* Signal it */
                KeSetEvent(&DebugObject->EventsPresent,
                           IO_NO_INCREMENT,
                           FALSE);
            }

            /* Set success */
            Status = STATUS_SUCCESS;
        }
        else
        {
        	//
            // 没有调试
            //
            Status = STATUS_DEBUGGER_INACTIVE;
        }

        /* Release the object lock */
        ExReleaseFastMutex(&DebugObject->Mutex);
    }

    /* Check if we had acquired the port lock */
    if (!NewEvent)
    {
    	//
        // 这里释放快速互斥锁
        //
        ExReleaseFastMutex(&DbgkpProcessDebugPortMutex);

        /* Check if we got here through success */
        if (NT_SUCCESS(Status))
        {
        	//
           	// 等待R3那边过来取消息
           	//
            KeWaitForSingleObject(&DebugEvent->ContinueEvent,
                                  Executive,
                                  KernelMode,
                                  FALSE,
                                  NULL);

            /* Copy API Message back */
            *Message = DebugEvent->ApiMsg;

            /* Set return status */
            Status = DebugEvent->Status;
        }
    }
    else
    {
        /* Check if we failed */
        if (!NT_SUCCESS(Status))
        {
            /* Dereference the process and thread */
            ObDereferenceObject(Thread);
            ObDereferenceObject(Process);

            /* Free the debug event */
            ExFreePool(DebugEvent);
        }
    }

    return Status;
}

发表评论


Warning: Undefined variable $user_ID in /www/wwwroot/joenchen.com/wp-content/themes/agan/comments.php on line 66

您必须登录 才能进行评论。