当前位置: 首页 > news >正文

windows内核研究(软件调试-软件断点)

软件调试


软件断点

调试的本质是什么?

就是在被调试程序中触发异常,然后被调试程序就会向_DEBUG_OBJECT结构体添加调试事件,这里我们调试器就接管这个异常了(调试的过程就是异常处理的过程)

软件断点

在x64dbg中通过快捷键F2能下一个软件中断,当程序运行到这里时,程序就会停下来,得到程序的控制权,也被称之为中断到调试器

本质上就是在当前指定位置的机器码(硬编码)改为了int 3,这里可以用其他内存搜索工具查看这个下断点的地址,可以发现是被改为了int 3(CC)

在这里插入图片描述

执行流程

  • 被调试进程
    1. 当CPU检测到INT 3指令
    2. 查IDT表找到对应中断处理函数
    3. CommonDispatchException
    4. KiDispatchException
    5. DbgkForwardException搜集并发送调试事件 -> DbgkpSendApiMessage(x,x)
  • 调试器进程
    1. 循环判断调试事件
    2. 取出调试事件
    3. 获取CPU寄存器上下文和内存
    4. 用户处理

代码测试

#include<iostream>
#include<windows.h>
#include <stdint.h>
#include <capstone/capstone.h>
#define DEBUGGR_PROCESS L"C:\\Users\\BananaLi\\Desktop\\HookMe.exe"// 保存原始字节
BYTE originalByte;
BYTE int3 = 0xCC;  // INT3 断点指令HANDLE g_hDebugThread;
BOOL bInitCapstone = TRUE;
CONTEXT context = { 0 };
BOOL bIsSystemInt3 = TRUE; // 第一次为系统断点 ntdll!LdrInitializeThunk
DWORD dwContinue DBG_CONTINUE;LPVOID lpAddress;
HANDLE hProcess;// 定义 Capstone 函数指针类型
typedef unsigned int (*cs_open_fn)(unsigned int arch, unsigned int mode, void** handle);
typedef unsigned int (*cs_disasm_fn)(void* handle, const uint8_t* code, size_t code_size, uint64_t address, size_t count, cs_insn** insn);
typedef void (*cs_free_fn)(cs_insn* insn, size_t count);
typedef unsigned int (*cs_close_fn)(void** handle);void DisassembleHex(HANDLE hProcess, LPVOID address, size_t size) {// 1. 加载 capstone.dllHMODULE capstone_dll = LoadLibrary(L"C:\\Users\\BananaLi\\Desktop\\capstone_x64.dll");if (!capstone_dll) {printf("Failed to load capstone.dll! Error: %d\n", GetLastError());}// 2. 获取函数指针cs_open_fn cs_open = (cs_open_fn)GetProcAddress(capstone_dll, "cs_open");cs_disasm_fn cs_disasm = (cs_disasm_fn)GetProcAddress(capstone_dll, "cs_disasm");cs_free_fn cs_free = (cs_free_fn)GetProcAddress(capstone_dll, "cs_free");cs_close_fn cs_close = (cs_close_fn)GetProcAddress(capstone_dll, "cs_close");if (!cs_open || !cs_disasm || !cs_free || !cs_close) {printf("Failed to get Capstone functions!\n");FreeLibrary(capstone_dll);}// 3. 初始化 Capstone(x64 模式)void* handle;if (cs_open(CS_ARCH_X86, CS_MODE_64, &handle) != CS_ERR_OK) {printf("Failed to initialize Capstone!\n");FreeLibrary(capstone_dll);}// 2. 从内存读取指令字节uint8_t code[32];SIZE_T bytesRead;if (!ReadProcessMemory(hProcess, address, code, size, &bytesRead) || bytesRead != size) {printf("Failed to read memory at 0x%p\n", address);cs_close(&handle);return;}// 3. 反汇编cs_insn* insn;size_t count = cs_disasm(handle, code, bytesRead, (uint64_t)address, 0, &insn);if (count > 0) {printf("Disassembly at 0x%p:\n", address);printf("Address    | Bytes       | Assembly\n");printf("----------------------------------\n");for (size_t i = 0; i < count; i++) {printf("0x%08llx | ", insn[i].address);for (size_t j = 0; j < insn[i].size; j++) {printf("%02x ", insn[i].bytes[j]);}printf("%*s | %s %s\n",(int)(15 - insn[i].size * 3), "",insn[i].mnemonic,insn[i].op_str);}cs_free(insn, count);}else {printf("Failed to disassemble at 0x%p\n", address);}// 4. 关闭引擎cs_close(&handle);
}BOOL WaitForUserCommand() {// 模拟调试器等待用户输入命令printf("请输入命令:\n");system("pause");return TRUE;
}BOOL Int3ExcptionProc(EXCEPTION_DEBUG_INFO* pExceptionInfo) {BOOL bRet = FALSE;// 第一次为系统断点if (bIsSystemInt3) {bIsSystemInt3 = FALSE;return TRUE;}else {// 恢复原码BOOL nnn = WriteProcessMemory(hProcess, pExceptionInfo->ExceptionRecord.ExceptionAddress, &originalByte, 1, NULL);printf("error : %d\n", GetLastError());// 显示断点位置printf("Int 3断点地址:0x%p\n",pExceptionInfo->ExceptionRecord.ExceptionAddress);// 获取线程上下文context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;GetThreadContext(g_hDebugThread, &context);// 修正ripcontext.Rip--;printf("rip的值:0x%p\n\n", context.Rip);GetThreadContext(g_hDebugThread, &context);FlushInstructionCache(GetCurrentProcess(),(LPCVOID)context.Rip,1);printf("------------还原后的正确汇编指令------------\n");// 从断点位置解释反汇编代码DisassembleHex(hProcess, lpAddress,16);// 等待用户输入命令while (bRet == FALSE) {bRet = WaitForUserCommand();}}return bRet;
}void setInt3BreakPoint(DEBUG_EVENT* pDebugEvent) {// 获取进程入口点地址LPTHREAD_START_ROUTINE lpStartAddress = (LPTHREAD_START_ROUTINE)pDebugEvent->u.CreateProcessInfo.lpStartAddress;hProcess = pDebugEvent->u.CreateProcessInfo.hProcess; lpAddress = pDebugEvent->u.CreateProcessInfo.lpStartAddress;printf("断点位置:0x%p\n",lpAddress);DWORD64 oldAddressData;ReadProcessMemory(hProcess, lpAddress, &oldAddressData, sizeof(DWORD64), NULL);printf("保存前断点地址对应的硬编码:%llx\n" , oldAddressData);// 1. 保存原始字节ReadProcessMemory(hProcess, lpAddress, &originalByte, sizeof(BYTE), NULL);printf("保存的originalByte:%llx\n", originalByte);// 2. 写入INT3指令WriteProcessMemory(hProcess, lpAddress, &int3, sizeof(BYTE), NULL);ReadProcessMemory(hProcess, lpAddress, &oldAddressData, sizeof(DWORD64), NULL);printf("保存后断点地址对应的硬编码:%llx\n\n", oldAddressData);printf("------------设置软件断点后的汇编指令------------\n");// 从断点位置解释反汇编代码DisassembleHex(hProcess, lpAddress, 16);// 3. 刷新指令缓存FlushInstructionCache(hProcess, lpAddress, sizeof(BYTE));
}// 异常过滤器
BOOL ExceptionHandler(DEBUG_EVENT* pDebugEvent) {BOOL bRet = TRUE;// 得到异常信息EXCEPTION_DEBUG_INFO exceptionInfo = pDebugEvent->u.Exception;// 得到线程句柄g_hDebugThread = OpenThread(THREAD_ALL_ACCESS, FALSE, pDebugEvent->dwThreadId);// 判断 异常类型switch (exceptionInfo.ExceptionRecord.ExceptionCode) {// int 3 异常case EXCEPTION_BREAKPOINT: {printf("断点异常\n");bRet = Int3ExcptionProc(&exceptionInfo);break;}// 还有很多其他的异常类型可以处理。。。}return bRet;}int main() {BOOL nIsContinue = TRUE;DEBUG_EVENT debugEvent = { 0 };BOOL bRet = TRUE;// 1.创建调试进程STARTUPINFO startUpInfo = { 0 };PROCESS_INFORMATION pInfo = { 0 };GetStartupInfo(&startUpInfo);bRet = CreateProcess(DEBUGGR_PROCESS, NULL, NULL, NULL, TRUE, DEBUG_PROCESS || DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &startUpInfo, &pInfo);if (bRet == FALSE) {printf("创建调试进程失败,错误码:%d\n",GetLastError());return 0;}// 调试循环(主框架)while (nIsContinue) {// 2.等待调试事件bRet = WaitForDebugEvent(&debugEvent, INFINITE);if (bRet == FALSE) {printf("等待调试事件失败,错误码:%d\n", GetLastError());return 0;}switch (debugEvent.dwDebugEventCode) {case EXCEPTION_DEBUG_EVENT: {// 处理异常bRet = ExceptionHandler(&debugEvent);if (!bRet)dwContinue = DBG_EXCEPTION_NOT_HANDLED;break;}case CREATE_PROCESS_DEBUG_EVENT: {// 在OPE入口设置断点setInt3BreakPoint(&debugEvent);break;}case EXIT_PROCESS_DEBUG_EVENT: {nIsContinue = FALSE;break;}case CREATE_THREAD_DEBUG_EVENT: {break;}case EXIT_THREAD_DEBUG_EVENT: {break;}case LOAD_DLL_DEBUG_EVENT: {break;}case UNLOAD_DLL_DEBUG_EVENT: {break;}default:break;}// 让被调试程序继续运行bRet = ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, dwContinue);}return 0;
}

效果展示

在这里插入图片描述
在这里插入图片描述

http://www.lryc.cn/news/609586.html

相关文章:

  • 房屋租赁小程序租房小程序房产信息发布系统房屋租赁微信小程序源码
  • 架构师面试(三十九):微服务重构单体应用
  • 剧本杀小程序系统开发:开启沉浸式推理社交新纪元
  • 力扣1124:表现良好的最长时间段
  • 【Java】使用FreeMarker来实现Word自定义导出
  • leetcode-sql-3497分析订阅转化
  • 旧物回收小程序:开启绿色生活新篇章
  • Array容器学习
  • LeetCode 132:分割回文串 II
  • 【YOLO系列】YOLOv12详解:模型结构、损失函数、训练方法及代码实现
  • 关于Npm和Nvm的用法
  • Linux 环境 libpq加载异常导致psql 连接 PostgreSQL 库失败失败案例
  • uniapp开发微信小程序textarea在ios下有默认内边距的问题(textarea兼容问题)
  • 如何给Word和WPS文档添加密码或取消密码
  • Ethereum:拥抱开源,OpenZeppelin 未来的两大基石 Relayers 与 Monitor
  • Jwts用于创建和验证 ​​JSON Web Token(JWT)​​ 的开源库详解
  • OpenLayers 入门指南【五】:Map 容器
  • R 语言科研绘图第 67 期 --- 箱线图-显著性
  • Nestjs框架: Node.js 多环境配置策略与 dotenv 与 config 库详解
  • 政府财政行业云原生转型之路
  • Druid学习笔记 01、快速了解Druid中SqlParser实现
  • 排序算法入门:直接插入排序详解
  • 室内分布系统
  • ICCV 2025|单视频生成动态4D场景!中科大微软突破4D生成瓶颈,动画效果炸裂来袭!
  • Flutter开发 了解Scaffold
  • 深入理解Java的SPI机制,使用auto-service库优化SPI
  • 区块链基础之Merkle B+树
  • Azure DevOps - 使用 Ansible 轻松配置 Azure DevOps 代理 - 第6部分
  • 打造个人数字图书馆:LeaNote+cpolar如何成为你的私有化知识中枢?
  • 多级表头的导出