首页
论坛
课程
招聘
[调试逆向] [系统底层] [原创] InfinityHook 事件逆向
2021-2-28 17:39 4112

[调试逆向] [系统底层] [原创] InfinityHook 事件逆向

2021-2-28 17:39
4112

0x0 前言

看本文之前建议先看一下《剖析InfinityHook原理 掀起一场更激烈的攻与防恶战》这篇文章。

 

了解一下InfinityHook的基本原理。

0xI InfinityHook寻找指针方式解析

我们先来看看原作者是如何寻找的,在源码的IfhpInternalGetCpuClock函数内

1
2
PVOID* StackMax = (PVOID*)__readgsqword(OFFSET_KPCR_RSP_BASE);
PVOID* StackFrame = (PVOID*)_AddressOfReturnAddress();

StackFrame为栈顶,StackMax为栈底,

 

获取两个值是为了确定范围,要在这两个值范围之间寻找在堆栈中那个要替换的指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
for (PVOID* StackCurrent = StackMax;
        StackCurrent > StackFrame;
        --StackCurrent)
    {
        //
        // This is intentionally being read as 4-byte magic on an 8
        // byte aligned boundary.
        //
        PULONG AsUlong = (PULONG)StackCurrent;
        if (*AsUlong != INFINITYHOOK_MAGIC_1)
        {
            continue;
        }
        //
        // If the first magic is set, check for the second magic.
        //
        --StackCurrent;
 
        PUSHORT AsShort = (PUSHORT)StackCurrent;
        if (*AsShort != INFINITYHOOK_MAGIC_2)
        {
            continue;
        }

将StackMax栈底给StackCurrent再给AsUlong,用AsUlong地址里的值与INFINITYHOOK_MAGIC_1相比较,

 

不相等就回到for循环减一,相等就减1继续与INFINITYHOOK_MAGIC_2比较,不相等就回到for减一循环,

 

直到找到两个值,也就是说,从栈底往上一个个的比对值是否为INFINITYHOOK_MAGIC,直到找到两个值才继续。这两个值分别为 0x501802 和0xF33,这里记下两个值后面有提及。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for (;StackCurrent < StackMax;++StackCurrent)
        {
            PULONGLONG AsUlonglong = (PULONGLONG)StackCurrent;
            if (!(PAGE_ALIGN(*AsUlonglong) >= SystemCallEntryPage &&
                PAGE_ALIGN(*AsUlonglong) < (PVOID)((uintptr_t)SystemCallEntryPage + (PAGE_SIZE * 2))))
            {
                continue;
            }
            void** SystemCallFunction = &StackCurrent[9];           
            if (IfhpCallback)
            {
                IfhpCallback(SystemCallIndex, SystemCallFunction);
            }
 
            break;
        }

找到两个值的位置后,再从这个位置向下,寻找系统调用,找到后,以这个系统调用的位置为基准,

 

往后找9个,每个8字节,也就是偏移72的位置的值,这样这个函数指针就找到了。

0x2逆向HOOK系统调用事件

是不是看的一脸懵逼,没错我也是,为什么是那两个特定值,那两个值是怎么来的。

 

那篇文章没写 ,源码的注释也没说,那换个其他事件这个寻找方式还能用吗?

 

不能,我试过了。

 

这里就要理一理思路了,这两个值既然在堆栈中,那么不是当前函数搞进去的,就是前一个函数,

 

或者前前的函数.........., 那么这个函数就应该在调用栈上,那我们就下个断点,在windbg中看一下调用栈。

 

 

从调用栈分析, ntdll!NtCreateEvent+0xa进入系统调用,然后在nt!KiSystemServiceExit+0x26a进入etw的记录函数,那么关键就应该是PerfInfoLogSysCallEntry函数,通过函数头部的二进制,在ida里搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.text:0000000140162A40                 mov     r11, rsp
.text:0000000140162A43                 sub     rsp, 48h
.text:0000000140162A47                 lea     rax, [r11+8]
.text:0000000140162A4B                 mov     [r11+8], rcx
.text:0000000140162A4F                 lea     rcx, [r11-18h]
.text:0000000140162A53                 mov     [r11-18h], rax
.text:0000000140162A57                 and     [rsp+48h+var_C], 0
.text:0000000140162A5C                 mov     r9d, 0F33h
.text:0000000140162A62                 mov     edx, 1
.text:0000000140162A67                 mov     r8d, 40000040h
.text:0000000140162A6D                 mov     [rsp+48h+var_20], 501802h
.text:0000000140162A75                 and     qword ptr [r11-28h], 0
.text:0000000140162A7A                 mov     [rsp+48h+var_10], 8
.text:0000000140162A82                 call    EtwpTraceKernelEvent
.text:0000000140162A87                 add     rsp, 48h
.text:0000000140162A8B                 retn

PerfInfoLogSysCallEntry很短,看完就能发现两个熟悉的数字,

 

在0000000140162A5C 的 mov r9d, 0F33h

 

与0000000140162A6D mov [rsp+48h+var_20], 501802h

 

0F33与501802h不刚好是在堆栈里搜索的两个数,

 

这里又要理一理思路了,还记得前面说的吗,找到这两个值后,向下寻找系统调用,

 

我们都知道,堆栈是先push的在底下,后push的在上面,先下寻找意味着寻找之前的调用函数,

 

看调用栈之前的函数那就是KiSystemServiceExit。

1
2
void** SystemCallFunction = &StackCurrent[9];
            DbgBreakPoint();

写个断点,断下后查看StackCurrent,这个基准系统调用的值

1
2
3
4
5
6
7
8
9
kd> dq 0xfffff880`03b82b88
fffff880`03b82b88  fffff800`03ee0205 fffff960`00134164
fffff880`03b82b98  00000000`00000000 00000000`0000002a
fffff880`03b82ba8  fffff960`00133fd6 00000000`00000003
fffff880`03b82bb8  00000000`00000000 00000000`0192efb0
fffff880`03b82bc8  00000000`00000005 fffff960`00134164
fffff880`03b82bd8  fffff800`03edff93 fffffa80`31d69660
fffff880`03b82be8  fffffa80`31d63910 00000000`00000000
fffff880`03b82bf8  fffffa80`31d63910 00000000`00000000

StackCurrent为0xfffff880`03b82b88,dq查看存的是 fffff800 03ee0205

1
2
3
4
5
6
7
8
9
10
kd> u fffff800`03ee0205
nt!KiSystemServiceExit+0x26a:
fffff800`03ee0205 488b4c2420      mov     rcx,qword ptr [rsp+20h]
fffff800`03ee020a 488b542428      mov     rdx,qword ptr [rsp+28h]
fffff800`03ee020f 4c8b442430      mov     r8,qword ptr [rsp+30h]
fffff800`03ee0214 4c8b4c2438      mov     r9,qword ptr [rsp+38h]
fffff800`03ee0219 4c8b542440      mov     r10,qword ptr [rsp+40h]
fffff800`03ee021e 4883c450        add     rsp,50h
fffff800`03ee0222 41ffd2          call    r10
fffff800`03ee0225 488945b0        mov     qword ptr [rbp-50h],rax

u看下汇编,正好是KiSystemServiceExit,正如前面猜想的寻找KiSystemServiceExit

1
2
3
4
5
6
7
8
9
kd> dq 0xfffff880`03b82b88
fffff880`03b82b88  fffff800`03ee0205 fffff960`00134164
fffff880`03b82b98  00000000`00000000 00000000`0000002a
fffff880`03b82ba8  fffff960`00133fd6 00000000`00000003
fffff880`03b82bb8  00000000`00000000 00000000`0192efb0
fffff880`03b82bc8  00000000`00000005 fffff960`00134164
fffff880`03b82bd8  fffff800`03edff93 fffffa80`31d69660
fffff880`03b82be8  fffffa80`31d63910 00000000`00000000
fffff880`03b82bf8  fffffa80`31d63910 00000000`00000000

以这个基准偏移9个(不是9个字节,偏移一个指8字节,也就是偏移72的地方)的地方,为地址ffff880`03b82bd0,这地址里的值为fffff960 00134164,也就是要替换的函数指针。

1
2
3
4
5
6
7
8
9
10
kd> u fffff960`00134164
win32k!NtUserCallNoParam:
fffff960`00134164 48895c2408      mov     qword ptr [rsp+8],rbx
fffff960`00134169 57              push    rdi
fffff960`0013416a 4883ec20        sub     rsp,20h
fffff960`0013416e 8bf9            mov     edi,ecx
fffff960`00134170 488b0da1a72000  mov     rcx,qword ptr [win32k!gpresUser (fffff960`0033e918)]
fffff960`00134177 ff15f3d21c00    call    qword ptr [win32k!_imp_ExEnterPriorityRegionAndAcquireResourceExclusive (fffff960`00301470)]
fffff960`0013417d c605249e200001  mov     byte ptr [win32k!gbValidateHandleForIL (fffff960`0033dfa8)],1
fffff960`00134184 48890565c32000  mov     qword ptr [win32k!gptiCurrent (fffff960`003404f0)],rax

u看一下是什么函数,为win32k!NtUserCallNoParam:,这个地址为UserCallNoParam的开头

1
2
3
4
5
6
7
MyDriver2!IfhpInternalGetCpuClock+0x15e [c:\users\hasee\documents\visual studio 2013\projects\mydriver1\mydriver1\infinityhook.cpp @ 622]
nt!EtwpReserveTraceBuffer+0xe2
nt!EtwpLogKernelEvent+0x24d
nt!EtwpTraceKernelEvent+0xa6
nt!PerfInfoLogSysCallEntry+0x47
nt!KiSystemServiceExit+0x26a
user32!NtUserCallNoParam+0xa

调用栈的发起者为user32!NtUserCallNoParam+0xa,

 

我们都知道调用一个函数,都是push 参数1,push参数 2,push 参数3,然后call ,,

 

这时候堆栈里上面是返回地址,往下是参数,他以返回地址为基准向下加偏移寻找要替换的函数指针,

 

那会不会他寻找的就是参数,

1
2
                  : Args to Child        : Call Site
fffff880`03b82b40 : fffff960`00134164  : nt!PerfInfoLogSysCallEntry+0x47

windbg查看调用栈显示参数,

 

为了美观省略了后面的参数,参数为fffff960`00134164,这个不正好和寻找的那个一样

1
2
3
4
5
6
7
kd> dq fffff880`03b82b40
fffff880`03b82b40  fffffa80`31d69660 fffff880`03b82c60
fffff880`03b82b50  00000000`0192f250 00000000`00000001
fffff880`03b82b60  00000000`00000000 fffff800`00501802
fffff880`03b82b70  fffff880`03b82b90 00000000`00000008
fffff880`03b82b80  fffff880`03b82b88 fffff800`03ee0205
fffff880`03b82b90  fffff960`00134164 00000000`00000000

查看这个函数的堆栈,在fffff880 03b82b88是我们寻找的基准系统调用的位置

1
2
3
4
5
6
7
8
9
kd> dq 0xfffff880`03b82b88
fffff880`03b82b88  fffff800`03ee0205 fffff960`00134164
fffff880`03b82b98  00000000`00000000 00000000`0000002a
fffff880`03b82ba8  fffff960`00133fd6 00000000`00000003
fffff880`03b82bb8  00000000`00000000 00000000`0192efb0
fffff880`03b82bc8  00000000`00000005 fffff960`00134164
fffff880`03b82bd8  fffff800`03edff93 fffffa80`31d69660
fffff880`03b82be8  fffffa80`31d63910 00000000`00000000
fffff880`03b82bf8  fffffa80`31d63910 00000000`00000000

回过头来再以这个基准系统调用的位置,要替换的函数指针值为fffff960`00134164,

 

一处为偏移1的(偏移1不是指偏移一个字节,是8个字节)fffff880`03b82b90,

 

宁一处为偏移9的fffff880`03b82bd0,原作者替换的是偏移9处的函数指针。

 

总结一下:

 

作者通过寻找EtwPerfInfoLogSysCallEntry放入堆栈的特定值来寻找,

 

EtwPerfInfoLogSysCallEntry函数的堆栈位置,

 

再向下寻找系统调用找到

 

KiSystemServiceExit函数返回地址在堆栈中位置,

 

再通过以KiSystemServiceExit为基准,替换偏移9处的参数指针,达到hook的目的。

0x3 逆向其他事件

1 特定值的寻找

1
2
3
EVENT_TRACE_FLAG_PROCESS
0x00000001
Property->EnableFlags = 0x00000001;

通过官网查询PROCESS事件的falg为1,修改这个值就行了,

1
2
3
4
5
6
7
8
9
nt!EtwpReserveTraceBuffer+0xe2
nt!EtwpLogKernelEvent+0x122
nt!EtwpTraceKernelEvent+0xa6
nt! ?? ::NNGAKEGL::`string'+0x219f0
nt!PspExitProcess+0x4e
nt!PspExitThread+0x4e9
nt!NtTerminateProcess+0x138
nt!KiSystemServiceCopyEnd+0x13
ntdll!NtTerminateProcess+0xa

函数虽然变了,但在调用栈的大概位置没变,查看nt! ?? ::NNGAKEGL::`string'+0x21a03

1
2
3
4
5
6
7
8
fffff800`0412de2b 8b5504          mov     edx,dword ptr [rbp+4]
fffff800`0412de2e 488d8d90000000  lea     rcx,[rbp+90h]
fffff800`0412de35 450fb7cd        movzx   r9d,r13w
fffff800`0412de39 41b801000000    mov     r8d,1
fffff800`0412de3f c744242803195000 mov     dword ptr [rsp+28h],501903h
fffff800`0412de47 488364242000    and     qword ptr [rsp+20h],0
fffff800`0412de4d e8bedae8ff      call    nt!EtwpTraceKernelEvent (fffff800`03fbb910)
fffff800`0412de52 488d4d20        lea     rcx,[rbp+20h]

可以在fffff800`0412de3f看见501903h,宁一个特定值不好从代码看,

 

我们在 mov dword ptr [rsp+28h],501903h在这段系统代码下个断点

 

断下后记下rsp+28的值,等断在我们的代码后再dq查看内存偏移1处的后三位,经过多次测试,

 

第二个值为8d0 。

2寻找地址猜想

1
2
3
4
5
6
7
8
9
nt!EtwpReserveTraceBuffer+0xe2
nt!EtwpLogKernelEvent+0x122
nt!EtwpTraceKernelEvent+0xa6
nt! ?? ::NNGAKEGL::`string'+0x219f0
nt!PspExitProcess+0x4e
nt!PspExitThread+0x4e9
nt!NtTerminateProcess+0x138
nt!KiSystemServiceCopyEnd+0x13
ntdll!NtTerminateProcess+0xa

从调用栈看,函数指针应该是PspExitProcess的地址,但我找了半天,u查看没找到PspExitProcess函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
nt!PspExitProcess:
fffff800`04191288 48895c2408      mov     qword ptr [rsp+8],rbx
fffff800`0419128d 48896c2410      mov     qword ptr [rsp+10h],rbp
fffff800`04191292 4889742418      mov     qword ptr [rsp+18h],rsi
fffff800`04191297 57              push    rdi
fffff800`04191298 4154            push    r12
fffff800`0419129a 4155            push    r13
fffff800`0419129c 4156            push    r14
fffff800`0419129e 4157            push    r15
fffff800`041912a0 4883ec30        sub     rsp,30h
fffff800`041912a4 488bda          mov     rbx,rdx
fffff800`041912a7 448af9          mov     r15b,cl
fffff800`041912aa f0838a4004000004 lock or dword ptr [rdx+440h],4
fffff800`041912b2 65488b3c2588010000 mov   rdi,qword ptr gs:[188h]
fffff800`041912bb 4533ed          xor     r13d,r13d
fffff800`041912be 41be01000000    mov     r14d,1
fffff800`041912c4 413acd          cmp     cl,r13b
fffff800`041912c7 747c            je      nt!PspExitProcess+0xbd (fffff800`04191345)
fffff800`041912c9 ba02030000      mov     edx,302h
fffff800`041912ce 488bcb          mov     rcx,rbx
fffff800`041912d1 e842d6feff      call    nt!EtwTraceProcess (fffff800`0417e918)
fffff800`041912d6 66ff8fc4010000  dec     word ptr [rdi+1C4h]

看了一下PspExitProcess,在fffff800`041912d1 call nt!EtwTraceProcess,这里距离PspExitProcess开头非常近,基本上保存了参数,经过应该一个判断,就进入了etw。

 

而系统调用的事件,进入etw是在函数快结束的位置,返回就继续返回就结束了。

 

据此猜想,process事件的要替换的函数指针应该是PspExitProcess里call进etw时保存函数返回地址,

 

而不是想系统调用一样PspExitProcess的一个新开始地址指针。

 

如果是新的会有重入问题,而且既然在头部进入etw记录,

 

返回就继续执行就行了,没必要那么做,当然这不是百分之百的确定,要把PspExitProcess逆一遍,看看返回后是否真的在继续执行PspExitProcess,或者经过一个判断直接跳到末尾然后返回(当然在ida里看跳转图,不能一次到末尾),如果是返回继续执行,那么替换函数返回地址,自己构造一个fakePspExitProcess函数,应该也是可行的。

0x4结束

以上均为个人的逆向分析,如果有错误或是不同观点,欢迎指出。


[公告] 2021 KCTF 春季赛 防守方征题火热进行中!

收藏
点赞2
打赏
分享
最新回复 (11)
雪    币: 119
活跃值: 活跃值 (55)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kuaishou 活跃值 2021-2-28 19:36
2
0
学习一下
雪    币: 1407
活跃值: 活跃值 (954)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
奋进的小杨 活跃值 2021-2-28 22:49
3
0
学习一下
雪    币: 158
活跃值: 活跃值 (552)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
fengyunabc 活跃值 1 2021-3-1 00:39
4
0
感谢分享!
雪    币: 3001
活跃值: 活跃值 (926)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
淡然他徒弟 活跃值 2021-3-1 05:40
5
0
mark
雪    币: 1692
活跃值: 活跃值 (296)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
木志本柯 活跃值 2021-3-1 09:42
6
0
很好InfinityHookd的帖子再多一点,目前都是直接cv github上的源码希望有一天能彻底把源码都讲清楚。这要好过讲ssdt hook
雪    币: 2780
活跃值: 活跃值 (2757)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
学技术打豆豆 活跃值 1 2021-3-2 09:24
7
0
火哥的视频,有一节专门讲这个的,讲的非常详细
雪    币: 2188
活跃值: 活跃值 (1388)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
下咯 活跃值 2021-3-2 10:19
8
0
学技术打豆豆 火哥的视频,有一节专门讲这个的,讲的非常详细

有链接吗,没找到

最后于 2021-3-2 10:22 被下咯编辑 ,原因:
雪    币: 695
活跃值: 活跃值 (469)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
Oday小斯 活跃值 2021-3-2 10:33
9
0
感谢分享
雪    币: 193
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
ookkaa 活跃值 2021-3-2 17:35
10
0
PerfInfoLogSysCallEntry返回地址上面就是系统调用地址
雪    币: 6196
活跃值: 活跃值 (347)
能力值: ( LV5,RANK:68 )
在线值:
发帖
回帖
粉丝
万剑归宗 活跃值 1 2021-3-7 19:11
11
0
想什么呢,不讲SSDT,怎么在坛子里拿精(证)啊
雪    币: 2188
活跃值: 活跃值 (1388)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
下咯 活跃值 2021-3-7 20:11
12
0
万剑归宗 想什么呢,不讲SSDT,怎么在坛子里拿精(证)啊[em_39]
大佬这主页浏览量有点离谱,上次看到过万的还是坛主,帖子浏览量只有主页浏览量的一半
游客
登录 | 注册 方可回帖
返回