Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >10.0 探索API调试事件原理

10.0 探索API调试事件原理

作者头像
王瑞MVP
发布于 2023-10-11 07:43:36
发布于 2023-10-11 07:43:36
18000
代码可运行
举报
运行总次数:0
代码可运行

本章笔者将通过Windows平台下自带的调试API接口实现对特定进程的动态转存功能,首先简单介绍一下关于调试事件的相关信息,调试事件的建立需要依赖于DEBUG_EVENT这个特有的数据结构,该结构用于向调试器报告调试事件。当一个程序发生异常事件或者被调试器附加时,就会产生对应的DEBUG_EVENT调试事件,通常DEBUG_EVENT包含了多种调试类型,包括异常事件、进程创建事件、线程创建事件、进程退出事件和线程退出事件等等,我们只需要动态捕捉这些调试事件并作相应的处理即可实现更多有用的功能。

调试事件通常可以分为如下几种类型;

  • 异常事件 (Exception Event) - 发生了异常,例如访问非法的内存、除以零或调用了无效的函数。
  • 进程创建事件 (Process Creation Event) - 当一个新进程被创建时发送此事件。
  • 进程退出事件 (Process Exit Event) - 当一个进程退出时发送此事件。
  • 线程创建事件 (Thread Creation Event) - 当一个新线程被创建时发送此事件。
  • 线程退出事件 (Thread Exit Event) - 当一个线程退出时发送此事件。
  • 调试字符串事件 (Debug String Event) - 当一个进程向其调试器发送字符串消息时发送此事件。
  • 输出字符串事件 (Output String Event) - 当输出调试字符串时发送此事件。
  • 动态链接库加载事件(LOAD_DLL_DEBUG_EVENT) - 当进程装载 DLL 时发送此事件。

当我们需要调试一个程序时有两种方式可以实现,第一种方式是通过CreateProcess()函数创建一个进程,并在调用函数时指定DEBUG_PROCESS || DEBUG_ONLY_THIS_PROCESS则当程序被运行起来后自动进入到调试状态,另一种方式则是通过DebugActiveProcess()函数,该函数接受一个正在运行的进程PID号,可动态附加到一个已运行程序上而对其进行调试。

一旦调试器通过CreateProcess()附加并运行,下一步则是通过WaitForDebugEvent()用于等待一个调试事件,当有调试事件到达后系统会将调试类型存储到debugEvent.dwDebugEventCode这个变量内,此时我们可以通过判断该变量内的参数来对特定的事件做出自定义处理操作,接着会通过ContinueDebugEvent()继续等待下一个调试事件的到来,我们以打开一个进程并创建调试为例,看一下如下代码片段;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>
#include <windows.h>

int main(int argc, char* argv[])
{
    DEBUG_EVENT debugEvent = { 0 };
    BOOL bRet = TRUE;

    // 创建调试进程
    STARTUPINFO startupInfo = { 0 };
    PROCESS_INFORMATION pInfo = { 0 };
    GetStartupInfo(&startupInfo);

    // 创建调试进程并设置 DEBUG_PROCESS || DEBUG_ONLY_THIS_PROCESS 调试事件
    bRet = CreateProcess("d://lyshark.exe", NULL, NULL, NULL, TRUE, DEBUG_PROCESS || DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &startupInfo, &pInfo);
    if (!bRet)
    {
        return 0;
    }

    // 附加调试进程
    // DebugActiveProcess(13940)

    // 无限循环等待调试事件
    while (WaitForDebugEvent(&debugEvent, INFINITE))
    {
        // 根据调试事件判断
        switch (debugEvent.dwDebugEventCode)
        {
            // 异常调试事件
        case EXCEPTION_DEBUG_EVENT:
            printf("异常处理事件 \n");
            break;

            // 线程创建调试事件
        case CREATE_THREAD_DEBUG_EVENT:
            printf("线程创建调试事件 \n");
            break;
            // 进程创建调试事件
        case CREATE_PROCESS_DEBUG_EVENT:
            printf("进程创建调试事件 \n");
            break;
            // 线程退出调试事件
        case EXIT_THREAD_DEBUG_EVENT:
            printf("线程退出调试事件 \n");
            break;
            // 进程退出调试事件
        case EXIT_PROCESS_DEBUG_EVENT:
            printf("进程退出调试事件 \n");
            break;
            // 装载DLL调试事件
        case LOAD_DLL_DEBUG_EVENT:
            printf("装载DLL调试事件 \n");
            break;
            // 卸载DLL调试事件
        case UNLOAD_DLL_DEBUG_EVENT:
            printf("卸载DLL调试事件 \n");
            break;
            // 输出调试信息事件
        case OUTPUT_DEBUG_STRING_EVENT:
            printf("输出调试信息事件 \n");
            break;
        }

        // 使调试器能够继续以前报告调试事件的线程
        bRet = ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);
    }

    system("pause");
    return 0;
}

当编译并运行上述程序后,读者应该能看到如下图所示的输出效果,其中包括了各类调试事件被触发时的提示信息,由于在调试事件内没有做任何操作,程序在加载后就被自动运行起来了;

本文作者: 王瑞 本文链接: https://www.lyshark.com/post/b8eecce4.html 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-10-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Windows环境下的调试器探究
1.CPU检测到INT 3指令 2.查IDT表找到对应的函数 3.CommonDispatchException 4.KiDispatchException 5.DbgkForwardException收集并发送调试事件
红队蓝军
2022/04/27
7740
Windows环境下的调试器探究
C/C++ 内存转储与获取DLL加载
CREATE_PROCESS_DEBUG_EVENT 创建进程的调试事件。CREATE_PROCESS_DEBUG_INFO结构体描述了该类调试事件的详细信息 OUTPUT_DEBUG_STRING_EVENT 该事件,当被调试进程调用OutputDebugString时就会引发该类调试事件,OUTPUT_DEBUG_STRING_INFO结构体描述了关于该事件的详细信息 LOAD_DLL_DEBUG_EVENT 当DLL被加载时,会调用该回调,LOAD_DLL_DEBUG_INFO结构体描述了它的详细信息,dll的路径被放在了,hfile字段,该字段默认是句柄方式存储的,需要手工转换,
王瑞MVP
2022/12/28
5540
软件调试详解
在windows里面调试跟异常息息相关,如果想要对调试得心应手,异常处理的知识是必不可少的,本文主要介绍的是软件调试方面的有关知识,讲解调试程序和被调试程序之间如何建立联系
红队蓝军
2022/05/08
5960
软件调试详解
反调试——9——调试器原理
无论是通过打开进程还是附加进程来实现调试,都只是开始调用的方式不一样,在调试器和操作系统之间的交互方式都是相同的。
冬夜先生
2021/10/20
9710
调试器编写第一讲,调试器基本框架
IBinary
2018/01/08
1.8K0
调试器编写第一讲,调试器基本框架
自由控制程序运行方式的编程技巧
本篇继续阅读学习《有趣的二进制:软件安全与逆向分析》,本章是自由控制程序运行方式的编程技巧,主要介绍调试器的原理、代码注入和API钩子
红客突击队
2022/09/29
6870
自由控制程序运行方式的编程技巧
原创Paper | TinyInst 的插桩实现原理分析
TinyInst 是一个基于调试器原理的轻量级动态检测库,由 Google Project Zero 团队开源,支持 Windows、macOS、Linux 和 Android 平台。同 DynamoRIO、PIN 工具类似,解决二进制程序动态检测的需求,不过相比于前两者 TinyInst 更加轻量级,更加便于用户理解,更加便于程序员进行二次开发。
Seebug漏洞平台
2023/10/24
7030
原创Paper | TinyInst 的插桩实现原理分析
初探Windows用户态调试机制
最近在学习张银奎老师的《软件调试》,获益良多。熟悉Windows调试机制,对我们深入理解操作系统以及游戏保护的原理有着莫大好处。
战神伽罗
2019/07/24
1.2K0
初探Windows用户态调试机制
API钩取
继续学习《逆向工程核心原理》,本篇笔记是第四部分:API钩取,主要介绍了调试钩取、DLL注入实现IAT钩取、API代码修改钩取和全局API钩取等内容
红客突击队
2022/09/29
9070
API钩取
Windows应用程序调试原理全景图
在Windows上做开发的程序猿们都知道,x86架构处理器有一条特殊的指令——int 3,也就是机器码0xCC,用于调试所用,当程序执行到int 3的时候会中断到调试器,如果程序不处于调试状态则会弹出一个错误信息,之后程序就结束。使用VC开发程序时,在Debug版本的程序中,编译器会向函数栈帧中填充大量的0xCC,用于调试使用。因此,经常我们的程序发生缓冲区溢出时,会看到大量的“烫烫烫…”,这是因为“烫”的编码正是两个0xCC。
轩辕之风
2020/03/11
1.3K0
利用本地RPC接口的UAC Bypass
AppInfo是一个本地RPC服务,其接口ID为201ef99a-7fa0-444c-9399-19ba84f12a1a,AppInfo 是 UAC 提升的关键。ShellExecuteEx()通过 RPC 调用将所有提升请求转发到 AppInfo NT 服务。AppInfo 在系统上下文中调用一个名为“approve.exe”的可执行文件,这是启动用户同意的对话框的可执行文件.
黑白天安全
2022/04/12
1.6K0
利用本地RPC接口的UAC Bypass
迷雾退散:揭秘创建进程时ebx为什么指向peb的答案
这篇文章的起因,是笔者之前在做样本分析的时候,经常会遇到需要调试傀儡进程的情况,而其中有一种情景是将启动的白进程PE文件整个掏空并用黑进程进行替换。
FB客服
2021/10/21
8110
10.1 调试事件读取寄存器
当读者需要获取到特定进程内的寄存器信息时,则需要在上述代码中进行完善,首先需要编写CREATE_PROCESS_DEBUG_EVENT事件,程序被首次加载进入内存时会被触发此事件,在该事件内首先我们通过lpStartAddress属性获取到当前程序的入口地址,并通过SuspendThread暂停程序的运行,当被暂停后则我没就可以通过ReadProcessMemory读取当前位置的一个字节机器码,目的是保存以便于后期的恢复,接着通过WriteProcessMemory向对端(void*)dwAddr地址写出一个0xCC断点,该断点则是int3停机指令,最后ResumeThread恢复这个线程的运行,此时程序中因存在断点,则会触发一个EXCEPTION_DEBUG_EVENT异常事件。
王瑞MVP
2023/10/04
2120
10.1 调试事件读取寄存器
14种DLL注入技术
本文是我搜集整理的DLL注入方法,有一些自己实现了,在文末的链接里有源码,其余没有实现的也有git链接或别的源码链接。
全栈程序员站长
2022/08/27
4.9K0
实战 | 进程启动技术的思路和研究
通过常用的api来创建进程是常规启动进程的方式,最常用的几个api有WinExec、ShellExecute、CreateProcess,我们一个一个来看一下
HACK学习
2021/11/12
1.2K0
VMPROTECT处理异常2-各种异常处理(VEH,VCH,SEH,UEF)
UEF、VEH、VCH异常处理函数定义(UEF和VEH、VCH的函数类型名不一样,但是参数是一样的):
franket
2020/04/06
3K0
C/C++ 程序反调试方法总结
C/C++ 要实现程序反调试有多种方法,BeingDebugged,NtGlobalFlag,ProcessHeap,CheckRemoteDebuggerPresent,STARTUPINFO,IsDebuggerPresent,父进程检测,TLS 线程局部存储,RDTSC时钟检测反调试,MapFileAndCheckSum,等都可实现反调试,这里我分别编写了一些案例,基本上一锅端了。
王瑞MVP
2022/12/28
5310
微软轻量级系统监控工具sysmon原理与实现完全分析
前两次我们分别讲了sysmon的ring3与ring0的实现原理,但是当初使用的版本的是8.X的版本,最新的版本10.X的sysmon功能有所增加,经过分析代码上也有变化。比如增加DNS得功能,这个功能实现也很简单,就是ETW里获取Microsoft-Windows-DNS-Client得数据,但是本篇不讲这个,本续篇主要讲内核里的事件结构。
FB客服
2020/10/27
1.3K0
微软轻量级系统监控工具sysmon原理与实现完全分析
10.3 调试事件转存进程内存
我们继续延申调试事件的话题,实现进程转存功能,进程转储功能是指通过调试API使获得了目标进程控制权的进程,将目标进程的内存中的数据完整地转存到本地磁盘上,对于加壳软件,通常会通过加密、压缩等手段来保护其代码和数据,使其不易被分析。在这种情况下,通过进程转储功能,可以将加壳程序的内存镜像完整地保存到本地,以便进行后续的分析。
王瑞MVP
2023/10/05
2100
10.3 调试事件转存进程内存
eBPF 入门开发实践教程八:在 eBPF 中使用 exitsnoop 监控进程退出事件,使用 ring buffer 向用户态打印输出
eBPF (Extended Berkeley Packet Filter) 是 Linux 内核上的一个强大的网络和性能分析工具。它允许开发者在内核运行时动态加载、更新和运行用户定义的代码。
云微
2023/02/24
6620
相关推荐
Windows环境下的调试器探究
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验