无限硬件中断的代码实现
2023-5-31 18:1:42 Author: 看雪学苑(查看原文) 阅读量:17 收藏


简介

在学习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\n", 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\n", 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\n", 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\n", 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\n", 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.\n");

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,单核。
 
参考:https://bbs.kanxue.com/thread-111149.htm
 
自己踩过的坑:
vt通过MTF设置调试并进行事件注入后陷入vmm的神奇特性!
https://bbs.kanxue.com/thread-277115.htm
vt接管#PF异常时出错,wrmsr,接着系统崩溃
https://bbs.kanxue.com/thread-277090.htm
vmm获取guest的DR寄存器出现问题,硬件断点
https://bbs.kanxue.com/thread-277050.htm
[求助] vt在接管中断时,想要自定义处理中断时,只有通过hook idt吗?
https://bbs.kanxue.com/thread-277041.htm

看雪ID:ALwalker

https://bbs.kanxue.com/user-home-832354.htm

*本文为看雪论坛优秀文章,由 ALwalker 原创,转载请注明来自看雪社区

# 往期推荐

1、在 Windows下搭建LLVM 使用环境

2、深入学习smali语法

3、安卓加固脱壳分享

4、Flutter 逆向初探

5、一个简单实践理解栈空间转移

6、记一次某盾手游加固的脱壳与修复

球分享

球点赞

球在看


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458505787&idx=2&sn=99a7ec7ebc64d0cc098d11f9f911ea21&chksm=b18ee2b186f96ba77d4a38fd518237af293c19cc58fe62c69149f7b76c75229b41148177e687#rd
如有侵权请联系:admin#unsafe.sh