About Handling Nmi

NMI不可屏蔽外部中断的使用方法 NMI(不可屏蔽的外部中断)是一个特殊的异常。因为它处于异常区,所以它不受全局中断使能位MSTATUS.MIE的控制。NMI作为外部事件,E906 将其异常向量号定义为24,优先级在所有的中断和异常中最高。在APT32F173中,可以升级为NMI的外部中断源有:外部时钟检测错误EXI0内存(Flash,SRAM)校验错误掉电检测上述中断源的NMI使能后,不会影响原有的中断通路,比如EMCM的中断通路也是有效的,当然NMI的优先级是最高的,所以会以NMI的形式请求中断。 阅读详情
 来源:SoftRCE.net

本文基于Windows2003以上32位系统,因为XP处理NMI很弱,我们后面再说。
最近为点硬件写驱动,需要处理关于NMI的一些东西,索性把以前记录的一些资料和最新的体会写下来,希望对大家有用。
Nmi(Non Maskable Interrupt),一般在紧急的电源故障、总线超时或者存储器奇偶校验出错时被发出,对应的中断向量号为2,即int 2。CPU有两根引脚INTR和NMI接受外部中断请求信号:INTR接受可屏蔽中断请求,NMI接受不可屏蔽中断请求。在80386以上,标志寄存器 EFLAGS中的IF标志决定是否屏蔽可屏蔽中断请求。下面是调试器的结果:

kd> !idt -a

Dumping IDT:
00: 8082409e nt!KiTrap00
01: 8082421a nt!KiTrap01
02: Task Selector = 0x0058
03: 8082462a nt!KiTrap03
04: 808247a6 nt!KiTrap04
05: 80824904 nt!KiTrap05
06: 80824a86 nt!KiTrap06
07: 808250e6 nt!KiTrap07
08: Task Selector = 0x0050
09: 80825518 nt!KiTrap09
0a: 80825632 nt!KiTrap0A
//...
ff: 808231b0 nt!KiUnexpectedInterrupt207

相关的中断可以查阅详细资料[1],我们只关心下面两个:
IDTEntry _KiTrap02, D_INT032 ; 2: NMI/NPX Error
IDTEntry _KiTrap08, D_INT032 ; 8: Double Exception
即Windbg中显示的:
02: Task Selector = 0x0058
08: Task Selector = 0x0050
表 示的是系统为处理这两个int而设置的任务门,KGDT_DF_TSS(50h)和KGDT_NMI_TSS(58h)。那么是系统什么时候设置的呢?ntoskrnl加载的时候,KERNEL_ENTRY_POINT是KiSystemStartup()函数,反汇编不难看出它的流程,你也可以参考WangYu的一篇文章[2],或者可以参考ReactOs的代码,不过我们简单地反汇编一些:

1)首先是为NMI建立任务门,这样我们就能捕捉到NMI了:
INIT:006053CE mov eax, [ebp+var_10] ; 之前由GetMachineBootPointers得到的Idt
INIT:006053D1 lea ecx, [eax+10h] ; 16/8 -> 2号中断描述符
INIT:006053D4 mov byte ptr [ecx+5], 85h ; 10000101 -> dpl=0, present, taskgate
INIT:006053D8 mov word ptr [ecx+2], 58h ; KGDT_NMI_TSS(58h)
INIT:006053DE lea ecx, [edi+58h]
INIT:006053E1 mov byte ptr [ecx+5], 89h ; 10001001 -> 32bit, dpl=0, present, TSS32, not busy
INIT:006053E5 mov edx, offset _KiNMITSS
INIT:006053EA mov eax, edx
INIT:006053EC mov [ecx+2], ax ; KgdtBaseLow(2h)
INIT:006053F0 shr eax, 10h
INIT:006053F3 mov [ecx+7], ah ; KgdtBaseHi(7h)
INIT:006053F6 mov [ecx+4], al ; KgdtBaseMid(4h)
INIT:006053F9 mov eax, 68h ; MINIMUM_TSS_SIZE(68h)
INIT:006053FE mov [ecx], ax ; KgdtLimitLow(0h)
INIT:00605401 push edx
INIT:00605402 push edx
INIT:00605403 call KiInitializeTSS(x)

2)然后是建立堆栈:
INIT:00605408 pop edx
INIT:00605409 mov eax, cr3
INIT:0060540C mov [edx+1Ch], eax ; TssCr3(1ch)
INIT:0060540F mov eax, offset P0BootStack
INIT:00605414 mov eax, [eax+38h]
INIT:00605417 mov [edx+4], eax ; TssEsp0(4h) , NMI stack
INIT:0060541A mov [edx+38h], eax
INIT:0060541D mov dword ptr [edx+20h], offset _KiTrap02
INIT:00605424 mov dword ptr [edx+24h], 0 ; eflags
INIT:0060542B mov word ptr [edx+4Ch], 8 ; KGDT_R0_CODE(8h) -> CS
INIT:00605431 mov word ptr [edx+58h], 30h ; KGDT_R0_PCR(30h) -> FS
INIT:00605437 mov word ptr [edx+50h], ss
INIT:0060543A mov word ptr [edx+48h], 23h ; KGDT_R3_DATA(20h) + RPL_MASK(3h)
INIT:00605440 mov word ptr [edx+54h], 23h ; 初始化ES,DS
INIT:00605446 push offset _KiDoubleFaultStack
//...
INIT:0060545D call KiInitializePcr(x,x,x,x,x,x,x)

这里涉及一些结构,用Windbg可以清晰地看到:
kd> !pcr 0
kd> dt -r1 nt!_KPCR
kd> dt nt!_KIDTENTRY (_KGDTENTRY)
kd> dt -r1 nt!_KTHREAD
kd> dt -r1 nt!_KPROCESS

如果想在XP里设置Handler处理NMI,那么也该仿造以上过程,设置处理例程KiTrap02,把NMI-TSS里的EIP指向你的处理代码,然后建立堆栈,最后还要在iret后面添加jmp回到你的处理代码.原因简单地说,就是每当接受一个NMI中断,处理器会在内部屏蔽再次响应NMI,这一屏蔽过程直到执行中断返回指令IRET后才结束。还是直接看反汇编的KiTrap02代码吧:

.text:00484748 ; =============== S U B R O U T I N E =====================
.text:00484748
.text:00484748 _KiTrap02 proc near
.text:00484748
.text:00484748 var_8 = dword ptr -8
.text:00484748 var_4 = dword ptr -4
.text:00484748
.text:00484748 cli
.text:00484749 mov eax, ds:0FFDFF040h ; nt!_KPCR + 40 == _KTSS
.text:0048474E mov ecx, ds:0FFDFF124h ; nt!_KTHREAD
.text:00484754 mov edi, [ecx+38h] ; Edi= _KPROCESS
.text:00484757 mov ecx, [edi+18h] ; ULONG_PTR DirectoryTableBase[2]
.text:00484757 ; DirectoryTableBase[1] -> hyperspace
.text:00484757 ; DirectoryTableBase[0] -> CR3
.text:0048475A mov [eax+1Ch], ecx ; TSS.Edx= DirectoryTableBase[0]
.text:0048475D mov cx, [edi+30h]
.text:00484761 mov [eax+66h], cx ; TSS.IoMapBase= KPROCESS.IopmOffset
.text:00484765 mov ecx, [edi+20h] ; LdtDescriptor:_KGDTENTRY
.text:00484768 test ecx, ecx
.text:0048476A jz short loc_484770
.text:0048476A
.text:0048476C mov cx, 48h
.text:0048476C
.text:00484770 loc_484770:
.text:00484770 mov [eax+60h], cx ; set TSS.LDT
; if(_KPROCESS.LdtDescriptor==0) replaced by KGDT_LDT(0x48)
.text:00484770
.text:00484774 push dword ptr ds:0FFDFF040h ; push dword ptr PCR[PcTss]
.text:0048477A mov eax, ds:0FFDFF03Ch ; mov eax, PCR[PcGdt(3c)]
.text:0048477F mov ch, [eax+5Fh] ; mov ch, [eax+KGDT_NMI_TSS(58)+KgdtBaseHi(7)]
.text:00484782 mov cl, [eax+5Ch] ; mov cl, [eax+KGDT_NMI_TSS(58)+KgdtBaseMid(4)]
.text:00484785 shl ecx, 16
.text:00484788 mov cx, [eax+5Ah] ; mov cx, [eax+KGDT_NMI_TSS(58)+KgdtBaseLow(2)]
.text:0048478C mov ds:0FFDFF040h, ecx ; mov PCR[PcTss], ecx
.text:0048478C ; // PCR.TSS points to the NMI TSS
.text:0048478C
.text:00484792 pushf
.text:00484793 and [esp+8+var_8], 11111111111111111011111111111111b
.text:0048479A popf ; // Clear Nested Task bit in EFLAGS, no more NMI handler
.text:0048479A
.text:0048479B mov ecx, ds:0FFDFF03Ch ; PcGdt
.text:004847A1 lea eax, [ecx+58h] ; lea eax, [ecx] + KGDT_NMI_TSS
.text:004847A4 mov byte ptr [eax+5], 10001001b
; 32bit, dpl=0, present, TSS32, not busy
.text:004847A4 ; // Clear the busy bit in the TSS selector
.text:004847A4
.text:004847A8 mov eax, [esp+4+var_4] ; var_4 = KiSaveProcessorState(...)
.text:004847AB push 0
//... ...
.text:0048480B push ebp
.text:0048480C call KiSaveProcessorState(x,x)
.text:0048480C
.text:00484811 call KiHandleNmi() ; <---------------- Our CallBack Routines here..
.text:00484811
.text:00484816 or al, al ; if any CallBackRoutine handled nmi trap..
.text:00484818 jnz short Nmi_Magic_Handled ; Jump to restore and wait..
.text:00484818
.text:0048481A cmp ds:NmiUnknowFlag, 0 ; if(NmiFlagX!= 0)
.text:00484821 jnz short loc_484825 ; Setup Kernel debugger...
.text:00484821
.text:00484823 jmp short loc_484849 ; Pass nmi to Hal.dll,actually crash..
.text:00484823
.text:00484825 ; ---------------------------------------------------------------------------
.text:00484825
.text:00484825 loc_484825:
.text:00484825 cmp ds:NmiUnknowFlag, 8 ; NmiFlagX== 0 Nothing setuped,just pass to HAL
.text:00484825 ; 1-7 Nothing setuped,just pass to HAL and do crash
.text:00484825 ; 8 Get into KdDebugger
.text:00484825 ; >8 Dead Lock
.text:0048482C jb short loc_484849
.text:0048482C
.text:0048482E jnz short loc_484847
; if ( !_KdDebuggerNotPresent )
; {
; if ( _KdDebuggerEnabled )
; KeEnterKernelDebugger();
; }
; else
; while(1);
.text:00484830 cmp ds:_KdDebuggerNotPresent, 0
.text:00484837 jnz short loc_484847
.text:00484839 cmp ds:_KdDebuggerEnabled, 0
.text:00484840 jz short loc_484847
.text:00484842 call KeEnterKernelDebugger()
.text:00484847 loc_484847:
.text:00484847 jmp short loc_484847
.text:00484849 ; ---------------------------------------------------------------------------
.text:00484849 loc_484849:
.text:00484849 inc ds:NmiUnknowFlag
.text:0048484F push 0
.text:00484851 call ds:HalHandleNMI(x) ; <----------------
.text:00484851
.text:00484857 dec ds:NmiUnknowFlag ; --NmiFlagX != 0 then crash
.text:0048485D jnz short _Nmi_DoCrash ; But can we reach here? hmm..
.text:0048485D
.text:0048485F
.text:0048485F Nmi_Magic_Handled:
.text:0048485F mov eax, ds:0FFDFF040h ; eax= _KTSS
.text:00484864 cmp word ptr [eax], 58h ; if KGDT_NMI_TSS(58h), Tss error,just crash
.text:00484868 jz short _Nmi_DoCrash
.text:00484868
.text:0048486A add esp, 8Ch ; Nmi handled,so let's restore
.text:0048486A
.text:00484870 pop dword ptr ds:0FFDFF040h ; restore PcTss
.text:00484876 mov ecx, ds:0FFDFF03Ch ; mov ecx, PCR[PcGdt]
.text:0048487C lea eax, [ecx+28h] ; lea eax, [ecx] + KGDT_TSS
.text:0048487F mov byte ptr [eax+5], 8Bh ; 32bit, dpl=0, present, TSS32, *busy*
.text:00484883 pushf
.text:00484884 or [esp+4+var_4], 4000h ; Set Nested Task bit in EFLAGS
.text:0048488B popf ; then iretd will start a tast switch
.text:0048488C iret
.text:0048488C
.text:0048488D ; ---------------------------------------------------------------------------
.text:0048488D jmp _KiTrap02 ; <--------!!!!!!!!!!!!!!!!!!!!!
.text:00484892
.text:00484892 _Nmi_DoCrash:
.text:00484892 mov eax, 2
.text:00484897 jmp _KiSystemFatalException ; Crash!! KeBugCheck2(UNEXPECTED_KERNEL_MODE_TRAP)
.text:00484897
.text:00484897 _KiTrap02 endp

大 致的流程是更新TSS,屏蔽中断响应,保存CPU状态,调用用户注册的NMI处理例程;如果不能处理,则交给默认的Nmi Handler,也就是HalHandleNMI(x),但是它并不能做什么实质性的解决工作,只是准备BugCheck...在XP下,NMI一旦触发,都是交给HalHandleNMI,然后直接crash掉系统.Win2003下提供了一个很好的机制KeRegisterNmiCallback, 你可以注册Nmi的处理例程,这样发生Nmi中断的时候,你可以进行一些收尾工作,但是注意在回调函数里有很多的限制,不能有系统函数调用,不能去获取 SpinLock,必须使用Interlocked系列进行数据操作等等..

函数原型:
PVOID
KeRegisterNmiCallback(
PNMI_CALLBACK CallbackRoutine,
PVOID Context
);

还原出来的源代码如下:

static PVOID gKiNmiCallbackListHead= NULL;
KSPIN_LOCK KiNmiCallbackListLock;
typedef struct _KNMI_CALLBACK_RECORD
{
struct _KNMI_CALLBACK_RECORD *PreCallBackRecord;
PNMI_CALLBACK CallbackRoutine;
PVOID Context;
_KNMI_CALLBACK_RECORD *CurCallBackRecord;
} KNMI_CALLBACK_RECORD, *PKNMI_CALLBACK_RECORD;

以上是全局变量和结构体的声明.

PVOID _KeRegisterNmiCallback( IN PNMI_CALLBACK CallbackRoutine,
IN OPTIONAL PVOID Context )
{
PVOID result;
PKNMI_CALLBACK_RECORD pKNmiCallbackRecord;
KIRQL OldIrql;
result = ExAllocatePoolWithTag(0, 16, 'IMNK');
pKNmiCallbackRecord = (PKNMI_CALLBACK_RECORD)result;
if ( result )
{
pKNmiCallbackRecord->CallbackRoutine = CallbackRoutine;
pKNmiCallbackRecord->Context = Context;
pKNmiCallbackRecord->CurCallBackRecord = result;
OldIrql = KfAcquireSpinLock(&KiNmiCallbackListLock);
pKNmiCallbackRecord->PreCallBackRecord = gKiNmiCallbackListHead;
InterlockedCompareExchange( (PLONG)&gKiNmiCallbackListHead, (LONG)pKNmiCallbackRecord,(LONG)gKiNmiCallbackListHead );
KfReleaseSpinLock(&KiNmiCallbackListLock, OldIrql);
result = pKNmiCallbackRecord->CurCallBackRecord;
}
return result;
}
NTSTATUS _KeDeregisterNmiCallback(PVOID Handle)
{
KIRQL OldIrql;
PKNMI_CALLBACK_RECORD *tmpCallBackRecord;
PKNMI_CALLBACK_RECORD CurCallBackRecord;
NTSTATUS result;

CurCallBackRecord = (PKNMI_CALLBACK_RECORD)gKiNmiCallbackListHead;
OldIrql = KfAcquireSpinLock(&KiNmiCallbackListLock);
tmpCallBackRecord = &(PKNMI_CALLBACK_RECORD)gKiNmiCallbackListHead;

if ( !gKiNmiCallbackListHead )
{
KfReleaseSpinLock(&KiNmiCallbackListLock, OldIrql);
result = 0xC0000008; return result;
}
do
{
if ( (ULONG)(CurCallBackRecord->CurCallBackRecord) == (ULONG)Handle )
break;
tmpCallBackRecord = (PKNMI_CALLBACK_RECORD*)CurCallBackRecord;
CurCallBackRecord = *(PKNMI_CALLBACK_RECORD*)CurCallBackRecord;
}
while ( CurCallBackRecord );
if ( CurCallBackRecord && (ULONG)(CurCallBackRecord->CurCallBackRecord) == (ULONG)Handle )
{
*tmpCallBackRecord = *(PKNMI_CALLBACK_RECORD*)CurCallBackRecord;
KfReleaseSpinLock(&KiNmiCallbackListLock, OldIrql);
ExFreePoolWithTag(CurCallBackRecord, 'IMNK'); result = 0;
}
else
{
KfReleaseSpinLock(&KiNmiCallbackListLock, OldIrql); result = 0xC0000008;
}
return result;
}

其中,gKiNmiCallbackListHead是系统的一个全局变量,它指向最后注册的CallBackRecord,以反向的链表把所有的CallBack连接起来.正如MSDN中描述的:
When a nonmaskable interrupt occurs, the system calls each registered callback in reverse order from the order in which they were registered.
For the first callback, the system passes FALSE as the Handled parameter. For each subsequent callback, if any previous callback returned TRUE, the system passes TRUE as the Handled parameter, otherwise it passes FALSE.
If any callback returns a value of TRUE, the system considers the interrupt to have been handled.
Otherwise, the system calls the HAL's default handler for the interrupt, which normally causes the system to bug check.

正如前面的反汇编代码,NMI处理的流程大致为KiSystemStartup -> _KiTrap02 -> KiHandleNmi -> HalHandleNMI.把其他两个函数的代码也还原如下:

BOOLEAN _KiHandleNmi()
{
PKNMI_CALLBACK_RECORD CurCallBackRecord;
BOOLEAN nmiHandled;
CurCallBackRecord = gKiNmiCallbackListHead;
nmiHandled = FALSE;
while ( CurCallBackRecord )
{
nmiHandled = (CurCallBackRecord->CallbackRoutine)(CurCallBackRecord->Context, nmiHandled) | nmiHandled;
CurCallBackRecord = CurCallBackRecord->PreCallBackRecord;
}
return nmiHandled;
}

HalHandleNMI比较长,加了点注释:

void _HalHandleNMI(PVOID NmiInfo)
{
UCHAR EISAExNmiStatus;
UCHAR PortNmiStatus;
UCHAR EisaPort;
PUCHAR Port;
UCHAR EisaNMIMsgBuff[28];
ULONG i;

HalpNMIInProgress = 1;
HalpDoingCrashDump = 1;
EisaPort = READ_PORT_UCHAR((PUCHAR)0x61); // 0x61 -> SYSTEM_CONTROL_PORT_B

if ( (unsigned __int8)InbvIsBootDriverInstalled() )
{
InbvAcquireDisplayOwnership();
InbvResetDisplay();
InbvSolidColorFill(0, 0, 639, 479, 4);
InbvSetTextColor(15); InbvInstallDisplayStringFilter(0);
InbvEnableDisplayString(1); InbvSetScrollRegion(0, 0, 639, 479);
}

HalDisplayString("/n*** Hardware Malfunction/n/n");
HalDisplayString("Call your hardware vendor for support/n/n");
if ( EisaPort & 0x80 )
HalDisplayString("NMI: Parity Check / Memory Parity Error/n");

if ( EisaPort & 0x40 )
HalDisplayString("NMI: Channel Check / IOCHK/n");

if ( HalpBusType == 1 ) // #define MACHINE_TYPE_EISA 1
{
EISAExNmiStatus = READ_PORT_UCHAR((PUCHAR)0x461); // 0x461 -> EISA_EXTENDED_NMI_STATUS
EisaPort = EISAExNmiStatus;
if ( (char)EISAExNmiStatus < 0 )
HalDisplayString("NMI: Fail-safe timer/n");
if ( EisaPort & 0x40 )
HalDisplayString("NMI: Bus Timeout/n");
if ( EisaPort & 0x20 )
HalDisplayString("NMI: Software NMI generated/n");
EisaPort = 1;
port = (EisaPort << 12) + 0xC80;
do {
WRITE_PORT_UCHAR(Port, 0xFF);
if ( (char)READ_PORT_UCHAR(Port) >= 0 )
{
PortNmiStatus = READ_PORT_UCHAR(Port + 4);
if ( PortNmiStatus & 0x2 )
{
if ( PortNmiStatus != 0xFFFFFFFF )
{ // EisaNMIMsg[]: "NMI: Eisa IOCHKERR board %/n"
memcpy(EisaNMIMsgBuff, &EisaNMIMsg, sizeof(EisaNMIMsgBuff));
i = 0;
if ( EisaNMIMsgBuff )
{
while ( EisaNMIMsgBuff != '%' )
{
++i;
if ( !EisaNMIMsgBuff )
goto DisplayMsg;
}
EisaNMIMsgBuff = (EisaPort > 9 ? 'A'-10 : '0') + EisaPort;
}
DisplayMsg: HalDisplayString(EisaNMIMsgBuff);
}
}
}
++EisaPort; Port += 0x1000;
} while ( EisaPort <= 0xF );
}
HalDisplayString("/n*** The system has halted ***/n");
if ( HalpNMIDumpFlag ) //Registry/Machine/System/CurrentControlSet/Control/CrashControl
KeBugCheckEx(0x80, 'ODT', 0, 0, 0);
if ( !KdDebuggerNotPresent )
{
if ( KdDebuggerEnabled )
KeEnterKernelDebugger();
} while ( 1 );
}

有 意思的是HalpNMIDumpFlag这个全局标志,它决定了NMI触发而且要Crash系统的时候是否生成 dump[3].其实就是注册表的这个位置:HKEY_LOCAL_MACHINE/System/CurrentControlSet/Control /CrashControl,CrashDumpEnabled设置为2,NMICrashDump设置为1.这个可以为服务器生成dump以调试,比如 DELL PE2850,设置好BISO允许使用NMI Button后,你只要按下机器上的NMI按钮就可以了.

todo...
排版太难受,要详细写的也搞的不想写了,以后再更新:)
________________________________________
[1] http://en.wikipedia.org/wiki/Interrupt_descriptor_table
http://wiki.osdev.org/GDT_Tutorial
[2] http://advdbg.org/blogs/advdbg_system/articles/409.aspx
[3] http://www.microsoft.com/whdc/system/sysinternals/dmpsw.mspx
[4] Nice Links:
http://bbs.pediy.com/showthread.php?p=417908
Windows 开机BOOT绘图 背景介绍        这次说的开机BOOT绘图,是在哪个阶段呢?时间起点:Windows内核正在初始化,内建驱动初始化完毕。时间终点:会话管理器加载win32.sys,启动winlogon进入系统。 给两个图: 前者是Windows自身的开机LOGO图,后者是运行时机是在autochk.exe运行的时候,江民在这里类似的做了自己的应用BootScan。把应用写在这里 阅读详情

相关推荐

DeepSeek + 番茄小说:AI辅助小说开发最全指南

DeepSeek与番茄小说合作推出AI辅助小说创作系统,通过DeepSeek的文本生成能力(包括情节构建、角色塑造等)结合番茄小说的市场数据,为作者提供从创意到发布的全流程支持。系统包含智能创意生成器、角色设计、章节自动生成等核心模块,采用数据驱动优化策略,支持128K长文本处理和多风格模仿。开发架构采用Python+FastAPI技术栈,提供分层审核机制确保质量。该系统可帮助作者突破创作瓶颈,提升效率,同时提供订阅制、内容分成等多元变现模式。需注意版权问题和原创性保证。

weixin_41445009的博客 1683

Windows系统通用回调

本文将针对Windows操作系统所提供系统回调操作做逐一分析,以了解每个回调所能达成什么样的功能及效果。比如部分回调只是用于通知,我们只能通过此回调来了解某一类型事件的发生,只能做审计,拦截动作需要通过其它途径实现;也有回调可以实现即时拦截,通过回调即可阻止恶意程序的攻击企图。 Windows系统在内核及应用层均有提供回调机制,下面分为两部分作介绍: 1 内核回调 内核回调机制 OS版本 功能描述 备注 PsSetCreateProcessNotifyRoutine ...

whl0071的专栏 2697

大数据审计常用的算法模型

数据预处理:用聚类算法(K-Means)对数据分组,缩小分析范围;异常初筛:用孤立森林/DBSCAN快速标记疑似异常样本;风险分级:用随机森林/XGBoost对疑似异常打分,确定核查优先级;关联深挖:用FP-Growth挖掘异常样本背后的人员/流程关联;文本佐证:用BERT分析相关合同/审批文本,获取舞弊证据。例如,某审计项目通过“K-Means分组→孤立森林标异常→FP-Growth找关联→BERT审合同”的流程,仅用1周就完成了过去3个月需10人团队的工作量,且挖出2起隐藏的串标舞弊案。

Yulay专栏 1069

内核回调函数

Kernel Callback Functions

maomao171314的专栏 1141

海思SD3403/SS928开发(二)环境搭建:从零到一构建高效开发环境的实战指南

本文详细介绍了海思SD3403/SS928开发板环境搭建的全流程,包括Ubuntu系统选择、基础配置、开发环境依赖安装、SDK工具链配置等关键步骤。通过实战经验分享,帮助开发者避免常见问题,快速构建高效开发环境,提升开发效率。

weixin_28717683的博客 286

【AI赋能】Sora + CapCut 打造爆款短视频:从脚本到发布的自动化工作流

本文详细介绍了如何利用Sora、Grok和CapCut等AI工具构建自动化短视频生产工作流。通过结构化Prompt生成专业脚本,借助Sora将文字转化为高质量视频素材,并利用CapCut进行高效剪辑与合成,实现从创意到发布的十倍效率提升,助力个人及团队打造爆款内容。

g8f9d0s1a2的博客 500

Linux内核深入理解中断和异常(4):不可屏蔽中断NMI、浮点异常和SIMD

Linux内核深入理解中断和异常(4):不可屏蔽中断NMI、浮点异常和SIMD rtoax 2021年3月 本文介绍一下几种trap: //* External hardware asserts (外部设备断言)the non-maskable interrupt [pin] on the CPU. //* The processor receives a message on the system bus or the APIC serial bus with a delivery mode `N

RToax 4704

蓝牙Mesh安全机制全解析:如何防止你的智能门锁被黑客控制?

本文深入解析蓝牙Mesh安全机制,重点探讨如何保护智能门锁免受黑客攻击。详细介绍了蓝牙Mesh的多层次安全架构,包括密钥管理体系、设备入网流程及实战防护策略,帮助用户构建更安全的智能家居网络。

h3i4j的博客 732

读书之《windows2000内部揭秘》--第3章-系统机制

第3章 系统机制 Microsoft windows2000 提供了诸如执行程序、内核以及设备驱动程序等内核模式组件所使用的一些基本机制。本章将对以下系统机制进行解释并讲述他们的使用方法:    ♦陷阱调度,包括中断、延迟过程调用(DCP)、异步过程调用(ACP)、异常调度和系统服务调度。    ♦执行程序对象管理器。    ♦同步,包括自旋锁、内核调度程序对象和等待的实现方式。

离剑式的个人空间 1013

2025.06.25【微生物】|Emu工具安装与使用详解:全长16S扩增子微生物丰度分析

Emu是一款基于贝叶斯方法的微生物多样性分析工具,专为处理高错误率的长读长16S/18S扩增子数据优化。本文详细介绍了Emu的安装与使用方法:1)通过conda创建独立环境并安装Emu;2)提供SILVA等预构建数据库的下载指南,并说明自定义数据库的构建方法;3)演示单样本和多样本分析流程,解读关键输出文件;4)介绍丰度表合并、分类等级汇总等实用功能扩展;5)建议后续分析方向。文章还附有官方GitHub链接和相关文献资源,为微生物群落结构研究提供了一套完整的分析解决方案。

1256

29、几何形态测量学在蝴蝶翅膀研究中的应用

本博客介绍了几何形态测量学在花斑木蛱蝶(Pararge aegeria)翅膀研究中的应用,探讨了环境因素对蝴蝶翅膀大小和形状的影响。通过选择合适的地标并采用Procrustes分析方法,研究揭示了不同景观类型下翅膀形状的差异及其潜在的适应性意义。文章还讨论了发育可塑性在飞行形态变异中的作用,并比较了几何形态测量学与其他传统方法的优势。最后提出了未来研究方向,包括多因素综合分析、发育机制探索及长期生态监测,为理解蝴蝶的适应性进化提供了重要线索。

ff678的博客 109

ntifs.h

// ntifs.h// http://www.acc.umu.se/~bosse/ntifs.html/*This is a free version of the file ntifs.h, release 56.The purpose of this include file is to build file system andfile system filter drivers for

zzz3265的专栏 1万+

发那科 Fanuc PLC 报文通讯,报文解析。附源程序

发那科 Fanuc PLC 报文通讯,最详细的发那科 PLC报文解析,不再局限在官方提供的库,也不用再局限在windows平台。

ITOfDragon的博客 1749

庖丁解 vmCore · 第六篇 · 众生之相:进程与线程的生死现场

解牛者,刀游骨缝,非割肉也。 读进程者,眼见状态,非枚举字段也。

深耕底层运维,沉淀实战干货,分享技术经验。 486

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

鸿蒙轻内核M核的故障管家:Fault异常处理

摘要:本文先简单介绍下Fault异常类型,向量表及其代码,异常处理C语言程序,然后详细分析下异常处理汇编函数实现代码。

华为云官方博客 2423

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

_KiSystemServiceRepeat函数在_KiSystemService 函数里面

_KiSystemServiceRepeat: _KiServiceExit2 是两个非常重要的符号。

linux-0.11调试教程 1183

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

系统蓝屏前后所做的事情

By jingzhongrong下述过程出自《Windows File System Internals》 首先,关闭所有中断请求其他的用户节点(在多处理器系统中)停止当前的操作使用HalDisplayString() 函数答应出错信息在屏幕上出现蓝色背景的提示,包括BugCheckCode等信息(包括KeBugCheckEx的其他可选参数)如果有描述BugChec

jingzhongrong 1289

#### golang channel的结构 ####

前言 Golang在并发编程上有两大利器,分别是channel和goroutine,这篇文章我们先聊聊channel。熟悉Golang的人都知道一句名言:“使用通信来共享内存,而不是通过共享内存来通信”。这句话有两层意思,Go语言确实在sync包中提供了传统的锁机制,但更推荐使用channel来解决并发问题。这篇文章会先从channel的用法、channel的原理两部分对channel做一个较为深入的探究。 channel用法 什么是channel 从字面上看,channel的意思大概就是管道的意.

仅做个人笔记 2万+

NMI 代码实现步骤

# Copyright (c)2018, 东北大学软件学院学生 # All rightsreserved # 文件名称:nmi.py # 作 者:孔云 #问题描述:计算算法聚类结果与ground truth的相似度 # coding:utf-8 #A是标准聚类结果,B是算法聚类结果 from sklearn import metrics import numpy as np def ope...

u012369559的专栏 5822

IDT系列:(一)初探IDT,Interrupt Descriptor Table,中断描述符表

IDT,Interrupt Descriptor Table,中断描述符表是CPU用来处理中断和程序异常的。一、有关IDT的基本知识1、中断时一种机制,用来处理硬件需要向CPU输入信息的情况。 比如鼠标,键盘等。2、中断和异常的产生是随机的,在CPU正常运行过程中随时可能产生。CPU的中断处理机制3、中断可以由硬件产生(称为外部中断),也可以由软件产生(称为内部中断),在程序中写入int n指令可以产生n号中断和异常(n从0-ffh)。4、同时CPU的中断异常机制还是重要特性的支持原理,比如程序调试,程序运

fwqcuc的专栏 2万+

解析kernel 2.6.24使用NMI中断对Hard lock的处理

本文针对x86架构linux kernel检测hard lock的方法进行了分析。基于kernel2.6.24源码解析了从NMI中断触发到oops发生的处理流程。其使用时钟计数器判断是否发生hard lock。

zhaojia的博客 1997

[windows内核]中断与异常处理

详细解释在手册中的: CHAPTER 6 INTERRUPT AND EXCEPTION HANDLING 一、中断 描述: 中断通常是由CPU外部的输入输出设备(硬件)所触发的,供外部设备通知CPU“有事情需要处理”,因此又叫中断请求(Interrupt Request) 中断请求的目的是希望CPU暂时停止执行当前正在执行的程序,转去执行中断请求所对应的中断处理例程(中断处理程序在哪有IDT表决定) 80x86有两条中断请求线: 可屏蔽中断线,称为INTR(Interrupt Requi

r250414958的博客 2795

上下文保存 中断_从操作系统(Windows)的角度讨论中断和异常机制

本章的内容是针对Windows XP的32位版本的,但是绝大多数内容也适用于Windows的其他32位版本(Windows NT、Windows 2000和Windows Vista),并且可以比较容易地推广到64位版本的Windows系统。11.1 中断描述符表在保护模式下,当有中断或异常发生时,CPU是通过中断描述符表(Interrupt Descriptor Table,IDT)来寻找处理函...

weixin_35741812的博客 766
上一篇: About the SMM rootkit
下一篇: 一个向别的进程插入dll的代码
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值