无限硬件中断的代码实现

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,单核。
中断处理asm
本作品采用《CC 协议》,转载必须注明作者和本文链接
(由于PTE控制着4KB物理页的属性,因此目标代码所属的整个物理页都被设置为不可执行。若是,则修改PTE的执行属性并进行事件注入至 #DB,内核异常处理函数将会将该异常派发给调试器。若不是,则仍需要修复PTE的可执行属性,置位rflags.TF以便于下条指令触发 #DB 异常被vmm接管,修复cr2并进行事件注入 #PF。
假如想在x86平台运行arm程序,称arm为source ISA, 而x86为target ISA, 在虚拟化的角度来说arm就是Guest, x86为Host。这种问题被称为Code-Discovery Problem。每个体系结构对应的helper函数在target/xxx/helper.h头文件中定义。
内核学习-异常处理
2021-12-31 16:22:12
异常产生后,首先是要记录异常信息,然后要寻找异常的处理函数,称为异常的分发,最后找到异常处理函数并调用,称为异常处理。异常处理,异常分发,异常处理 展开。
API(Application Programming Interface),我们调用时只需提供正确的参数以及接收返回值就可以判断API执行是否成功或者通过GetLastError获得错误原因.
能运行的环境包括I/O,权限控制,系统调用,进程管理,内存管理等多项功能都可以归结到上边两点中。需要注意的是,kernel 的crash 通常会引起重启。注意大多数的现代操作系统只使用了 Ring 0 和 Ring 3。
MSF+生成流量免杀木马
2022-01-14 11:34:16
在实战中,即便你绕过了杀毒软件的检测,也很有可能会结束在某些流量监控的设备上。MSF可以说其是每一个内网玩家的必用工具。理所当然,这款工具也自然而然地被各大安全厂商分析,捕捉其在命令执行时产生的数据和流量。当我们使用一个没有做过加密处理的原版工具时,内网中的安全设备会根据我们的流量特征进行判断,认定我们为恶意进程,从而导致控制中断。Meterpreter技巧生成后门msfvenom?
看雪论坛作者ID:xxxlion
nmap -PN -sS -sV --script=vulscan –script-args vulscancorrelation=1 -p80 target. nmap -PN -sS -sV --script=all –script-args vulscancorrelation=1 target. NetCat,它的实际可运行的名字叫nc,应该早很就被提供,就象另一个没有公开但是标准的Unix工具。
vmp 相关的问题
2021-11-19 16:58:50
为新版本的符合一个叫做寄存器轮转的问题所以他可能jmp ebp,jmp edi等等的。
虚拟机检测技术整理
2023-05-11 09:15:35
第一次尝试恶意代码分析就遇到了虚拟机检测,于是就想着先学习一下检测的技术然后再尝试绕过。学习后最终发现,似乎最好的方法不应该是去patch所有检测方法,而是直接调试并定位检测函数再绕过。但既然已经研究了两天,索性将收集到的资料整理一下,方便后人查找。恶意软件可以搜索这些文件、目录或进程的存在。VMware 虚拟机中可能会有如下的文件列表:C:\Program Files\VMware\
VSole
网络安全专家