Win64 驱动内核编程-32.枚举与删除注册表回调
枚舉與刪除注冊表回調
????注冊表回調是一個監控注冊表讀寫的回調,它的效果非常明顯,一個回調能實現在SSDT?上?HOOK?十幾個?API?的效果。部分游戲保護還會在注冊表回調上做功夫,監控?service
鍵的子鍵,實現雙層攔截驅動加載(在映像回調那里還有一層)。而在卡巴斯基等?HIPS?類軟件里,則用來監控自啟動等鍵值。
? ? 注冊表回調在?XP?系統上貌似是一個數組,但是從?WINDOWS?2003?開始,就變成了一個鏈表。這個鏈表的頭稱為?CallbackListHead,可在?CmUnRegisterCallback?中找到:
? ? 搜索的過程跟尋找進程、線程、映像的數組類似,根據?lea?REG,XXX?來定位。不過為了更加精準,這次資料中是采用兩次?lea?REG,XXX?來定位:
?
ULONG64 FindCmpCallbackAfterXP() { ULONG64 uiAddress=0; PUCHAR pCheckArea=NULL, i=0, j=0, StartAddress=0, EndAddress=0; ULONG64 dwCheckAddr=0; UNICODE_STRING unstrFunc; UCHAR b1=0,b2=0,b3=0; ULONG templong=0,QuadPart=0xfffff800; RtlInitUnicodeString(&unstrFunc, L"CmUnRegisterCallback"); pCheckArea = (UCHAR*)MmGetSystemRoutineAddress (&unstrFunc) ; if (!pCheckArea) { KdPrint(("MmGetSystemRoutineAddress failed.")); return 0; } StartAddress = (PUCHAR)pCheckArea; EndAddress = (PUCHAR)pCheckArea + PAGE_SIZE; for(i=StartAddress;i<EndAddress;i++) { if( MmIsAddressValid(i) && MmIsAddressValid(i+1) && MmIsAddressValid(i+2) ) { b1=*i; b2=*(i+1); b3=*(i+2); if( b1==0x48 && b2==0x8d && b3==0x0d ) //488d0d(lea rcx,) { j=i-5; b1=*j; b2=*(j+1); b3=*(j+2); if( b1==0x48 && b2==0x8d && b3==0x54 ) //488d54(lea rdx,) { memcpy(&templong,i+3,4); uiAddress = MakeLong64ByLong32(templong) + (ULONGLONG)i + 7; return uiAddress; } } } } return 0; }?
?
?
????定位完畢之后,就是枚舉鏈表了。注冊表回調是一個“結構體鏈表”,類似于?EPROCESS,它的定義如下:
?
typedef struct _CM_NOTIFY_ENTRY { LIST_ENTRY ListEntryHead; ULONG UnKnown1; ULONG UnKnown2; LARGE_INTEGER Cookie; ULONG64 Context; ULONG64 Function; }CM_NOTIFY_ENTRY, *PCM_NOTIFY_ENTRY;
? ? 我們只關心兩個值,一個是?Cookie,一個是?Function。前者可以理解成注冊表回調的“句柄”(用?CmUnRegisterCallback?注銷回調傳入的就是這個?Cookie),后者是回調函數的地址。代碼如下:
?
?
ULONG CountCmpCallbackAfterXP(ULONG64* pPspLINotifyRoutine) { ULONG sum = 0; ULONG64 dwNotifyItemAddr; ULONG64* pNotifyFun; ULONG64* baseNotifyAddr; ULONG64 dwNotifyFun; LARGE_INTEGER cmpCookie; PLIST_ENTRY notifyList; PCM_NOTIFY_ENTRY notify; dwNotifyItemAddr = *pPspLINotifyRoutine; notifyList = (LIST_ENTRY *)dwNotifyItemAddr; do { notify = (CM_NOTIFY_ENTRY *)notifyList; if (MmIsAddressValid(notify)) { if (MmIsAddressValid((PVOID)(notify->Function)) && notify->Function > 0x8000000000000000) { DbgPrint("[CmCallback]Function=%p\tCookie=%p", (PVOID)(notify->Function),(PVOID)(notify->Cookie.QuadPart)); //notify->Function=(ULONG64)MyRegistryCallback; sum ++; } } notifyList = notifyList->Flink; }while ( notifyList != ((LIST_ENTRY*)(*pPspLINotifyRoutine)) ); return sum; }
執行效果(在有360的機器上執行的):
?
????
對付注冊表回調有三種方法:
1.直接使用CmUnRegisterCallback?把回調注銷;
2.把鏈表中記錄的回調地址修改為自定義的空函數的回調地址;
3.直接在目標回調地址上寫一個?RET,使其不執行任何代碼就返回。?第三種?方法?沒有針對性,可以用于對付?任何?回調函數。DisableFunctionWithReturnValue?用來對付有返回值的回調函數,DisableFunctionWithoutReturnValue?用于對付無返回值的回調函數。
?
/* 給函數頭寫RET廢除函數功能,這份代碼可以用于對抗任意回調函數,但僅限于WIN64系統。 DisableFunctionWithReturnValue用于有返回值的回調 DisableFunctionWithoutReturnValue用于無返回值的回調 */KIRQL WPOFFx64() { KIRQL irql=KeRaiseIrqlToDpcLevel(); UINT64 cr0=__readcr0(); cr0 &= 0xfffffffffffeffff; __writecr0(cr0); _disable(); return irql; }void WPONx64(KIRQL irql) { UINT64 cr0=__readcr0(); cr0 |= 0x10000; _enable(); __writecr0(cr0); KeLowerIrql(irql); }VOID DisableFunctionWithReturnValue(PVOID Address) { KIRQL irql; CHAR patchCode[] = "\x33\xC0\xC3"; //xor eax,eax + ret if(MmIsAddressValid(Address)) { irql=WPOFFx64(); memcpy(Address,patchCode,3); WPONx64(irql); } }VOID DisableFunctionWithoutReturnValue(PVOID Address) { KIRQL irql; if(MmIsAddressValid(Address)) { irql=WPOFFx64(); RtlFillMemory(Address,1,0xC3); WPONx64(irql); } }?
?
?
?
?
代碼備份:win7?X64
?
typedef struct _CM_NOTIFY_ENTRY { LIST_ENTRY ListEntryHead; ULONG UnKnown1; ULONG UnKnown2; LARGE_INTEGER Cookie; ULONG64 Context; ULONG64 Function; }CM_NOTIFY_ENTRY, *PCM_NOTIFY_ENTRY;LARGE_INTEGER Cookie; ULONG64 CmCallbackListHead=0;NTSTATUS MyRegistryCallback(IN PVOID CallbackContext,IN PVOID Argument1,IN PVOID Argument2) { return STATUS_SUCCESS; }ULONG CountCmpCallbackAfterXP(ULONG64* pPspLINotifyRoutine) { ULONG sum = 0; ULONG64 dwNotifyItemAddr; ULONG64* pNotifyFun; ULONG64* baseNotifyAddr; ULONG64 dwNotifyFun; LARGE_INTEGER cmpCookie; PLIST_ENTRY notifyList; PCM_NOTIFY_ENTRY notify; dwNotifyItemAddr = *pPspLINotifyRoutine; notifyList = (LIST_ENTRY *)dwNotifyItemAddr; do { notify = (CM_NOTIFY_ENTRY *)notifyList; if (MmIsAddressValid(notify)) { if (MmIsAddressValid((PVOID)(notify->Function)) && notify->Function > 0x8000000000000000) { DbgPrint("[CmCallback]Function=%p\tCookie=%p", (PVOID)(notify->Function),(PVOID)(notify->Cookie.QuadPart)); //notify->Function=(ULONG64)MyRegistryCallback; sum ++; } } notifyList = notifyList->Flink; }while ( notifyList != ((LIST_ENTRY*)(*pPspLINotifyRoutine)) ); return sum; }LONG64 MakeLong64ByLong32(LONG lng32) { LONG64 lng64=0; if(lng32>0) { lng64=(LONG64)lng32; } else { lng64=0xffffffffffffffff; memcpy(&lng64,&lng32,4); } return lng64; }ULONG64 FindCmpCallbackAfterXP() { ULONG64 uiAddress=0; PUCHAR pCheckArea=NULL, i=0, j=0, StartAddress=0, EndAddress=0; ULONG64 dwCheckAddr=0; UNICODE_STRING unstrFunc; UCHAR b1=0,b2=0,b3=0; ULONG templong=0,QuadPart=0xfffff800; RtlInitUnicodeString(&unstrFunc, L"CmUnRegisterCallback"); pCheckArea = (UCHAR*)MmGetSystemRoutineAddress (&unstrFunc) ; if (!pCheckArea) { KdPrint(("MmGetSystemRoutineAddress failed.")); return 0; } StartAddress = (PUCHAR)pCheckArea; EndAddress = (PUCHAR)pCheckArea + PAGE_SIZE; for(i=StartAddress;i<EndAddress;i++) { if( MmIsAddressValid(i) && MmIsAddressValid(i+1) && MmIsAddressValid(i+2) ) { b1=*i; b2=*(i+1); b3=*(i+2); if( b1==0x48 && b2==0x8d && b3==0x0d ) //488d0d(lea rcx,) { j=i-5; b1=*j; b2=*(j+1); b3=*(j+2); if( b1==0x48 && b2==0x8d && b3==0x54 ) //488d54(lea rdx,) { memcpy(&templong,i+3,4); uiAddress = MakeLong64ByLong32(templong) + (ULONGLONG)i + 7; return uiAddress; } } } } return 0; }void EnumCmCallback64() { //test to add my reg callback CmRegisterCallback(MyRegistryCallback, NULL, &Cookie); DbgPrint("[MY FUNCTION]: %p",(PVOID)MyRegistryCallback); DbgPrint("[MY COOKIE]: %p",(PVOID)Cookie.QuadPart); //get CmCallbackListHead address CmCallbackListHead=FindCmpCallbackAfterXP(); DbgPrint("CmCallbackListHead: %p",(PVOID)CmCallbackListHead); //enum callback address CountCmpCallbackAfterXP((ULONG64*)CmCallbackListHead); //unregister my callback CmUnRegisterCallback(Cookie); }/* 給函數頭寫RET廢除函數功能,這份代碼可以用于對抗任意回調函數,但僅限于WIN64系統。 DisableFunctionWithReturnValue用于有返回值的回調 DisableFunctionWithoutReturnValue用于無返回值的回調 */KIRQL WPOFFx64() { KIRQL irql=KeRaiseIrqlToDpcLevel(); UINT64 cr0=__readcr0(); cr0 &= 0xfffffffffffeffff; __writecr0(cr0); _disable(); return irql; }void WPONx64(KIRQL irql) { UINT64 cr0=__readcr0(); cr0 |= 0x10000; _enable(); __writecr0(cr0); KeLowerIrql(irql); }VOID DisableFunctionWithReturnValue(PVOID Address) { KIRQL irql; CHAR patchCode[] = "\x33\xC0\xC3"; //xor eax,eax + ret if(MmIsAddressValid(Address)) { irql=WPOFFx64(); memcpy(Address,patchCode,3); WPONx64(irql); } }VOID DisableFunctionWithoutReturnValue(PVOID Address) { KIRQL irql; if(MmIsAddressValid(Address)) { irql=WPOFFx64(); RtlFillMemory(Address,1,0xC3); WPONx64(irql); } }宋孖健,13
?
?
?
?
?
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的Win64 驱动内核编程-32.枚举与删除注册表回调的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Win64 驱动内核编程-31.枚举与删
- 下一篇: Win64 驱动内核编程-33.枚举与删