最近在看HyperDbg这个项目,看到了一种基于VT的syscall挂钩方法,记录一下。EFER(Extended Feature Enable Register)是MSR寄存器的一种,相关功能可以在intel白皮书卷3A,第2.2.1节中找到。
EFER寄存器的SCE位用来控制对SYSCALL/SYSRET的支持。
在卷2B,第4.3节可以找到SYSCALL指令的操作
可以看出当EFER寄存器的SCE位为0时会产生#UD异常,如果我们手动将SCE清零,那么就会导致#UD异常,再通过设置VT中对#UD异常的响应事件就可以拦截SYSCALL。
用代码模拟SYSCALL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
_Use_decl_annotations_
BOOLEAN
SyscallHookHandleUD(PGUEST_REGS Regs, UINT32 CoreIndex)
{
CR3_TYPE GuestCr3;
UINT64 OriginalCr3;
UINT64 Rip;
VIRTUAL_MACHINE_STATE
*
CurrentVmState
=
&g_GuestState[CoreIndex];
/
/
/
/
Reading guest's RIP
/
/
__vmx_vmread(VMCS_GUEST_RIP, &Rip);
if
(g_IsUnsafeSyscallOrSysretHandling)
{
/
/
/
/
In some computers, we realized that safe accessing to memory
/
/
is
problematic. It means that our syscall approach might
not
/
/
working properly.
/
/
Based on our tests, we realized that system doesn't generate
/
/
regularly. So, we can imagine that only kernel addresses are SYSRET
/
/
instruction
and
SYSCALL
is
on a user
-
mode RIP.
/
/
It's faster than our safe methods but
if
the system generates a
/
/
then a BSOD will happen. But
if
the system
is
working regularly, then
/
/
no BSOD happens. For more information, see documentation at !syscall2
/
/
or
!sysret2 commands
/
/
if
(Rip &
0xff00000000000000
)
{
goto EmulateSYSRET;
}
else
{
goto EmulateSYSCALL;
}
}
else
{
/
/
/
/
Get the guest
's running process'
s cr3
/
/
GuestCr3.Flags
=
GetRunningCr3OnTargetProcess().Flags;
/
/
/
/
No, longer needs to be checked because we're sticking to system process
/
/
and
we have to change the cr3
/
/
/
/
if
((GuestCr3.Flags & PCID_MASK) !
=
PCID_NONE)
OriginalCr3
=
__readcr3();
__writecr3(GuestCr3.Flags);
/
/
/
/
Read the memory
/
/
UCHAR InstructionBuffer[
3
]
=
{
0
};
if
(MemoryMapperCheckIfPageIsPresentByCr3(Rip, GuestCr3))
{
/
/
/
/
The page
is
safe to read (present)
/
/
It's
not
necessary to use MemoryMapperReadMemorySafeOnTargetProcess
/
/
because we already switched to the process's cr3
/
/
MemoryMapperReadMemorySafe(Rip, InstructionBuffer,
3
);
}
else
{
/
/
/
/
The page
is
not
present, we have to inject a
/
/
CurrentVmState
-
>IncrementRip
=
FALSE;
/
/
/
/
For testing purpose
/
/
/
/
LogInfo(
"#PF Injected"
);
/
/
/
/
Inject
/
/
EventInjectPageFault(Rip);
/
/
/
/
We should
not
inject
/
/
return
FALSE;
}
__writecr3(OriginalCr3);
/
/
SYSCALL_INSTRUCTION
if
(InstructionBuffer[
0
]
=
=
0x0F
&&
InstructionBuffer[
1
]
=
=
0x05
)
{
goto EmulateSYSCALL;
}
/
/
SYSRET_INSTRUCTION
if
(InstructionBuffer[
0
]
=
=
0x48
&&
InstructionBuffer[
1
]
=
=
0x0F
&&
InstructionBuffer[
2
]
=
=
0x07
)
{
goto EmulateSYSRET;
}
return
FALSE;
}
/
/
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/
/
/
/
Emulate SYSRET instruction
/
/
EmulateSYSRET:
/
/
/
/
Test
/
/
/
/
LogInfo(
"SYSRET instruction => 0x%llX"
, Rip);
/
/
/
/
/
/
Perform the dispatching
and
the emulation of the SYSRET event
/
/
DispatchEventEferSysret(CoreIndex, Regs, Rip);
return
TRUE;
/
/
/
/
Emulate SYSCALL instruction
/
/
EmulateSYSCALL:
/
/
/
/
Test
/
/
/
/
LogInfo(
"SYSCALL instruction => 0x%llX , Process Id : 0x%x"
, Rip, PsGetCurrentProcessId());
/
/
/
/
/
/
Perform the dispatching
and
the emulation of the SYSCAKK event
/
/
DispatchEventEferSyscall(CoreIndex, Regs, Rip);
return
TRUE;
}