实现原理:
inlinehook内核文件ntosknl.exe的内部函数KeInitializeApc,对相关参数进行检测,若发现不符合规则的APC插入动作则进行拦截。
拦截实现方式:
将KAPC结构中的参数Thread修改成自身的某一KThread结构指针,达到"挂入A,实际却挂到B的效果",从而使该APC失去效用。
若关键函数sub_130A4返回为1,则修改KAPC结构中的Thread参数,进行拦截;若返回为0,则放行此次插入APC动作。(见红色代码部分)
InlineHook位置:
蓝色代码部分
检测规则:
1)进程自身内的APC全部予以放过
2)进程间的用户APC互插,仅允许system进程的孙子进程(即csrss.exe winlogin.exe)和某一特定用户进程
3)拦截非正规调用KeInitializeApc进行用户APC插入的动作
详见sub_130A4 函数分析,里面的函数一环套另一环,比较多,只帖出了主函数部分
代码:
nt!KeInitializeApc: ; mov edi,edi ; push ebp ; mov ebp,esp ; mov eax,dword ptr [ebp+8] ;[ebp+8]: Apc mov edx,dword ptr [ebp+10h] ;[ebp+10h]: TargetEnvironment cmp edx,2 ; mov ecx,dword ptr [ebp+0Ch] ;[ebp+0Ch]: Thread mov word ptr [eax],12h ;Apc->Type = 0x12 mov word ptr [eax+2],30h ;Apc->Size = 0x30 jne nt!KeInitializeApc+0x24 (804fd3c2) ;若TargetEnvironment != CurrentApcEnvironment则跳转 mov dl,byte ptr [ecx+165h] ;[ecx+165h]:Thread->ApcStateIndex,表示当前线程的环境值 --------------------------------------------------- nop ;inline Hook部分:仅对Apc->Thread进行了处理 call xyz12345+0x333c (ba27b33c) ;处理函数 --------------------------------------------------- mov dword ptr [eax+14h],ecx ; mov ecx,dword ptr [ebp+18h] ; mov byte ptr [eax+2Ch],dl ; mov dword ptr [eax+18h],ecx ; mov ecx,dword ptr [ebp+1Ch] ; xor edx,edx ; cmp ecx,edx ; mov dword ptr [eax+1Ch],ecx ; je nt!KeInitializeApc+0x50 (804fbaa6) ; mov cl,byte ptr [ebp+20h] ; mov byte ptr [eax+2Dh],cl ; --------------------------------------------------; 在进入处理函数之前,堆栈空间内容: |Context | |Mode | |NormalRoutine | |RundownRoutine | |KernelRoutine | |TargetEnvironment | |Thread | |Apc | |KeInitializeApc.Ret| |KeInitializeApc.Ebp| b2a7033c: ; mov edi, edi ; pusha ; pushf ; lock inc dword_16AC0 ;多核同步 mov eax, dword_16B44 ;dword_16B44:PsExitSpecialApc函数地址 test eax, eax ; jnz short loc_1338A ; mov ebx, [ebp+14h] ;[ebp+14h]:KernelRoutine mov ecx, [ebp+20h] ;[ebp+20h]:NormalRoutine mov edx, [ebp+10h] ;[ebp+10h]:TargetEnvironment mov esi, [ebp+0Ch] ;[ebp+0Ch]:Thread mov eax, Object ;Object: 进程的ETHREAD结构指针 cmp eax, esi ; jnz short loc_133B5 ;若当前线程不是Object代表的线程则跳转 test ecx, ecx ; jnz short loc_133B5 ;若NormalRoutine!= NULL 则跳转 test edx, edx ; jnz short loc_133B5 ;若TargetEnvironment != OriginalApcEnvironment则跳转 mov edi, dword_16A7C ;dword_16A7C: ntoskln.exe模块的Base cmp ebx, edi ; jb short loc_133B5 ;若KernelRoutine 低于dword_16A7C 则跳转 mov eax, dword_16AA0 ;dword_16AA0: ntoskln.exe模块的Size add edi, eax ; cmp ebx, edi ; jg short loc_133B5 ;若KernelRoutine 高于dword_16A7C + dword_16AA0 则跳转 mov dword_16B44, ebx ; jmp short loc_133B5 ; --------------------------------------------------; ; .text:0001338A loc_1338A: ; mov eax, [ebp+0Ch] ;[ebp+0Ch]:Thread push eax ; mov eax, [ebp+14h] ;[ebp+14h]:KernelRoutine push eax ; mov eax, [ebp+4] ;[ebp+4]: KeInitializeApc.Ret push eax ; call sub_130A4 ;关键函数 test al, al ; jz short loc_133B5 ; popf ; popa ; mov ecx, Object ; mov [eax+8], ecx ;Apc->Thread = Object mov ecx, [ebp+14h] ; lock dec dword_16AC0 ; retn ; ----------------------------- ; .text:000133B5 loc_133B5: ; popf ; popa ; mov [eax+8], ecx ; mov ecx, [ebp+14h] ; lock dec dword_16AC0 ; retn ;
代码:
char __stdcall sub_130A4(unsigned int a1, int a2, void *a3) //(KeInitializeApc.Ret, KernelRoutine, Thread) { ... v4 = PsGetCurrentProcessId();//获得当前进程的进程ID v21 = sub_126D2(v5);//由Ethread指针返回此线程所属进程的进程ID(非挂靠进程) if ( Object && !(unsigned __int8)sub_12CEC(v4) && v4 != (HANDLE)v21 && v21 != -1 ) //当前进程不是插入APC目标线程的所属进程,目标线程所属进程不是NtCurrentProcess(),当前进程不在自身的私有结构链表中 { if ( !(unsigned __int8)sub_12CEC((PVOID)v21) ) //若目标线程所属进程id不在私有结构链表中,则返回 return v3; v3 = 1; //捕获1)非正规调用KeInitializeApc 2)用户空间APC(用户空间APC的KernelRoutine为PsExitSpecialApc) if ( a1 <= dword_16A7C || a1 >= dword_16AA0 + dword_16A7C || a2 == dword_16B44 ) //a1 <= ntosknl.exe.Base || a1 >= ntosknl.exe.Base + ntosknl.exe.Size || a2 == PsExitSpecialApc { if ( a2 <= (unsigned int)dword_16A7C || a2 >= (unsigned int)(dword_16A7C + dword_16AA0) ) {//若a2不在ntosknl.exe模块势力范围,则打印可疑信息 if ( a2 != dword_16B44 ) goto LABEL_37; } else {//若a2在ntosknl.exe模块势力范围,但不等于PsExitSpecialApc则跳过此次处理 if ( a2 != dword_16B44 ) return 0; } v12 = KeGetCurrentThread(); if ( !(unsigned __int8)sub_12F12(v12) ) {//排除某一用户程序的插APC行为 if ( (_WORD)NtBuildNumber <= 3790 ) //windows xp 64-bit Edition Version 2003 { v14 = IoGetCurrentProcess(); v6 = sub_12660(v14);//返回当前进程的父进程的Eprocess结构 VirtualAddress = (PVOID)v6; if ( v6 ) { v7 = sub_12660((PVOID)v6);//返回当前进程的爷进程的Eprocess结构 VirtualAddress = (PVOID)v7; if ( v7 ) { v8 = sub_11944(v7);//获得爷进程ID if ( (_WORD)NtBuildNumber != 2195 ) //若系统版本不是 windows 2000 v9 = v8 == 4;//确认爷进程是否为4,即是否为 system进程 else v9 = v8 == 8; if ( v9 ) return 0; //若某个进程的爷进程为system进程,则允许Apc插入 } } } else { ...... v10 = sub_1268A(v5);//返回Thread所属进程的Eprocess结构指针 VirtualAddress = (PVOID)v10; if ( v10 ) { *(_DWORD *)(dword_1684C + v10) &= 0xFFFFFFF7;//dword_1684C = 0x248 //清除Eprocess.Flags的ProcessDelete位 *(_DWORD *)((char *)v5 + dword_16AE8) &= 0xFFFFFFFE;//dword_16AE8 = 0x248 //清除Ethread.CrossThreadFlags的Terminated位 } } v3 = 1; LABEL_37: ... return v3; } } return 0; } return 0; }