首页
论坛
课程
招聘
[翻译]利用KPROCESS结构的InstrumentationCallback域实现Hook
2017-12-12 22:40 6658

[翻译]利用KPROCESS结构的InstrumentationCallback域实现Hook

2017-12-12 22:40
6658

        本文中,我想介绍一种非常有趣的尾部挂钩方法,该方法在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/
本文由  看雪翻译小组 木无聊偶  编译
转载请注明来源

第五届安全开发者峰会(SDC 2021)10月23日上海召开!限时2.5折门票(含自助午餐1份)

收藏
点赞1
打赏
分享
最新回复 (22)
雪    币: 2347
活跃值: 活跃值 (41)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
空白即是正义 活跃值 2017-12-13 00:39
2
0
学习了
雪    币: 1764
活跃值: 活跃值 (125)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
木无聊偶 活跃值 4 2017-12-13 07:07
3
0
空白即是正义 学习了
共同学习
雪    币: 3559
活跃值: 活跃值 (300)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
放学打我不 活跃值 1 2017-12-13 10:38
4
0
这个用来注入dll好像不错
雪    币: 1764
活跃值: 活跃值 (125)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
木无聊偶 活跃值 4 2017-12-13 12:12
5
0
放学打我不 这个用来注入dll好像不错[em_19]
嗯呢,我翻译的时候也这么想来着,哈哈哈
雪    币: 21
活跃值: 活跃值 (229)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
靴子 活跃值 2017-12-13 19:10
6
0
win7测试了下  没成功  !  难道我得打开方式不对?
雪    币: 6817
活跃值: 活跃值 (153)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
聖blue 活跃值 2017-12-13 22:36
7
0
雪    币: 1764
活跃值: 活跃值 (125)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
木无聊偶 活跃值 4 2017-12-14 07:01
8
0
靴子 win7测试了下 没成功 ! 难道我得打开方式不对?
我看文章里说的是win10,要不你试试?
雪    币: 1764
活跃值: 活跃值 (125)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
木无聊偶 活跃值 4 2017-12-14 07:01
9
0
聖blue [em_35][em_66]
雪    币: 432
活跃值: 活跃值 (968)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
萌克力 活跃值 2017-12-14 07:54
10
0
的确特别骚..其实学习调试r0是非常有意思的事情  比R3有意思的太多了...
雪    币: 617
活跃值: 活跃值 (1083)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
库尔 活跃值 2017-12-14 10:12
11
0
mark一下
雪    币: 501
活跃值: 活跃值 (165)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
Justgoon 活跃值 1 2017-12-14 11:12
12
0
mark,win7  x64测试没成功,NtSetInformationProcess填充InstrumentationCallback失败,windbg里修改InstrumentationCallback到shellcode地址,下断点也没反应,估计还需要适配别的什么地方么
雪    币: 1764
活跃值: 活跃值 (125)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
木无聊偶 活跃值 4 2017-12-14 22:26
13
0
Justgoon mark,win7 x64测试没成功,NtSetInformationProcess填充InstrumentationCallback失败,windbg里修改InstrumentationCallba ...
我觉得还是先在作者环境相同的win10下测试成功,再在win7下尝试,也有可能对shellcode地址有一定范围要求,这个需要实际试一下~
雪    币: 1764
活跃值: 活跃值 (125)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
木无聊偶 活跃值 4 2017-12-14 22:27
14
0
萌克力 的确特别骚..其实学习调试r0是非常有意思的事情 比R3有意思的太多了...
嗯呢,想真正理解系统机制还是要深入进去看
雪    币: 6067
活跃值: 活跃值 (246)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kingswb 活跃值 2018-1-3 18:09
15
0
mark学习
雪    币: 55
活跃值: 活跃值 (76)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tsoo 活跃值 2018-1-6 22:25
16
0
"尾部绕过技术允许后处理,这对于在原始流程执行完毕之后对输出参数进行过滤十分有用。"

X64前4参数在寄存器里面,  在函数尾部挂钩也没法过滤输出参数.    只能过滤下返回值. 而NT函数返回值都是NTSTATUS.
雪    币: 1764
活跃值: 活跃值 (125)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
木无聊偶 活跃值 4 2018-1-8 10:18
17
0
tsoo "尾部绕过技术允许后处理,这对于在原始流程执行完毕之后对输出参数进行过滤十分有用。"X64前4参数在寄存器里面,&nbsp; 在函数尾部挂钩也没法过滤输出参数.&n ...
这里的输出参数我觉得是地址引用类型的参数,这种参数在x86和x64下都是可用的,另外我理解的是,可操作的项确实也包括返回值
雪    币: 388
活跃值: 活跃值 (387)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
wowocock 活跃值 1 2018-1-8 14:58
18
0
木无聊偶 我觉得还是先在作者环境相同的win10下测试成功,再在win7下尝试,也有可能对shellcode地址有一定范围要求,这个需要实际试一下~
win7下会返回STATUS_INFO_LENGTH_MISMATCH,自己逆NTOS后,可见长度为8
  if  (  (_DWORD)a4  !=  8  )
            return  0xC0000004;
typedef  struct  _PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION_W7
{
       PVOID  Callback;
}  PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION_W7,  *PPROCESS_INSTRUMENTATION_CALLBACK_INFORMATION_W7;
PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION_W7  nirvanaW7;
nirvanaW7.Callback  =  (PVOID)(ULONG_PTR)medium;
status  =  NtSetInformationProcess(
                       GetCurrentProcess(),
                       (PROCESS_INFORMATION_CLASS)ProcessInstrumentationCallback,
                       &nirvanaW7,
                       sizeof(nirvanaW7)  );
雪    币: 55
活跃值: 活跃值 (76)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tsoo 活跃值 2018-1-9 00:13
19
0
木无聊偶 这里的输出参数我觉得是地址引用类型的参数,这种参数在x86和x64下都是可用的,另外我理解的是,可操作的项确实也包括返回值
callback函数执行时机是在syscall  -->  ret  之间,  这个时候rcx,  rdx,  r8,r9  的值已经"丢"了,  即使有参数是引用类型,  在这个时机也无能为力了吧(因为没法知道当初传递的参数到底是啥) 

比如NtOpenProcess  ,  根据X64调用约定,  前四个参数在寄存器里面,  调用代码就类似这样:
mov  r9,.....
mov  r8,......
mov  rdx,.....
lea    rcx,  pHandle
call  NtOpenProcess

假设我想实现替换掉pHandle里面的进程句柄的目的(也就是在我的callback里面改掉  *phandle  ),    那就必须得知道当初call  openprocess的时候  rcx的值是多少,  但是在callback里面,  rcx,rdx的值已经变化了. 
貌似X64调用约定也没有规定函数ret的时候rcx,rdx...一定要跟当初call函数时一致.   

如果是32位,  在函数ret的时候,  参数还在栈里面,  的确能够过滤引用类的参数.  

目前个人感觉X64只能过滤到返回值, 除非有其他办法能得到当初RCX的值. 
当然能过滤返回值也不错了, 错误的返回值也能干扰到程序逻辑.
雪    币: 55
活跃值: 活跃值 (76)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tsoo 活跃值 2018-1-9 00:26
20
0
wowocock win7下会返回STATUS_INFO_LENGTH_MISMATCH,自己逆NTOS后,可见长度为8 if ( (_DWORD)a4 != 8 ) return 0xC000000 ...
大牛说得对,  WIN7上结构体里面只有一个  callback地址.

然后对于原生64进程,  我感觉的确是只能过滤到返回值,  如果要过滤输出参数,  那只能过滤到参数个数大于4个的NTAPI,  而且只能过滤到第5个及以后的参数.    大牛有高招没?

或者只勾特定点,  结合调用源的特征码,  强行还原rcx  ,rdx.
雪    币: 247
活跃值: 活跃值 (833)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
fengyunabc 活跃值 1 2018-5-18 19:39
21
0
感谢分享!
雪    币: 206
活跃值: 活跃值 (961)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
yy虫子yy 活跃值 2018-11-20 15:01
22
0
wowocock win7下会返回STATUS_INFO_LENGTH_MISMATCH,自己逆NTOS后,可见长度为8 if ( (_DWORD)a4 != 8 ) return 0xC000000 ...
32位模式sizeof(nirvanaW7)=4
64位模式sizeof(nirvanaW7)=8
所以NtSetInformationProcess和callback必须在64位模式下调用才对吧?
Win7成功了没?
雪    币: 295
活跃值: 活跃值 (605)
能力值: ( LV5,RANK:68 )
在线值:
发帖
回帖
粉丝
万剑归宗 活跃值 1 2018-11-20 15:33
23
0
yy虫子yy 32位模式sizeof(nirvanaW7)=4 64位模式sizeof(nirvanaW7)=8 所以NtSetInformationProcess和callback必须在64位模式下调用才对吧 ...
win7 下 只有 x64模式有效,
wow64无效。
游客
登录 | 注册 方可回帖
返回