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

发布于:2025-08-05 ⋅ 阅读:(10) ⋅ 点赞:(0)

软件调试


软件断点

调试的本质是什么?

就是在被调试程序中触发异常,然后被调试程序就会向_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.dll
	HMODULE 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);
		// 修正rip
		context.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;
}

效果展示

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


网站公告

今日签到

点亮在社区的每一天
去签到