無限硬件中斷的代碼實現
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
網絡安全專家