Windows Native Debugging Internals

Chromium命令行参数详解:开发调试与性能优化指南 命令行参数是控制软件行为的底层机制,Chromium作为现代浏览器内核,通过双横线(--)前缀的参数提供了丰富的运行时控制能力。这些参数本质上是通过修改程序启动配置来调整功能模块的加载和行为模式,在开发调试、性能优化和自动化测试等场景具有重要价值。以GPU加速参数为例,`--enable-gpu-rasterization`可以强制启用硬件加速渲染,而`--disable-gpu`则用于诊断图形相关问题,这种细粒度的控制能力正是Chromium被广泛应用于Web开发和测试的关键优势。在持续集成、跨平台兼容性 阅读详情
Introduction

In part two of this three part article series, the native interface to Windows debugging is dissected in detail. The reader is expected to have some basic knowledge of C and general NT Kernel architecture and semantics. Also, this is not an introduction on what debugging is or how to write a debugger. It is meant as a reference for experienced debugger writers, or curious security experts.

Native Debugging

Now it's time to look at the native side of things, and how the wrapper layer inside ntdll.dll communicates with the kernel. The advantage of having the DbgUi layer is that it allows better separation between Win32 and the NT Kernel, which has always been a part of NT design. NTDLL and NTOSKRNL are built together, so it's normal for them to have intricate knowledge of each others. They share the same structures, they need to have the same system call IDs, etc. In a perfect world, the NT Kernel should have to know nothing about Win32.

Additionally, it helps anyone that wants to write debugging capabilities inside a native application, or to write a fully-featured native-mode debugger. Without DbgUi, one would have to call the Nt*DebugObject APIs manually, and do some extensive pre/post processing in some cases. DbgUi simplifies all this work to a simple call, and provides a clean interface to do it. If the kernel changes internally, DbgUi will probably stay the same, only its internal code would be modified.

We start our exploration with the function responsible for creating and associating a Debug Object with the current Process. Unlike in the Win32 world, there is a clear distinction between creating a Debug Object, and actually attaching to a process.

NTSTATUS
NTAPI
DbgUiConnectToDbg(VOID)
{
    OBJECT_ATTRIBUTES ObjectAttributes;

    /* Don't connect twice */
    if (NtCurrentTeb()->DbgSsReserved[1]) return STATUS_SUCCESS;

    /* Setup the Attributes */
    InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, 0);

    /* Create the object */
    return ZwCreateDebugObject(&NtCurrentTeb()->DbgSsReserved[1],
                               DEBUG_OBJECT_ALL_ACCESS,
                               &ObjectAttributes,
                               TRUE);
}

As you can see, this is a trivial implementation, but it shows us two things. Firstly, a thread can only have one debug object associated to it, and secondly, the handle to this object is stored in the TEB's DbgSsReserved array field. Recall that in Win32, the first index, [0], is where the Thread Data was stored. We've now learnt that [1] is where the handle is stored.

Now let's see how attaching and detaching are done:

NTSTATUS
NTAPI
DbgUiDebugActiveProcess(IN HANDLE Process)
{
    NTSTATUS Status;

    /* Tell the kernel to start debugging */
    Status = NtDebugActiveProcess(Process, NtCurrentTeb()->DbgSsReserved[1]);
    if (NT_SUCCESS(Status))
    {
        /* Now break-in the process */
        Status = DbgUiIssueRemoteBreakin(Process);
        if (!NT_SUCCESS(Status))
        {
            /* We couldn't break-in, cancel debugging */
            DbgUiStopDebugging(Process);
        }
    }

    /* Return status */
    return Status;
}

NTSTATUS
NTAPI
DbgUiStopDebugging(IN HANDLE Process)
{
    /* Call the kernel to remove the debug object */
    return NtRemoveProcessDebug(Process, NtCurrentTeb()->DbgSsReserved[1]);
}

Again, these are very simple implementations. We can learn, however, that the kernel is not responsible for actually breaking inside the remote process, but that this is done by the native layer. This DbgUiIssueRemoteBreakin API is also used by Win32 when calling DebugBreakProcess, so let's look at it:

NTSTATUS
NTAPI
DbgUiIssueRemoteBreakin(IN HANDLE Process)
{
    HANDLE hThread;
    CLIENT_ID ClientId;
    NTSTATUS Status;

    /* Create the thread that will do the breakin */
    Status = RtlCreateUserThread(Process,
                                 NULL,
                                 FALSE,
                                 0,
                                 0,
                                 PAGE_SIZE,
                                 (PVOID)DbgUiRemoteBreakin,
                                 NULL,
                                 &hThread,
                                 &ClientId);

    /* Close the handle on success */
    if(NT_SUCCESS(Status)) NtClose(hThread);

    /* Return status */
    return Status;
}

All it does is create a remote thread inside the process, and then return to the caller. Does that remote thread do anything magic? Let's see:

VOID
NTAPI
DbgUiRemoteBreakin(VOID)
{
    /* Make sure a debugger is enabled; if so, breakpoint */
    if (NtCurrentPeb()->BeingDebugged) DbgBreakPoint();

    /* Exit the thread */
    RtlExitUserThread(STATUS_SUCCESS);
}

Nothing special at all; the thread makes sure that the process is really being debugged, and then issues a breakpoint. And, because this API is exported, you can call it locally from your own process to issue a debug break (but note that you will kill your own thread). In our look at the Win32 Debugging implementation, we've noticed that the actual debug handle is never used, and that calls always go through DbgUi. Then the NtSetInformationDebugObject system call was called, a special DbgUi API was called before, to actually get the debug object associated with the thread. This API also has a counterpart, so let's see both in action:

HANDLE
NTAPI
DbgUiGetThreadDebugObject(VOID)
{
    /* Just return the handle from the TEB */
    return NtCurrentTeb()->DbgSsReserved[1];
}

VOID
NTAPI
DbgUiSetThreadDebugObject(HANDLE DebugObject)
{
    /* Just set the handle in the TEB */
    NtCurrentTeb()->DbgSsReserved[1] = DebugObject;
}

For those familiar with object-oriented programming, this will seem similar to the concept of accessor and mutator methods. Even though Win32 has perfect access to this handle and could simply read it on its own, the NT developers decided to make DbgUi much like a class, and make sure access to the handle goes through these public methods. This design allows the debug handle to be stored anywhere else if necessary, and only these two APIs will require changes, instead of multiple DLLs in Win32.

Now for a visit of the wait/continue functions, which under Win32 were simply wrappers:

NTSTATUS
NTAPI
DbgUiContinue(IN PCLIENT_ID ClientId,
              IN NTSTATUS ContinueStatus)
{
    /* Tell the kernel object to continue */
    return ZwDebugContinue(NtCurrentTeb()->DbgSsReserved[1],
                           ClientId,
                           ContinueStatus);
}

NTSTATUS
NTAPI
DbgUiWaitStateChange(OUT PDBGUI_WAIT_STATE_CHANGE DbgUiWaitStateCange,
                     IN PLARGE_INTEGER TimeOut OPTIONAL)
{
    /* Tell the kernel to wait */
    return NtWaitForDebugEvent(NtCurrentTeb()->DbgSsReserved[1],
                               TRUE,
                               TimeOut,
                               DbgUiWaitStateCange);
}

Not surprisingly, these functions are also wrappers in DbgUi. However, this is where things start to get interesting, since if you'll recall, DbgUi uses a completely different structure for debug events, called DBGUI_WAIT_STATE_CHANGE. There is one API that we have left to look at, which does the conversion, so first, let's look at the documentation for this structure:

//
// User-Mode Debug State Change Structure
//
typedef struct _DBGUI_WAIT_STATE_CHANGE
{
    DBG_STATE NewState;
    CLIENT_ID AppClientId;
    union
    {
        struct
        {
            HANDLE HandleToThread;
            DBGKM_CREATE_THREAD NewThread;
        } CreateThread;
        struct
        {
            HANDLE HandleToProcess;
            HANDLE HandleToThread;
            DBGKM_CREATE_PROCESS NewProcess;
        } CreateProcessInfo;
        DBGKM_EXIT_THREAD ExitThread;
        DBGKM_EXIT_PROCESS ExitProcess;
        DBGKM_EXCEPTION Exception;
        DBGKM_LOAD_DLL LoadDll;
        DBGKM_UNLOAD_DLL UnloadDll;
    } StateInfo;
} DBGUI_WAIT_STATE_CHANGE, *PDBGUI_WAIT_STATE_CHANGE;

The fields should be pretty self-explanatory, so let's look at the DBG_STATE enumeration:

//
// Debug States
//
typedef enum _DBG_STATE
{
    DbgIdle,
    DbgReplyPending,
    DbgCreateThreadStateChange,
    DbgCreateProcessStateChange,
    DbgExitThreadStateChange,
    DbgExitProcessStateChange,
    DbgExceptionStateChange,
    DbgBreakpointStateChange,
    DbgSingleStepStateChange,
    DbgLoadDllStateChange,
    DbgUnloadDllStateChange
} DBG_STATE, *PDBG_STATE;

If you take a look at the Win32 DEBUG_EVENT structure and associated debug event types, you'll notice some differences which might be useful to you. For starters, Exceptions, Breakpoints and Single Step exceptions are handled differently. In the Win32 world, only two distinctions are made: RIP_EVENT for exceptions, and EXCEPTION_DEBUG_EVENT for a debug event. Although code can later figure out if this was a breakpoint or single step, this information comes directly in the native structure. You will also notice that OUTPUT_DEBUG_STRING event is missing. Here, it's DbgUi that's at a disadvantage, since the information is sent as an Exception, and post-processing is required (which we'll take a look at soon). There are also two more states that Win32 does not support, which is the Idle state and the Reply Pending state. These don't offer much information from the point of view of a debugger, so they are ignored.

Now let's take a look at the actual structures seen in the unions:

//
// Debug Message Structures
//
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;
    PVOID NamePointer;
} DBGKM_LOAD_DLL, *PDBGKM_LOAD_DLL;

typedef struct _DBGKM_UNLOAD_DLL
{
    PVOID BaseAddress;
} DBGKM_UNLOAD_DLL, *PDBGKM_UNLOAD_DLL;

If you're familiar with the DEBUG_EVENT structure, you should notice some subtle differences. First of all, no indication of the process name, which explains why MSDN documents this field being optional and not used by Win32. You will also notice the lack of a pointer to the TEB in the thread structure. Finally, unlike new processes, Win32 does display the name of any new DLL loaded, but this also seems to be missing in the Load DLL structure; we'll see how this and other changes are dealt with soon. As far as extra information goes however, we have the "SubsystemKey" field. Because NT was designed to support multiple subsystems, this field is critical to identifying from which subsystem the new thread or process was created from. Windows 2003 SP1 adds support for debugging POSIX applications, and while I haven't looked at the POSIX debug APIs, I'm convinced they're built around the DbgUi implementation, and that this field is used differently by the POSIX library (much like Win32 ignores it).

Now that we've seen the differences, the final API to look at is DbgUiConvertStateChangeStructure, which is responsible for doing these modifications and fixups:

NTSTATUS
NTAPI
DbgUiConvertStateChangeStructure(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange,
                                 OUT PVOID Win32DebugEvent)
{
    NTSTATUS Status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    THREAD_BASIC_INFORMATION ThreadBasicInfo;
    LPDEBUG_EVENT DebugEvent = Win32DebugEvent;
    HANDLE ThreadHandle;

    /* Write common data */
    DebugEvent->dwProcessId = (DWORD)WaitStateChange->
                                     AppClientId.UniqueProcess;
    DebugEvent->dwThreadId = (DWORD)WaitStateChange->AppClientId.UniqueThread;

    /* Check what kind of even this is */
    switch (WaitStateChange->NewState)
    {
        /* New thread */
        case DbgCreateThreadStateChange:

            /* Setup Win32 code */
            DebugEvent->dwDebugEventCode = CREATE_THREAD_DEBUG_EVENT;

            /* Copy data over */
            DebugEvent->u.CreateThread.hThread =
                WaitStateChange->StateInfo.CreateThread.HandleToThread;
            DebugEvent->u.CreateThread.lpStartAddress =
                WaitStateChange->StateInfo.CreateThread.NewThread.StartAddress;

            /* Query the TEB */
            Status = NtQueryInformationThread(WaitStateChange->StateInfo.
                                              CreateThread.HandleToThread,
                                              ThreadBasicInformation,
                                              &ThreadBasicInfo,
                                              sizeof(ThreadBasicInfo),
                                              NULL);
            if (!NT_SUCCESS(Status))
            {
                /* Failed to get PEB address */
                DebugEvent->u.CreateThread.lpThreadLocalBase = NULL;
            }
            else
            {
                /* Write PEB Address */
                DebugEvent->u.CreateThread.lpThreadLocalBase =
                    ThreadBasicInfo.TebBaseAddress;
            }
            break;

        /* New process */
        case DbgCreateProcessStateChange:

            /* Write Win32 debug code */
            DebugEvent->dwDebugEventCode = CREATE_PROCESS_DEBUG_EVENT;

            /* Copy data over */
            DebugEvent->u.CreateProcessInfo.hProcess =
                WaitStateChange->StateInfo.CreateProcessInfo.HandleToProcess;
            DebugEvent->u.CreateProcessInfo.hThread =
                WaitStateChange->StateInfo.CreateProcessInfo.HandleToThread;
            DebugEvent->u.CreateProcessInfo.hFile =
                WaitStateChange->StateInfo.CreateProcessInfo.NewProcess.
                FileHandle;
            DebugEvent->u.CreateProcessInfo.lpBaseOfImage =
                WaitStateChange->StateInfo.CreateProcessInfo.NewProcess.
                BaseOfImage;
            DebugEvent->u.CreateProcessInfo.dwDebugInfoFileOffset =
                WaitStateChange->StateInfo.CreateProcessInfo.NewProcess.
                DebugInfoFileOffset;
            DebugEvent->u.CreateProcessInfo.nDebugInfoSize =
                WaitStateChange->StateInfo.CreateProcessInfo.NewProcess.
                DebugInfoSize;
            DebugEvent->u.CreateProcessInfo.lpStartAddress =
                WaitStateChange->StateInfo.CreateProcessInfo.NewProcess.
                InitialThread.StartAddress;

            /* Query TEB address */
            Status = NtQueryInformationThread(WaitStateChange->StateInfo.
                                              CreateProcessInfo.HandleToThread,
                                              ThreadBasicInformation,
                                              &ThreadBasicInfo,
                                              sizeof(ThreadBasicInfo),
                                              NULL);
            if (!NT_SUCCESS(Status))
            {
                /* Failed to get PEB address */
                DebugEvent->u.CreateThread.lpThreadLocalBase = NULL;
            }
            else
            {
                /* Write PEB Address */
                DebugEvent->u.CreateThread.lpThreadLocalBase =
                    ThreadBasicInfo.TebBaseAddress;
            }

            /* Clear image name */
            DebugEvent->u.CreateProcessInfo.lpImageName = NULL;
            DebugEvent->u.CreateProcessInfo.fUnicode = TRUE;
            break;

        /* Thread exited */
        case DbgExitThreadStateChange:

            /* Write the Win32 debug code and the exit status */
            DebugEvent->dwDebugEventCode = EXIT_THREAD_DEBUG_EVENT;
            DebugEvent->u.ExitThread.dwExitCode =
                WaitStateChange->StateInfo.ExitThread.ExitStatus;
            break;

        /* Process exited */
        case DbgExitProcessStateChange:

            /* Write the Win32 debug code and the exit status */
            DebugEvent->dwDebugEventCode = EXIT_PROCESS_DEBUG_EVENT;
            DebugEvent->u.ExitProcess.dwExitCode =
                WaitStateChange->StateInfo.ExitProcess.ExitStatus;
            break;

        /* Any sort of exception */
        case DbgExceptionStateChange:
        case DbgBreakpointStateChange:
        case DbgSingleStepStateChange:

            /* Check if this was a debug print */
            if (WaitStateChange->StateInfo.Exception.ExceptionRecord.
                ExceptionCode == DBG_PRINTEXCEPTION_C)
            {
                /* Set the Win32 code */
                DebugEvent->dwDebugEventCode = OUTPUT_DEBUG_STRING_EVENT;

                /* Copy debug string information */
                DebugEvent->u.DebugString.lpDebugStringData =
                    (PVOID)WaitStateChange->
                           StateInfo.Exception.ExceptionRecord.
                           ExceptionInformation[1];
                DebugEvent->u.DebugString.nDebugStringLength =
                    WaitStateChange->StateInfo.Exception.ExceptionRecord.
                    ExceptionInformation[0];
                DebugEvent->u.DebugString.fUnicode = FALSE;
            }
            else if (WaitStateChange->StateInfo.Exception.ExceptionRecord.
                     ExceptionCode == DBG_RIPEXCEPTION)
            {
                /* Set the Win32 code */
                DebugEvent->dwDebugEventCode = RIP_EVENT;

                /* Set exception information */
                DebugEvent->u.RipInfo.dwType =
                    WaitStateChange->StateInfo.Exception.ExceptionRecord.
                    ExceptionInformation[1];
                DebugEvent->u.RipInfo.dwError =
                    WaitStateChange->StateInfo.Exception.ExceptionRecord.
                    ExceptionInformation[0];
            }
            else
            {
                /* Otherwise, this is a debug event, copy info over */
                DebugEvent->dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
                DebugEvent->u.Exception.ExceptionRecord =
                    WaitStateChange->StateInfo.Exception.ExceptionRecord;
                DebugEvent->u.Exception.dwFirstChance =
                    WaitStateChange->StateInfo.Exception.FirstChance;
            }
            break;

        /* DLL Load */
        case DbgLoadDllStateChange :

            /* Set the Win32 debug code */
            DebugEvent->dwDebugEventCode = LOAD_DLL_DEBUG_EVENT;

            /* Copy the rest of the data */
            DebugEvent->u.LoadDll.lpBaseOfDll =
                WaitStateChange->StateInfo.LoadDll.BaseOfDll;
            DebugEvent->u.LoadDll.hFile =
                WaitStateChange->StateInfo.LoadDll.FileHandle;
            DebugEvent->u.LoadDll.dwDebugInfoFileOffset =
                WaitStateChange->StateInfo.LoadDll.DebugInfoFileOffset;
            DebugEvent->u.LoadDll.nDebugInfoSize =
                WaitStateChange->StateInfo.LoadDll.DebugInfoSize;

            /* Open the thread */
            InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
            Status = NtOpenThread(&ThreadHandle,
                                 THREAD_QUERY_INFORMATION,
                                 &ObjectAttributes,
                                 &WaitStateChange->AppClientId);
            if (NT_SUCCESS(Status))
            {
                /* Query thread information */
                Status = NtQueryInformationThread(ThreadHandle,
                                                  ThreadBasicInformation,
                                                  &ThreadBasicInfo,
                                                  sizeof(ThreadBasicInfo),
                                                  NULL);
                NtClose(ThreadHandle);
            }

            /* Check if we got thread information */
            if (NT_SUCCESS(Status))
            {
                /* Save the image name from the TIB */
                DebugEvent->u.LoadDll.lpImageName =
                    &((PTEB)ThreadBasicInfo.TebBaseAddress)->
                    Tib.ArbitraryUserPointer;
            }
            else
            {
                /* Otherwise, no name */
                DebugEvent->u.LoadDll.lpImageName = NULL;
            }

            /* It's Unicode */
            DebugEvent->u.LoadDll.fUnicode = TRUE;
            break;

        /* DLL Unload */
        case DbgUnloadDllStateChange:

            /* Set Win32 code and DLL Base */
            DebugEvent->dwDebugEventCode = UNLOAD_DLL_DEBUG_EVENT;
            DebugEvent->u.UnloadDll.lpBaseOfDll =
                WaitStateChange->StateInfo.UnloadDll.BaseAddress;
            break;

        /* Anything else, fail */
        default: return STATUS_UNSUCCESSFUL;
    }

    /* Return success */
    return STATUS_SUCCESS;
}

Let's take a look at the interesting fixups. First of all, the lack of a TEB pointer is easily fixed by calling NtQueryInformationThread with the ThreadBasicInformation type, which returns, among other things, a pointer to the TEB, which is then saved in the Win32 structure. As for Debug Strings, the API analyzes the exception code and looks for DBG_PRINTEXCEPTION_C, which has a specific exception record that is parsed and converted into a debug string output.

So far so good, but perhaps the nastiest hack is present in the code for DLL loading. Because a loaded DLL doesn't have a structure like EPROCESS or ETHREAD in kernel memory, but in ntdll's private Ldr structures, the only thing that identifies it is a Section Object in memory for its memory mapped file. When the kernel gets a request to create a section for an executable memory mapped file, it saves the name of the file in a field inside the TEB (or TIB, rather) called ArbitraryUserPointer.

This function then knows that a string is located there, and sets it as the pointer for the debug event's lpImageName member. This hack has been in NT every since the first builds, and as far as I know, it's still there in Vista. Could it be that hard to solve?

Once again, we come to an end in our discussion, since there isn't much left in ntdll that deals with the Debug Object. Here's an overview of what was discussed in this part of the series:
  • DbgUi provides a level of separation between the kernel and Win32 or other subsystems. It's written as a fully independent class, even having accessor and mutator methods instead of exposing its handles.
  • The handle to a thread's Debug Object is stored in the second field of the DbgSsReserved array in the TEB.
  • DbgUi allows a thread to have a single DebugObject, but using the native system calls allows you to do as many as you want.
  • Most DbgUi APIs are simple wrappers around the NtXxxDebugObject system calls, and use the TEB handle to communicate.
  • DbgUi is responsible for breaking into the attached process, not the kernel.
  • DbgUi uses its own structure for debug events, which the kernel understands. In some ways, this structure provides more information about some events (such as the subsystem and whether this was a single step or a breakpoint exception), but in others, some information is missing (such as a pointer to the thread's TEB or a separate debug string structure).
  • The TIB (located inside the TEB)'s ArbitraryPointer member contains the name of the loaded DLL during a Debug Event.
 
【信息科学与工程学】【数据中心】第三十五篇 云计算数据中心的学科知识04 编号学科(课程)核心知识点在云计算/云存储/云网络/云安全/云MaaS中的作用代表教材/资料/论文 + 数学方程式列表工业界应用D1421​云原生数据库:TiDB​HTAP(混合事务/分析处理)、分布式SQL、水平扩展、强一致(Raft)、自动故障恢复、与MySQL兼容、TiFlash列式引擎提供弹性扩展的分布式数据库;支撑高并发在线交易与实时分析教材:《TiDB in Action》PingCAP(2020) 论文:《TiDB: A Raft-based HTAP Database》(2017) 阅读详情

相关推荐

从零开始:如何用Python建立你的第一个人工智能模型

在这篇文章中,我们将介绍如何从零开始使用Python建立你的第一个人工智能模型。无论你是刚接触编程的新手,还是有经验的开发者想进一步探索人工智能领域,这篇文章都将为你提供清晰、详细的指南。我们将一步步探索数据预处理、模型建立、训练和测试的过程,以及如何解读模型的结果。

Allen1862105的博客 4万+

DbgUiRemoteBreakin反调试

DbgUiRemoteBreakin是ntdll提供的用于在目标进程中创建远线程后下软件断点的函数 当我们用OD附加调试时,CreateRemoteThread函数在目标程序中创建了一个远程线程,然后在远程线程中调用DbgUiRemoteBreakin函数,DbgUiRemoteBreakin内部调用了DbgBreakPoint函数,DbgBreakPoint函数内部下了一个int 3断点,触发异常让操作系统运行异常处理程序,然后操作系统把控制权交管给调试器 ps:DbgUiIssueRemoteBreak

sanqiuai的博客 2615

Thor芯片部署

英伟达Jetson Thor芯片性能分析与应用现状 摘要:英伟达最新发布的Jetson Thor芯片基于Blackwell架构,官方宣称AI算力达2070TFLOPS(FP4),较前代Orin提升7.5倍,配备14核Arm CPU和128GB内存。然而实际量产版算力缩水至700TOPS,且因4nm制程良率问题多次延期至2025-2026年交付。该芯片支持FP4/FP8量化、Transformer引擎等先进特性,适用于自动驾驶、机器人等边缘AI场景,但受供应链限制,国内厂商倾向自研方案。开发者可通过JetPa

xiaomu_347的博客 435

调试器的原理

软件断点 int3指令 机器码为1字节,即0xCC 没有数量限制 局限性 属于代码类断点,即可以让CPU执行到代码段内的某个地址时停下来,不适用于数据段和I/O空间 对于在ROM(只读存储器)中执行的程序(比如BIOS或者其他固件程序),无法动态增加软件断点。因为目标内存是只读的无法动态写入断点指令。这时就要使用我们后面要介绍的硬件断点。 实验 是用windbg给MessageBoxW下断点,windbg只有在程序运行的时候才会把断点的地方改为int3 0:011> u user32!Mess

南宫封清的博客 1172

prplOS适配USB LAN网卡

本文介绍了将USB LAN网卡(如rndis0)适配到prplOS的LAN管理流程。

四儿家的小祖宗的博客 322

SAP Joule Studio 能不能用来开发 skill:可以,但它开发的是 Joule 语境下的企业级 skill,而不是替代所有 SAP 开发技术的万能锤

SAP Joule Studio 专为开发Joule技能(Joule skills)而设计,这些技能是执行确定性单步业务动作的原子能力。它通过自然语言接口连接SAP和非SAP系统,适合处理边界清晰、可审计的任务(如创建工单、查询发票)。Joule技能作为会话式业务编排层,与ABAP、CAP等核心技术互补而非替代。开发支持低代码和专业代码两种方式,并具备企业级治理能力,但需要相应的BTP订阅和技术基础。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 202

vs附加到进程调试不了_如何调试xxProtect

0x1:今天终于拿到了萌妹以求的xxprotect的demo,好久没搞反调试这一块了。心痒痒,开搞。0x2: 打开demo,长这样。是个64的程序,直接上x64dbg ,发现竟然找不到进程.尝试直接OpenProcess,报错 STATUS_INVALID_CID。根据msdn描述,很清楚了。做个实验测试一下。系统根据cid拿不到eprocess信息,看看PsLookupProcessByProc...

weixin_39527879的博客 1223

S/4HANA Private Cloud 与 On-Premise 的 Key User Custom Fields 运输与升级避坑指南

本文探讨了SAP S/4HANA Cloud Private Edition和On-Premise系统中Key User Custom Fields的管理要点。文章指出,Custom Fields涉及元数据生成、对象一致性、传输治理等完整链路,操作不当会导致字段丢失或升级失败。关键建议包括:使用ATO Fiori App管理扩展项与传输绑定,配置正确的job user权限,避免在多系统并行开发同一字段。特别强调升级时需执行CFD_REGENERATE_IN_ONPREMISE报表,并通过标准传输机制推进变更

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 196

初探Windows用户态调试机制

我们在感叹Onlydbg强大与便利的同时,是否考虑过它实现的原理呢? 作为一个技术人员知其然必知其所以然,这才是我们追求的本心。 最近在学习张银奎老师的《软件调试》,获益良多。熟悉Windows调试机制,对我们深入理解操作系统以及游戏保护的原理有着莫大好处。 0X01 初探调试原理 调试系统的实现思路如图所示: 调试器与被调试程序建立联系,程序像调试器发送调试信息...

weixin_30587927的博客 228

AgentScope 2.0 源码解析-工具系统解读:从内置工具到权限管控的完整实践

本文深入解析AgentScope 2.0工具系统的完整架构,覆盖内置工具集(文件读写、命令执行、搜索匹配)、MCP协议集成(STDIO/HTTP双传输、客户端封装、工作空间网关)、工具包管理与动态分组、权限引擎的多级决策机制(危险路径保护、只读识别、模式控制),以及自定义工具开发流程。通过分层架构图和时序图直观展示组件协作关系,结合安全最佳实践与故障排除指南,为开发者提供从工具使用到安全管控的全链路参考。

program哲学 574

K8S搭建(centos)完整版

每个人都有惰性,但不断学习是好好生活的根本,共勉!

感谢阅读 4150

工欲善其事,必先利其器之—react-native-debugger调试react native应用

记录与分享 react native debug工具的使用

技术的学习与积累是个技术活 3569

FISCO BCOS(八)——— 一键部署 WeBase

环境 版本 Java Oracle JDK 8 至 14 MySQL MySQL-5.6及以上 Python Python3.6及以上 PyMySQL 1、获取部署安装包: wget https://osp-1257653870.cos.ap-guangzhou.myqcloud.com/WeBASE/releases/download/v1.5.4/webase-deploy.zip 2、解压安装包: unzip webase-deploy.zip 3、进入目录: c...

林中有神君 3608

radare2 使用 winkd 调试插件通过 KD 协议调试 Windows 内核

本文介绍 radare2(r2)内建的 **WinDBG / KD 调试支持**:通过 `winkd` IO 插件与调试插件,radare2 可以经由虚拟串口(命名管道 / Unix socket)附着到运行在 VirtualBox、QEMU 等虚拟机中的 Windows 系统,使用微软内核调试器(KD)协议远程读写内核内存、控制执行流、设置断点并枚举进程与模块。阅读本文后,你将掌握从 Windo

gitblog_00418的博客 76

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Thorium浏览器终极指南:基于Chromium的高性能隐私优化浏览器深度解析

Thorium是一款基于Chromium源代码深度优化的高性能浏览器,以放射性元素钍(Thorium)命名,致力于在保持Chromium核心稳定性的同时,通过编译器优化和隐私增强功能提供卓越的性能体验。作为开源浏览器生态中的佼佼者,Thorium不仅继承了Chromium的所有优秀特性,更在性能、隐私保护和用户体验方面做出了突破性改进。对于追求极致速度的技术爱好者和需要隐私保护环境的开发者来说,T

gitblog_00185的博客 1099

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

bgfx 跨平台渲染库完全指南:API 无关的 “Bring Your Own Engine“ 渲染中间件

bgfx 是一个跨平台、与具体图形 API 无关(graphics API agnostic)的渲染库,采用 "Bring Your Own Engine/Framework"(自带引擎/框架)的集成哲学:它只负责渲染,把引擎、场景管理、窗口、输入等全部留给使用者。本指南以 [README.md](https://link.gitcode.com/i/05ae4ed224099bde41c4333

gitblog_00817的博客 426

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Deno Node.js 兼容测试实战指南:运行、诊断、修复与配置 test-compat 测试

本文以 Deno 仓库中的 Node.js 兼容测试技能文档(`.claude/skills/node-compat/SKILL.md`)为核心,系统讲解 `./x test-compat` 测试的完整工作流:如何构建并运行单个测试、如何定位失败原因、如何把失败归类为「可修复缺陷 / 本质不兼容 / 不值得修复」三类,以及如何通过 `tests/node_compat/config.jsonc`

gitblog_00129的博客 414

Detox REPL 交互式调试指南:在测试运行中实时排查与修复失败用例

Detox REPL 是 Detox 内置的交互式调试环境,它基于 Node.js 的 Read-Eval-Print Loop(读取-求值-输出循环)实现,让你可以在测试执行中途暂停,手动逐条运行 Detox 命令、查看视图层级 XML、甚至用自然语言驱动 Detox Pilot,从而在真实设备/模拟器的当前状态上定位和修复问题。读完本文,你将掌握 `--repl` 与 `--repl=auto

gitblog_01009的博客 135

Tauri 2.x 系列(十):测试、日志、调试与性能——让桌面应用可验证、可观测

用户看到什么;API 返回什么稳定错误码;日志是否包含 trace ID、组件、阶段和耗时;是否创建残留进程、端口或临时文件;是否可以重试;重启应用后数据是否仍一致。只断言 HTTP 500 不足以证明故障处理正确。fill:#333;important;important;fill:none;fill:#333;stroke:red;important;important;important;important;important;important;fill:red;

QAZ600888的博客 104

代码 阅读器:SI (Source Insight)、vscode(Visual Studio Code)、code-server、Visual Studio、OpenGrok

代码 阅读器:IDE、SI (Source Insight)、OpenGrok

freeking101的博客 7550

windows下的用户态调试的底层与上层实现

操作系统:windows XP   调试器通过CreateProcess传入带有DEBUG_PROCESS和DEBUG_ONLY_THIS_PROCESS的dwCreationFlags创建被调试进程。这种情况下,进程创建的早期(执行NtCreateProcess或NtCreateProcessEx之前),调用DbgUiConnectToDbg()使调用线程和调试子系统建立连接。DbgUiCo...

weixin_33910759的博客 234

差点吓尿,手贱不要乱点support native debug

在群里有人聊起可以通过NDK的插件,支持对.SO库的DEBUG调试,我一想这不吊炸天么,赶紧去试于是到了 http://trac.pjsip.org/repos/wiki/Getting-Started/Android#Requirements 这个页面,愁了半天,发现 http://tools.android.com/recent/usingthendkplugin 才是关键,...

aoshunjian2584的博客 175

聊聊游戏辅助那些事上部第一篇——过掉那些讨厌的游戏保护。

 这个系列计划分为上下两部,上部是内置型辅助,下部是按键型辅助。我不保证会写完。 聊聊游戏辅助那些事上部第一篇——过掉那些讨厌的游戏保护。 相对以前那些裸奔游戏,现在的新游戏多少都会有些保护,这是绕不过去的坎。 下面针对我发现的几个保护方法,逐个的聊一聊。 1、R3应用层下,DebugActiveProcess 加载调试器时,会设置一个远程断点,而这个远程断点其实多此一举,反而有时会

YoungBoz的专栏 5196

简单绕过 DbgUiRemoteBreakin 反调试

调试器附加到游戏下断点后,会出现以下提示: (OllyDbg 和 x64dbg 的现象应该是游戏闪退) 第一时间想到的就应该是游戏对有关下断的API进行了HOOK,DbgUiRemoteBreakin这个函数内部会调用DbgBreakPoint执行断点指令,以触发断点异常,强制把程序断了下来: 我们用CE查看这个函数的头部,发现他跳转到了游戏检测模块的地址: 来到这个后我们发现他调用了Exi...

LYSM 5902

用户态调试过程

在了解了用户态调试模型之后,根据模型来理解用户态调试过程。

pokeyode的专栏 1076

深层解析最核心的dll:NTDLL.dll

打开NTDLL.dll,惊奇的发现原来CRT的许多基本函数居然都是在这里实现的!甚至包括qsort,ceil这样的函数,还有臭名昭著的 strcpy(严格来讲,这只能怪使用者不当心)。堆的释放,进城管理,似乎都是在这。于是,我决定,仔细察看以下它,这1410个函数是做什么的.

Tommy(凌飞)的专栏 6754
上一篇: 逆向未知dhook.sys驱动源代码
下一篇: Windows User Mode Debugging Internals
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值