本文中,我想介紹一種非常有趣的尾部掛鉤方法,該方法在2015年的REcon會議上由Alex提出。
尾部繞過技術允許後處理,這對於在原始流程執行完畢之後對輸出引數進行過濾十分有用。
這並不是一種新的技術,但我認為,這種技術及其我試驗過的所有會引發崩潰的POC程式碼都十分有趣。
我嘗試構建不會引發崩潰(至少對我而言)並且能夠正常結束的POC(順便提一句,它生成一個EXE檔案,而不是DLL)。
在KPROCESS結構的偏移地址0x2c8處,包含一個名為InstrumentationCallback的域(在Windbg偵錯程式中利用相應的命令能夠看到該域,具體如下所示):
0:000> dt _KPROCESS
ntdll!_KPROCESS
+0x000 Header : _DISPATCHER_HEADER
+0x018 ProfileListHead : _LIST_ENTRY
+0x028 DirectoryTableBase : Uint8B
+0x030 ThreadListHead : _LIST_ENTRY
+0x040 ProcessLock : Uint4B
+0x044 ProcessTimerDelay : Uint4B
+0x048 DeepFreezeStartTime : Uint8B
+0x050 Affinity : _KAFFINITY_EX
+0x0f8 ReadyListHead : _LIST_ENTRY
+0x108 SwapListEntry : _SINGLE_LIST_ENTRY
+0x110 ActiveProcessors : _KAFFINITY_EX
+0x1b8 AutoAlignment : Pos 0, 1 Bit
+0x1b8 DisableBoost : Pos 1, 1 Bit
+0x1b8 DisableQuantum : Pos 2, 1 Bit
+0x1b8 DeepFreeze : Pos 3, 1 Bit
+0x1b8 TimerVirtualization : Pos 4, 1 Bit
+0x1b8 CheckStackExtents : Pos 5, 1 Bit
+0x1b8 PpmPolicy : Pos 6, 3 Bit
+0x1b8 ActiveGroupsMask : Pos 9, 20 Bit
+0x1b8 ReservedFlags : Pos 29, 3 Bit
+0x1b8 ProcessFlags : Int4B
+0x1bc BasePriority : Char
+0x1bd QuantumReset : Char
+0x1be Visited : UChar
+0x1bf Flags : _KEXECUTE_OPTIONS
+0x1c0 ThreadSeed : [20] Uint4B
+0x210 IdealNode : [20] Uint2B
+0x238 IdealGlobalNode : Uint2B
+0x23a Spare1 : Uint2B
+0x23c StackCount : _KSTACK_COUNT
+0x240 ProcessListEntry : _LIST_ENTRY
+0x250 CycleTime : Uint8B
+0x258 ContextSwtiches : Uint8B
+0x260 SchedulingGroup : Ptr64 _KSCHEDULING_GROUP
+0x268 FreezeCount : Uint4B
+0x26c KernelTime : Uint4B
+0x270 UserTime : Uint4B
+0x274 ReadyTime : Uint4B
+0x278 Spare2 : [80] UChar
+0x2c8 InstrumentationCallback : Ptr64 Void <<< That’s it :)
+0x2d0 SecureState : <unnamed-tag>
在Windows系統Vista以及之後的版本中,你可以使用InstrumentationCallback域來指定回撥函式的地址,每次函式從核心態返回使用者態之後系統都會呼叫指定的回撥函式。
一種指定回撥函式的方法是使用驅動實現,但實踐證明還有一種更簡單的方式,即使用無需任何特殊許可權的使用者態API函式NtSetInformationProcess,我們只需要配置正確的結構體就夠了。具體程式碼如下所示:
PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION nirvana;
nirvana.Callback = (PVOID)(ULONG_PTR)medium; //callback function
nirvana.Reserved = 0; //always 0
nirvana.Version = 0; //0 for x64, 1 for x86
//just one call
NtSetInformationProcess(GetCurrentProcess(),
(PROCESS_INFORMATION_CLASS)ProcessInstrumentationCallback,
&nirvana,
sizeof(nirvana) /*0x08*/);
然而有個困難是,由於需要直接與暫存器打交道,我們必須使用匯編語言編寫回撥函式的程式碼。具體如下所示:
include ksamd64.inc
EXTERN hook:NEAR
.code
medium PROC
; 為什麼要壓入這些暫存器的值?請參考網址:
; https://docs.microsoft.com/en-us/cpp/build/caller-callee-saved-registers
push rax ; 返回值
push rcx
push rbx
push rbp
push rdi
push rsi
push rsp
push r12
push r13
push r14
push r15
; 沒有以下這條語句,程式將引發崩潰
; 其實一個小得多的值就足夠了,詳見網址:
; https://www.reddit.com/r/ReversEngineering/comments/7gpkw4/hooking_via_instrumentationcallback/
; P.S. 感謝反饋
sub rsp, 1000h
mov rdx, rax
mov rcx, r10
call hook
add rsp, 1000h
pop r15
pop r14
pop r13
pop r12
pop rsp
pop rsi
pop rdi
pop rbp
pop rbx
pop rcx
pop rax
jmp r10
medium ENDP
END
以上:) 程式碼成功執行於Windows 10 v1709 x64系統
你可以從如下網址獲取POC程式碼:
https://github.com/secrary/Hooking-via-InstrumentationCallback
資源:
·對Nirvana進行掛鉤(https://www.youtube.com/watch?v=bqU0y4FzvT0)
·Windows
x64系統的系統服務掛鉤技術和高階除錯(https://www.codeproject.com/Articles/543542/Windows-x-system-service-hooks-and-advanced-debug)
·反除錯技巧(https://pastebin.com/9TqRGsM5)
·Windows 10系統對Nirvana進行掛鉤操作簡介(https://sww-it.ru/2016-04-11/1332)
原文地址:https://secrary.com/Random/InstrumentationCallback/
本文由 看雪翻譯小組 木無聊偶 編譯
轉載請註明來源