inline hook 多核安全相关问题随记。。。

 

Intel CPU相关指令:
LOCK
这是一个指令前缀,在所对应的指令操作期间使此指令的目标操作数指定的存储区域锁定,以得到保护。
XADD
先交换两个操作数的值,再进行算术加法操作。多处理器安全,在80486及以上CPU中支持。
CMPXCHG
比较交换指令,第一操作数先和AL/AX/EAX比较,如果相等ZF置1,第二操作数赋给第一操作数,否则ZF清0,第一操作数赋给AL/AX/EAX。
多处理器安全,在80486及以上CPU中支持。
CMPXCHG8B
同上,64位比较交换指令,第二操作数隐含为EDX:EAX,比较EDX:EAX与64位的目标,如果相等则ECX:EBX送往目标且ZF置1,否则目标送EDX:EAX且ZF清0。

windows互锁API列表:
InterlockedCompareExchange/InterlockedCompareExchange64
InterlockedCompareExchangeAcquire/InterlockedCompareExchangeAcquire64
InterlockedCompareExchangePointer
InterlockedCompareExchangeRelease/InterlockedCompareExchangeRelease64
InterlockedDecrement/InterlockedDecrement64
InterlockedDecrementAcquire
InterlockedDecrementRelease
InterlockedExchange/InterlockedExchange64
InterlockedExchangeAcquire64
InterlockedExchangeAdd/InterlockedExchangeAdd64
InterlockedExchangePointer
InterlockedIncrement/InterlockedIncrement64
InterlockedIncrementAcquire
InterlockedIncrementRelease

  • 多处理器安全就是当某值被某处理器修改后,其他处理器应当知晓,而不再使用CPU缓存内的旧数据。
  • 本文不讨论WinNT3.51/Win95以及更早的操作系统(它们是为80386设计的),以及安装在80386上的Win98(Win98安装时自动判断是否支持XADD指令)。

看一个简单的函数,它的作用是将lpAddend加1,并返回之。
LONG InterlockedIncrement( LPLONG lpAddend )
{
    MOV           ecx, lpAddend
    MOV           eax, 1
    LOCK XADD     dword ptr [ecx], eax
    INC           eax
    RET           4
}

看一个复杂的函数,它的作用是将lValue赋值给*plTarget,并返回*plTarget的原先值。
LONG InterlockedExchange( LPLONG plTarget, LONG lValue )
{
    MOV           ecx, plTarget
    MOV           edx, lValue
    MOV           eax, dword ptr [ecx]
L:  LOCK CMPXCHG  dword ptr [ecx], edx
    JNE           L
    RET           8
}
看,不得不动用了一个循环指令。类似的还有InterlockedCompareExchange(pdestination,exchage,comperand)函数,如果Destination等于Comperand,将Exchange赋值给Destination,否则什么也不干,返回值为Destination的原先值。

循环技巧是很有用的,Jeffrey Richter给出了一个功能代码,如果值大于0,则加1,代码如下:
do {
    LONG lStartValue = *plTarget;
    LONG lNewValue = lStartValue + ((lStartValue > 0) ? 1 : 0);
} while( InterlockedCompareExchange(plTarget,lNewValue,lStartValue) != lStartValue );

一个未知的问题是,为什么在有的操作系统中没看到LOCK。未来的研究是线程/进程锁。

 
 
 
 

Windows貌似没有提供相关函数来处理hook代码时的多核同步问题,搜索相关帖子,找到一个通过获取用于线程调度自旋锁方式来达到这个目标;还有一种,是插dpc,让其他cpu空耗,这段期间完成代码替换。

感觉都不好用00!想起前一段时间看软件调试时提到了可以冻结其他cpu的函数KiSendFreeze(),能从多核模式变成单核模式。但不能逆向操作-不能再从单核模式恢复多核模式,也不好00!

既然这些主防在内核上下了这么多控制点,她们是怎么处理多核同步问题的。跟踪逆向了一下,

.text:00010DFC sub_10DFC       proc near               ; CODE XREF: sub_10E24+BE p
.text:00010DFC                                         ; sub_10E24+FB p ...
.text:00010DFC
.text:00010DFC var_4           = dword ptr -4
.text:00010DFC arg_0           = dword ptr 8
.text:00010DFC
.text:00010DFC                 mov     edi, edi
.text:00010DFE                 push    ebp
.text:00010DFF                 mov     ebp, esp
.text:00010E01                 push    ecx
.text:00010E02                 cli
.text:00010E03                 push    eax
.text:00010E04                 mov     eax, cr0
.text:00010E07                 mov     [ebp+var_4], eax
.text:00010E0A                 and     eax, 0FFFEFFFFh
.text:00010E0F                 mov     cr0, eax

.text:00010E12                 pop     eax
.text:00010E13                 mov     eax, [ebp+arg_0]
.text:00010E16                 mov     ecx, [ebp+var_4]
.text:00010E19                 mov     [eax], ecx
.text:00010E1B                 leave
.text:00010E1C                 retn    4
.text:00010E1C sub_10DFC       endp


.text:00010F1F                 call    sub_10DFC
.text:00010F24                 mov     ebx, dword_13464[ebx]
.text:00010F2A                 sub     edx, ebx
.text:00010F2C                 sub     edx, 4
.text:00010F2F                xchg    edx, [ebx]
.text:00010F31                 push    eax
.text:00010F32                 mov     eax, [ebp+var_20]
.text:00010F35                 mov     cr0, eax
.text:00010F38                 pop     eax
.text:00010F39                 sti
无非也是关中断,开启内存可写,然后一句xchg    edx, [ebx]搞定。她们HOOK的位置都是callxxxx中的xxxx这四个字节,一句xchg指令就可以搞定。由此来通过巧妙的选择hook位置及hook字节数绕过多核同步问题。

 
//还有一种搞法,可以hook任意字节数,在有本书上有介绍


#include "ntddk.h"

#include "gainexclusivity.h"

ULONG NumberOfRaisedCPU;

ULONG AllCPURaised;

PKDPC g_basePKDPC;

 

 

 

/****************************************************************************

*

*    FUNCTION:VOID RaiseCPUIrqlAndWait()

*

*    PURPOSE: The callback routine when a DPC is to be removed

*

****************************************************************************/

 

VOID RaiseCPUIrqlAndWait(

 IN PKDPC Dpc,

 IN PVOID DeferredContext,

 IN PVOID SystemArgument1,

 IN PVOID SystemArgument2

 )

{

InterlockedIncrement(&NumberOfRaisedCPU);

while (!InterlockedCompareExchange(&AllCPURaised, 1, 1))

{

__asm nop;

}

InterlockedDecrement(&NumberOfRaisedCPU);

}

 

/****************************************************************************

*

*    FUNCTION:NTSTATUS ReleaseExclusivity(PKDPC pKdpc)

*

*    PURPOSE: Release resources creates by GainExlusivity()

*

****************************************************************************/

 

void ReleaseExclusivity()

{

InterlockedIncrement(&AllCPURaised);

while (InterlockedCompareExchange(&NumberOfRaisedCPU, 0, 0))

{

__asm nop;

}

 

if (NULL != g_basePKDPC)

{

ExFreePool((PVOID)g_basePKDPC);

g_basePKDPC = NULL;

}

return;

}

 

/****************************************************************************

*

*    FUNCTION:PKDPC GainExlusivity(VOID)

*

*    PURPOSE: Call this function, before you want to modify EPROCESS or...

*

****************************************************************************/

 

BOOLEAN GainExlusivity(VOID)

{

NTSTATUS ntStatus;

ULONG u_currentCPU;

CCHAR i;

PKDPC pKdpc, temp_pkdpc;

 

if (DISPATCH_LEVEL != KeGetCurrentIrql())

{

return FALSE;

}

 

InterlockedAnd(&NumberOfRaisedCPU, 0);

InterlockedAnd(&AllCPURaised, 0);

 

temp_pkdpc = (PKDPC)ExAllocatePool(NonPagedPool, 

KeNumberProcessors * sizeof(KDPC));

if (NULL == temp_pkdpc)

{

return NULL;

}

g_basePKDPC = temp_pkdpc;

 

u_currentCPU = KeGetCurrentProcessorNumber();

for (i = 0; i < KeNumberProcessors; i++, *temp_pkdpc++)

{

if (i != u_currentCPU)

{

KeInitializeDpc(temp_pkdpc, RaiseCPUIrqlAndWait, NULL);

KeSetTargetProcessorDpc(temp_pkdpc, i);

KeInsertQueueDpc(temp_pkdpc, NULL, NULL);

}

}

 

while (KeNumberProcessors - 1 

!= InterlockedCompareExchange(&NumberOfRaisedCPU, 

KeNumberProcessors - 1, 

KeNumberProcessors - 1)

)

{

__asm nop;

}

 

return TRUE;

}


#define CLEAR_WP()                      \

    __asm   cli                         \

    __asm   mov     eax,cr0             \

    __asm   and     eax,not 000010000h  \

__asm   mov     cr0,eax

 

#define SET_WP()                        \

    __asm   mov     eax,cr0             \

    __asm   or      eax,000010000h      \

    __asm   mov     cr0,eax             \

__asm   sti

 



void __stdcall SetWp()

{

    SET_WP();

    ReleaseExclusivity();

}

void __stdcall ClearWp()

{

GainExlusivity();

    CLEAR_WP();

}

 

//在替换前后 调用SetWp() 和 ClearWp()就可


 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值