系统安全二进制安全 [2022]不使用hypervisor接管windows内核异常 不使用hypervisor的情况下接管内核异常: how? 让我们检查一下内核处理流程: 触发异常后,在KdTrap中通过 KdpDebugRoutineSelect 来去进行异常处理选择(有调试器走1) ```cpp __int64 __fastcall KdTrap(int a1, int a2, int a3, int a4, char a5, char a6) { if ( KdpDebugRoutineSelect ) return KdpTrap_0(a1, a2, a3, a4, a5, a6); else return KdpStub_0(a1, a2, a3, a4, a5, a6); } ``` 在异常不等于STATUS_BREAKPOINT的情况下,也就是不是int3中断的情况下,走到kdreport ```cpp char __fastcall KdpTrap_0( __int64 trapInfo, __int64 commandString, __int64 commandType, __int64 debuggerContext, char response) { char v5; // dl _QWORD *contextPointer; // rbx unsigned __int64 addressValue; // rax __int64 previousAddressValue; // rdi int subtractionResult1; // eax int subtractionResult2; // eax int subtractionResult3; // eax int subtractionResult4; // eax int printFunctionReturnValue; // eax __int64 currentAddressValue; // rax v5 = 0; contextPointer = (_QWORD *)debuggerContext; if ( *(_DWORD *)commandType != 0x80000003 ) return KdpReport(trapInfo, 0, commandType, debuggerContext); ``` 只要不是 fast error或者单步异常,走到KdExitDebugger 里面 ```cpp if ( *a3 == -1073740768 || (unsigned int)(v6 + 2147483645) <= 1 || (unsigned int)(v6 - 1073741854) <= 1 || (NtGlobalFlag & 1) != 0 ) { v9 = a6; if ( a6 || (unsigned int)v6 > 0x4000001D && v6 != 0xC0000037 && v6 < 0x40000020 ) goto LABEL_6; } else { v9 = a6; if ( a6 ) { LABEL_6: v10 = ((__int64 (*)(void))KdEnterDebugger)(); CurrentPrcb = KeGetCurrentPrcb(); v12 = v10; KdpCopyContext(*(_QWORD *)&CurrentPrcb[1].PPPagedLookasideList[3].FreeMisses, *(unsigned int *)(a4 + 48), a4); KiSaveProcessorControlState((__int64)&CurrentPrcb->ProcessorState.SpecialRegisters.MsrLStar, v13); v14 = *(_QWORD *)&CurrentPrcb[1].PPPagedLookasideList[3].FreeMisses; LOBYTE(v15) = v9; v16 = *(_DWORD *)(v14 + 48); v17 = KdpReportExceptionStateChange((__int64)a3, v14, v15); KdpCopyContext(a4, v16, *(_QWORD *)&CurrentPrcb[1].PPPagedLookasideList[3].FreeMisses); KiRestoreProcessorControlState(&CurrentPrcb->ProcessorState.SpecialRegisters.MsrLStar); LOBYTE(v18) = v12; KdExitDebugger_0(v18); result = v17; KdpControlCPressed_0 = 0; return result; } } ``` KdExitDebugger在处理完一些必要的状态后,走到 KeThawExecution_0(v6);里面 而PoAllProcIntrDisabled_0只要为0 就会调用我们的老朋友 **KeQueryPerformanceCounter** ```cpp __int64 __fastcall KeThawExecution_0(char a1) { char v2; // di __int64 v3; // rcx unsigned __int8 v4; // bl unsigned __int64 v5; // rcx unsigned __int64 v6; // rax struct _KPRCB *CurrentPrcb; // rcx __int64 result; // rax v2 = 0; if ( (KiFreezeFlag & 8) == 0 ) v2 = KdPortLocked; ((void (__fastcall *)(_QWORD, _QWORD))off_401A88[0])(0i64, 0i64); if ( !PoAllProcIntrDisabled_0 ) { MEMORY[0xFFFFF78000000350] = KeQueryPerformanceCounter(0i64).QuadPart; KiInterruptTimeErrorAccumulator = 0i64; } ``` 为什么说 **KeQueryPerformanceCounter**是我们的老朋友呢,因为这个hal层的吊东西已经被日烂了,从infinity hook到infinity hook V2. v3 v4 他已经被日烂了 这次被日的原因和infinity hook v4一模一样,是这一步出了问题: ```cpp v2 = HalpPerformanceCounter; if ( *(_DWORD *)(HalpPerformanceCounter + 0xE4) == 5 ) { v37 = 10000000i64; if ( HalpTimerReferencePage ) { if ( (*(_DWORD *)(HalpPerformanceCounter + 224) & 0x10000) != 0 ) v20 = *(_QWORD *)(HalpPerformanceCounter + 72) + (unsigned int)(*(_DWORD *)(HalpPerformanceCounter + 0x50) * HIDWORD(KeGetPcr()[1].LockArray)); else v20 = *(_QWORD *)(HalpPerformanceCounter + 72); v21 = (*(__int64 (__fastcall **)(__int64))(HalpPerformanceCounter + 112))(v20); v22 = MEMORY[0xFFFFF780000003B8]; v10.QuadPart = v22 + RtlUnsignedMultiplyHigh(v21, *((_QWORD *)HalpTimerReferencePage + 1)); } else { do { v24 = *(_QWORD *)(v2 + 0xD0); do { v25 = *(_QWORD *)(v2 + 0xC8); InternalData = HalpTimerGetInternalData(v2); v27 = (*(__int64 (__fastcall **)(__int64))(v2 + 0x70))(InternalData); _InterlockedOr(v36, 0); v28 = *(_QWORD *)(v2 + 200); } while ( v25 != v28 ); } ``` 注意其中的 ```cpp v25 = *(_QWORD *)(v2 + 0xC8); InternalData = HalpTimerGetInternalData(v2); v27 = (*(__int64 (__fastcall **)(__int64))(v2 + 0x70))(InternalData); ``` v2+0x70指向的是HalpHvCounterQueryCounter 在最新的infinity hook中也是他出了毛病,没有被PG保护 因此在满足下列条件后,即可进行内核VEH操作: ```cpp KdpDebugRoutineSelect = 1 PoAllProcIntrDisabled = 0 HalpPerformanceCounter + 0xDC != 0x40 ``` **这会PG吗?** 不 他不会.别问咋知道的,某大型软件现在正在用它全局接管内核异常 完整调用栈: ```cpp 00 fffff803`3d12cfa8 fffff803`3d21ecf8 hal!HalpTimerGetInternalData 01 fffff803`3d12cfb0 fffff803`3d4fd046 hal!KeQueryPerformanceCounter+0x168 02 fffff803`3d12cff0 fffff803`3dabee65 nt!KeThawExecution+0x4a 03 fffff803`3d12d020 fffff803`3d4f4d34 nt!KdExitDebugger+0xb1 04 fffff803`3d12d050 fffff803`3dac2419 nt!KdpReport+0xd8 05 fffff803`3d12d090 fffff803`3d2b9b90 nt!KdpTrap+0x14d 06 fffff803`3d12d0e0 fffff803`3d2b97b0 nt!KdTrap+0x2c 07 fffff803`3d12d120 fffff803`3d453602 nt!KiDispatchException+0x1e0 08 fffff803`3d12d7d0 fffff803`3d44ccf0 nt!KiExceptionDispatch+0xc2 09 fffff803`3d12d9b0 fffff803`3d449471 nt!KiBreakpointTrap+0x370 0a fffff803`3d12db48 fffff803`3d4c2a7e nt!DbgBreakPointWithStatus+0x1 0b fffff803`3d12db50 fffff803`3d4810a4 nt!KdCheckForDebugBreak+0xdbbe2 0c fffff803`3d12db80 fffff803`3d309b8d nt!KeAccumulateTicks+0x1743e4 0d fffff803`3d12dbe0 fffff803`3d30ac73 nt!KiUpdateRunTime+0x5d 0e fffff803`3d12dc30 fffff803`3d21e883 nt!KeClockInterruptNotify+0x8f3 0f fffff803`3d12df40 fffff803`3d361195 hal!HalpTimerClockInterrupt+0x63 10 fffff803`3d12df70 fffff803`3d4420ea nt!KiCallInterruptServiceRoutine+0xa5 11 fffff803`3d12dfb0 fffff803`3d4426d7 nt!KiInterruptSubDispatchNoLockNoEtw+0xea 12 fffff803`3d11f540 fffff803`3d238a0f nt!KiInterruptDispatchNoLockNoEtw+0x37 13 fffff803`3d11f6d8 fffff803`3d413925 hal!HalProcessorIdle+0xf 14 fffff803`3d11f6e0 fffff803`3d30c3d8 nt!PpmIdleDefaultExecute+0x15 15 fffff803`3d11f710 fffff803`3d30bc05 nt!PpmIdleExecuteTransition+0x648 16 fffff803`3d11fb00 fffff803`3d44435c nt!PoIdle+0x345 17 fffff803`3d11fc60 00000000`00000000 nt!KiIdleLoop+0x2c ``` 阅读全文 2022-12-21 huoji 2 条评论
系统安全二进制安全C/C++ [2022]HyperMap -通过PTE自引用直接读取物理内存 在hypervisor中的host直接操作guest物理内存的情况中,我遇到了一个棘手的问题,即所有的API都是page_code的.使用他们不安全(即便是AMD有GIF的情况下). 所以需要设计一个高效、快速的内存访问系统.即通过设置一个魔术数字,实现base + offset的guest物理内存读取.我把它称之为hypermap 阅读全文 2022-10-28 huoji 0 条评论
系统安全C/C++汇编 [2022]hypervisor: 检测与预防(四) copy paster们久等了 书接上回,前情回顾.如果没有看过请浏览: [[2022]hypervisor: 检测与预防(三)](https://key08.com/index.php/2022/07/10/1493.html "[2022]hypervisor: 检测与预防(三)") 阅读全文 2022-10-01 huoji 0 条评论
二进制安全C/C++ [2022]hypervisor一个麻烦: Time Stamp Counter ### Time Stamp Counter 时间戳计数器( TSC ) 是一个 64 位寄存器,存在于自Pentium以来的所有x86处理器上.它计算自复位以来的 CPU周期数.该指令返回 EDX:EAX 中的 TSC.在x86-64模式下,还会清除RAX和RDX的高32位. ### 特性 对CPU执行的时间高度敏感 大部分GUI、网络设备依赖它用于计数从而实现一些功能 包括ETW也有可选项使用它计数 ### 问题 一个对CPU执行时间高度敏感、而且不容易篡改的东西,最容易拿来干啥? 检测CPU的周期,用于探测hypervisor是否存在 这是一个简单的例子: 阅读全文 2022-07-19 huoji 0 条评论
系统安全C/C++一线开发 [2022]hypervisor: 检测与预防(三) copy paster们久等了 书接上回,前情回顾.如果没有看过请浏览: [[2022]hypervisor: 检测与预防 (一)](https://key08.com/index.php/2022/05/12/1477.html "[2022] hypervisor: 检测与预防 (一)") [[2022]hypervisor: 检测与预防 (二)](https://key08.com/index.php/2022/06/19/1489.html "[2022]hypervisor: 检测与预防 (二)") ### KiErrata671Present 阅读全文 2022-07-10 huoji 0 条评论
C/C++一线开发 [2022]hypervisor: 检测与预防 (二) 前情回顾: [[2022] hypervisor: 检测与预防 (一)](https://key08.com/index.php/2022/05/12/1477.html "[2022] hypervisor: 检测与预防 (一)") 让我们来看看新的问题: 阅读全文 2022-06-19 huoji 0 条评论
系统安全C/C++ [2022] hypervisor: 检测与预防 (一) ### rdtsc 大部分虚拟机包括沙箱用的都存在一个问题 cpuid等一些会造成vmexit的东西, 被硬件加速了 不开虚拟机的时候 只花很少的时间,大于200个CPU周期左右 一旦进了虚拟机 会到大约20000个周期不等 木马 外挂 可以通过判断CPU周期确定自己是不是在虚拟机里 导致沙箱不存活之类的 阅读全文 2022-05-12 huoji 2 条评论