无限硬件中断的代码实现
VSole2023-06-01 09:06:35
一、简介
在学习vt时了解到无限硬件断点技术,即不依赖于dr寄存器实现硬件断点。
由于硬件断点依赖于调试寄存器 dr0-dr3,这就意味着只能设置4个硬件断点。
无限硬件断点的原理:
1.根据目标地址计算出其PTE地址,设置PTE的 nx/xd 属性,即不可执行属性。
(由于PTE控制着4KB物理页的属性,因此目标代码所属的整个物理页都被设置为不可执行。)
2.当执行流执行至目标物理页时,由于代码的不可执行属性而触发 #PF 缺页中断。
3.此时根据触发缺页中断的线性地址判断是否为目标地址?若是,则修改PTE的执行属性并进行事件注入至 #DB,内核异常处理函数将会将该异常派发给调试器。若不是,则仍需要修复PTE的可执行属性,置位rflags.TF以便于下条指令触发 #DB 异常被vmm接管,修复cr2并进行事件注入 #PF。
4.当vmm接管 #DB 异常时,判断是否为目标进程的目标线性地址,并根据情况进行分类处理。
二、源码实现
关键函数的源码如下:
void HandleOfInterruption() { Rflags rflags = { 0 }; VmExitInterruptInformation exit_interrupt_info = { 0 }; ExitQualification exit_qualification = { 0 }; IA32_DEBUGCTL_STRUCRION ia32_debugctl = { 0 }; rflags.all = g_pGuestRegs->rflags; asm_vmread32(&g_vmm_handle_config.exit_instruction_length, VM_EXIT_INSTRUCTION_LEN); asm_vmread(&exit_qualification, EXIT_QUALIFICATION); asm_vmread32(&exit_interrupt_info, VM_EXIT_INTR_INFO); asm_vmread32(&ia32_debugctl, IA32_DEBUGCTL); if (exit_interrupt_info.Bits.valid) { /* * 中断类型: 0.外部中断, 2.nmi, 3.硬件中断, 6.软中断 */ switch (exit_interrupt_info.Bits.vector) { case 1: // debug 硬件中断 { // signel-step exit_qualification.Bits.bs=true if (rflags.Bits.tf && !ia32_debugctl.Bits.btf) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[+] #DB signel-step ==> GuestRIP: 0x%p, GuestRSP: 0x%p", g_pGuestRegs->rip, g_pGuestRegs->rsp); /* * 是否开启无线硬件中断? 若开启则重新设置页属性nx */ if (g_vmx_config.enable_unlimit_hardware_breakpoint) { unsigned __int64 guest_cr3; asm_vmread(&guest_cr3, GUEST_CR3); if (directory_table_base.user_cr3 == guest_cr3 || directory_table_base.kernel_cr3 == guest_cr3) { SetupPteNx(g_pGuestRegs->rip, TRUE); rflags.Bits.tf = FALSE; asm_vmwrite(GUEST_RFLAGS, rflags.all); break; } } g_vmm_handle_config.Config.Bits.event_inject = TRUE; break; } // haredWare breakpointer if (!rflags.Bits.tf && (exit_qualification.all & 0xf)) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[+] #DB hard-ware ==> GuestRIP: 0x%p, GuestRSP: 0x%p", g_pGuestRegs->rip, g_pGuestRegs->rsp); g_vmm_handle_config.Config.Bits.event_inject = TRUE; break; } break; } case 3: // int3 软中断 { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[+] #BP ==> GuestRIP: 0x%p, GuestRSP: 0x%p", g_pGuestRegs->rip, g_pGuestRegs->rsp); g_vmm_handle_config.Config.Bits.event_inject = TRUE; break; } case 0xe: // page_fault 软中断 { unsigned __int64 guest_cr3; asm_vmread(&guest_cr3, GUEST_CR3); if (g_vmx_config .enable_unlimit_hardware_breakpoint && ( directory_table_base.user_cr3 == guest_cr3 || directory_table_base.kernel_cr3 == guest_cr3)) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[+] #PF ==> GuestRIP: 0x%p, GuestRSP: 0x%p, fault_address: 0x%p", g_pGuestRegs->rip, g_pGuestRegs->rsp, exit_qualification.all); /* * 目标地址: 1.取消nx; 2.将异常注入给#DB; */ if (exit_qualification.all == targetAddress) { exit_interrupt_info.Bits.type = 3; exit_interrupt_info.Bits.vector = 1; exit_interrupt_info.Bits.error_code_valid = FALSE; g_vmm_handle_config.Config.Bits.event_inject = TRUE; SetupPteNx(exit_qualification.all, FALSE); } else { /* 程序正常返回执行 signel-step */ SetupPteNx(exit_qualification.all, FALSE); g_vmm_handle_config.Config.Bits.event_inject = FALSE; rflags.Bits.tf = TRUE; asm_vmwrite(GUEST_RFLAGS, rflags.all); } } else { asm_WriteCr2(exit_qualification.all); g_vmm_handle_config.Config.Bits.event_inject = TRUE; } break; } default: DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[+] 未处理中断 ==> type: %d, index: %d", exit_interrupt_info.Bits.type, exit_interrupt_info.Bits.vector); } /* * 事件注入 */ if (g_vmm_handle_config.Config.Bits.event_inject) { VmEntryInterruptionInformationField interruption_information_field = { 0 }; interruption_information_field.Bits.valid = TRUE; interruption_information_field.Bits.type = exit_interrupt_info.Bits.type; interruption_information_field.Bits.vector = exit_interrupt_info.Bits.vector; if (exit_interrupt_info.Bits.error_code_valid) { UINT64 ExitInterruptErrorCode = 0; interruption_information_field.Bits.deliver_error_code = TRUE; asm_vmread(&ExitInterruptErrorCode, VM_EXIT_INTR_ERROR_CODE); asm_vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, ExitInterruptErrorCode); } asm_vmwrite(VM_ENTRY_INSTRUCTION_LEN, g_vmm_handle_config.exit_instruction_length); asm_vmwrite(VM_ENTRY_INTR_INFO_FIELD, interruption_information_field.all); /*VmxProcessorBasedControls process_base; asm_vmread(&process_base.all, CPU_BASED_VM_EXEC_CONTROL); process_base.Bits.monitor_trap_flag = TRUE; asm_vmwrite(CPU_BASED_VM_EXEC_CONTROL, process_base.all);*/ } } else DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[!] 无效ExitInterruptInfo."); tag_ret: return; } __int64 pte_base; // 用于线性地址与pte的转换 DirectoryTableBase directory_table_base = { 0 }; __int64* targetAddress = 0x0000000140001130; _PsLookupProcessByProcessId WkPsLookUpProcessByProcessId; void SetupPteNx(__int64* address, BOOLEAN setup) { PHYSICAL_ADDRESS pa; pa.QuadPart = 0xffffffffffffffff; PAttachProcessStruct attach_procee = (PAttachProcessStruct)MmAllocateContiguousMemory(sizeof(AttachProcessStruct), pa); attach_procee->target_cr3 = directory_table_base.kernel_cr3; attach_procee->pte = (((__int64)address >> 9) & 0x7ffffffff8) + pte_base; attach_procee->setup = setup; /* * 切换Cr3并修改pte */ AttachProcess(attach_procee->target_cr3, &attach_procee->currect_cr3); memcpy(&attach_procee->pte_t, (void*)attach_procee->pte, 8); attach_procee->pte_t.Bits.xd = attach_procee->setup; memcpy((void*)attach_procee->pte, &attach_procee->pte_t, 8); AttachProcess(attach_procee->currect_cr3, &attach_procee->currect_cr3); MmFreeContiguousMemory(attach_procee); } void InitDirectoryTableBaseByPid(__int64 pid) { PEPROCESS pEProcess; WkPsLookUpProcessByProcessId(pid, &pEProcess); directory_table_base.kernel_cr3 = *(__int64*)((__int64)pEProcess + 0x28); directory_table_base.user_cr3 = *(__int64*)((__int64)pEProcess + 0x280); ObDereferenceObject(pEProcess); } void UnlimitHareWareBreakpoint(int index) { UNICODE_STRING unicode_PsLookUpProcessByProcessId; UNICODE_STRING unicode_MiSystemFault; idt_hook_config.Bits.set_pte_nx = TRUE; // 初始化PsLookupProcessByProcessId RtlInitUnicodeString(&unicode_PsLookUpProcessByProcessId, L"PsLookupProcessByProcessId"); WkPsLookUpProcessByProcessId = (_PsLookupProcessByProcessId)MmGetSystemRoutineAddress(&unicode_PsLookUpProcessByProcessId); pte_base = *(__int64*)((__int64)MmProtectMdlSystemAddress + 0xc9); InitDirectoryTableBaseByPid(3724); SetupPteNx(targetAddress, TRUE); } asm_AttachProcess proc mov rax, cr3 mov [rdx], rax mov cr3, rcx ret asm_AttachProcess endp
效果图:
需要注意的是,我的测试环境为Windows10,单核。

VSole
网络安全专家