龙空技术网

一种尾部挂钩方法|利用 InstrumentationCallback 实现Hook

看雪学苑 117

前言:

而今我们对“push方法的返回值”可能比较关怀,同学们都需要分析一些“push方法的返回值”的相关内容。那么小编同时在网上网罗了一些对于“push方法的返回值””的相关文章,希望你们能喜欢,看官们快快来学习一下吧!

本文中,我想介绍一种非常有趣的尾部挂钩方法,该方法在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 :

在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

; 为什么要压入这些寄存器的值?请参考网址:

;

push rax ; 返回值

push rcx

push rbx

push rbp

push rdi

push rsi

push rsp

push r12

push r13

push r14

push r15

; 没有以下这条语句,程序将引发崩溃

; 其实一个小得多的值就足够了,详见网址:

;

; 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代码:

资源:

·对Nirvana进行挂钩()

·Windows x64系统的系统服务挂钩技术和高级调试()

·反调试技巧()

·Windows 10系统对Nirvana进行挂钩操作简介()

本文由 看雪翻译小组 木无聊偶 编译转载请注明来源

标签: #push方法的返回值