我们都知道,当vmp检测到被调试,会有如下弹框
通过这条报错信息,不难在源码中找到
然后通过它的消息传递机制,不难找到
然后查找 mtDebuggerFound 的引用即可检索到各处反调试相关源码,也就是此文将要详细说的,至于其他部分的检测,感兴趣的童鞋可以自行研究。
那么这个 os_build_number 怎么获取的呢
简单说,一共两种获取方式,1. 从 peb 里直接去取得;2.从 ntdll.dll 的头部获取文件版本号从而确定系统版本。
可能有的童鞋会问了,系统版本号拿来判断反调试是不是有点什么大病,其实不是,私以为,这边判断系统版本号纯纯的只是为了方便取 syscall 所使用的系统调用号。
如下,vmp 应该是把全量的发行版系统都是硬编码了:
当系统版本号不在 vmp 适配过的范围(比如测试版 windows),他则会去 map 一份新的 ntdll ,然后从中找他要的NT函数的系统调用号,至于系统调用号是什么,这里就不赘述了。
会心一笑,peb里的这个位就不用过多解释了
查询 ProcessDebugPort,如果查到了,自然是被调试了,也是很常见的反调
查询 ProcessDebugObjectHandle, 如果 存在调试对象句柄,那也是被调试了,也属于常见反调
针对内核调试器的监测,也属常见
这也是针对了一些常见的内核级调试器的检测,他们的驱动名
对调试器隐藏了当前线程
检测自己要调用的函数有没有被下0xCC断点
通过关闭无效句柄来判断是否成功,如果成功则中了陷阱
可还行,两个检测写在一起了,通过设置flags的TrapFlag触发异常,然后在异常处理里检查硬件断点寄存器是否设置
至此,反调弹框部分基本看完了
vmp的反调试基本是一些常见的反调试手段,
其中比较棘手的是一些NT函数的调用,他使用了SYSCALL,通过自实现的系统调用规避了我们从 r3 hook 然后绕过的可能。
通过网上一顿检索,确实看到了不少从 r0 来过 vmp 反调的插件/工具/源码。
但是!难道!我们就只能上驱动了么? 它是r3却把我们逼到了r0,有没有纯纯的三环方法还能绕过他的呢?
答案当然是,当然存在(狗头),不然我也就不写这个分享了。
通过不死心的源码阅读,终于让我看到了这块代码
也就是关键的这一句
此时,小伙伴就会问了,VMP在搞啥?
这其实是 vmp 在给 wine 环境做兼容,如果发现 ntdll.dll 的导出表存在 wine_get_version 函数,则会关闭使用系统调用的特性!!!
关闭系统调用以后,那还不是随便我们hook?
所以理论上只要给 ntdll.dll 的导出表做点手脚即可
理论可行,开始动手
首先,重复造轮子的事不要做,针对vmp的那些常见的反调,已经有很多大佬写好插件并开源了,
我在这里拿这个x64dbg官方的插件做例子 https://github.com/x64dbg/ScyllaHide
找到x64dbg插件的代码 ScyllaHide\InjectorCLI\ApplyHooking.cpp
在其中插入一段新的代码
通过这段代码,复制了一份ntdll.dll的导出表,并在里边添加了wine_get_version的导出项,实际调用其实是调用的 NtCurrentProcess。
然后,调用这个函数
然后编译产物,放到x64dbg插件目录
至此,vmp的反调保护已经被我们从纯纯的 r3 bypass了(狗头)
加个导出表还是个比较简单的操作,vmp 想要兼容 wine 环境,但是又只进行了很简单的校验,而且源码又泄露了,这才给了我们可乘之机。
此外 vmp 源码种还有很多别的地方值得学习,这点下次有机会再说。
ps: 我本次实验用的vmp版本是 最我能找到的最高版本的 3.8.4
void
LoaderMessage(MessageType type,
const
void
*param1 = NULL,
const
void
*param2 = NULL)
{
const
VMP_CHAR *message;
bool
need_format =
false
;
switch
(type) {
case
mtDebuggerFound:
message =
reinterpret_cast
<
const
VMP_CHAR *>(FACE_DEBUGGER_FOUND);
break
;
case
mtVirtualMachineFound:
message =
reinterpret_cast
<
const
VMP_CHAR *>(FACE_VIRTUAL_MACHINE_FOUND);
break
;
case
mtFileCorrupted:
message =
reinterpret_cast
<
const
VMP_CHAR *>(FACE_FILE_CORRUPTED);
break
;
case
mtUnregisteredVersion:
message =
reinterpret_cast
<
const
VMP_CHAR *>(FACE_UNREGISTERED_VERSION);
break
;
case
mtInitializationError:
message =
reinterpret_cast
<
const
VMP_CHAR *>(FACE_INITIALIZATION_ERROR);
need_format =
true
;
break
;
case
mtProcNotFound:
message =
reinterpret_cast
<
const
VMP_CHAR *>(FACE_PROC_NOT_FOUND);
need_format =
true
;
break
;
case
mtOrdinalNotFound:
message =
reinterpret_cast
<
const
VMP_CHAR *>(FACE_ORDINAL_NOT_FOUND);
need_format =
true
;
break
;
default
:
return
;
}
void
LoaderMessage(MessageType type,
const
void
*param1 = NULL,
const
void
*param2 = NULL)
{
const
VMP_CHAR *message;
bool
need_format =
false
;
switch
(type) {
case
mtDebuggerFound:
message =
reinterpret_cast
<
const
VMP_CHAR *>(FACE_DEBUGGER_FOUND);
break
;
case
mtVirtualMachineFound:
message =
reinterpret_cast
<
const
VMP_CHAR *>(FACE_VIRTUAL_MACHINE_FOUND);
break
;
case
mtFileCorrupted:
message =
reinterpret_cast
<
const
VMP_CHAR *>(FACE_FILE_CORRUPTED);
break
;
case
mtUnregisteredVersion:
message =
reinterpret_cast
<
const
VMP_CHAR *>(FACE_UNREGISTERED_VERSION);
break
;
case
mtInitializationError:
message =
reinterpret_cast
<
const
VMP_CHAR *>(FACE_INITIALIZATION_ERROR);
need_format =
true
;
break
;
case
mtProcNotFound:
message =
reinterpret_cast
<
const
VMP_CHAR *>(FACE_PROC_NOT_FOUND);
need_format =
true
;
break
;
case
mtOrdinalNotFound:
message =
reinterpret_cast
<
const
VMP_CHAR *>(FACE_ORDINAL_NOT_FOUND);
need_format =
true
;
break
;
default
:
return
;
}
if
(!os_build_number) {
if
(data.options() & LOADER_OPTION_CHECK_DEBUGGER) {
LoaderMessage(mtDebuggerFound);
return
LOADER_ERROR;
}
tmp_loader_data
-
>set_is_debugger_detected(true);
}
if
(!os_build_number) {
if
(data.options() & LOADER_OPTION_CHECK_DEBUGGER) {
LoaderMessage(mtDebuggerFound);
return
LOADER_ERROR;
}
tmp_loader_data
-
>set_is_debugger_detected(true);
}
if
(peb->BeingDebugged) {
LoaderMessage(mtDebuggerFound);
return
LOADER_ERROR;
}
if
(peb->BeingDebugged) {
LoaderMessage(mtDebuggerFound);
return
LOADER_ERROR;
}
if
(NT_SUCCESS(
reinterpret_cast
<tNtQueryInformationProcess *>(syscall | sc_query_information_process)(process, ProcessDebugPort, &debug_object,
sizeof
(debug_object), NULL)) && debug_object != 0) {
LoaderMessage(mtDebuggerFound);
return
LOADER_ERROR;
}
if
(NT_SUCCESS(
reinterpret_cast
<tNtQueryInformationProcess *>(syscall | sc_query_information_process)(process, ProcessDebugPort, &debug_object,
sizeof
(debug_object), NULL)) && debug_object != 0) {
LoaderMessage(mtDebuggerFound);
return
LOADER_ERROR;
}
if
(NT_SUCCESS(
reinterpret_cast
<tNtQueryInformationProcess *>(syscall | sc_query_information_process)(process, ProcessDebugObjectHandle, &debug_object,
sizeof
(debug_object),
reinterpret_cast
<
PULONG
>(&debug_object)))
|| debug_object == 0) {
LoaderMessage(mtDebuggerFound);
return
LOADER_ERROR;
}
if
(NT_SUCCESS(
reinterpret_cast
<tNtQueryInformationProcess *>(syscall | sc_query_information_process)(process, ProcessDebugObjectHandle, &debug_object,
sizeof
(debug_object),
reinterpret_cast
<
PULONG
>(&debug_object)))
|| debug_object == 0) {
LoaderMessage(mtDebuggerFound);
return
LOADER_ERROR;
}
SYSTEM_KERNEL_DEBUGGER_INFORMATION info;
NTSTATUS status = nt_query_system_information(SystemKernelDebuggerInformation, &info,
sizeof
(info), NULL);
if
(NT_SUCCESS(status) && info.DebuggerEnabled && !info.DebuggerNotPresent) {
LoaderMessage(mtDebuggerFound);
return
LOADER_ERROR;
}
SYSTEM_KERNEL_DEBUGGER_INFORMATION info;
NTSTATUS status = nt_query_system_information(SystemKernelDebuggerInformation, &info,
sizeof
(info), NULL);
if
(NT_SUCCESS(status) && info.DebuggerEnabled && !info.DebuggerNotPresent) {
LoaderMessage(mtDebuggerFound);
return
LOADER_ERROR;
}
SYSTEM_MODULE_INFORMATION *buffer = NULL;
ULONG
buffer_size = 0;
status = nt_query_system_information(SystemModuleInformation, &buffer, 0, &buffer_size);
if
(buffer_size) {
buffer =
reinterpret_cast
<SYSTEM_MODULE_INFORMATION *>(LoaderAlloc(buffer_size * 2));
if
(buffer) {
status = nt_query_system_information(SystemModuleInformation, buffer, buffer_size * 2, NULL);
if
(NT_SUCCESS(status)) {
for
(
size_t
i = 0; i < buffer->Count && !is_found; i++) {
SYSTEM_MODULE_ENTRY *module_entry = &buffer->Module[i];
for
(
size_t
j = 0; j < 5 ; j++) {
const
char
*module_name;
switch
(j) {
case
0:
module_name =
reinterpret_cast
<
const
char
*>(FACE_SICE_NAME);
break
;
case
1:
module_name =
reinterpret_cast
<
const
char
*>(FACE_SIWVID_NAME);
break
;
case
2:
module_name =
reinterpret_cast
<
const
char
*>(FACE_NTICE_NAME);
break
;
case
3:
module_name =
reinterpret_cast
<
const
char
*>(FACE_ICEEXT_NAME);
break
;
case
4:
module_name =
reinterpret_cast
<
const
char
*>(FACE_SYSER_NAME);
break
;
}
if
(Loader_stricmp(module_name, module_entry->Name + module_entry->PathLength,
true
) == 0) {
is_found =
true
;
break
;
}
}
}
}
LoaderFree(buffer);
}
}
SYSTEM_MODULE_INFORMATION *buffer = NULL;
ULONG
buffer_size = 0;
status = nt_query_system_information(SystemModuleInformation, &buffer, 0, &buffer_size);
if
(buffer_size) {
buffer =
reinterpret_cast
<SYSTEM_MODULE_INFORMATION *>(LoaderAlloc(buffer_size * 2));
if
(buffer) {
status = nt_query_system_information(SystemModuleInformation, buffer, buffer_size * 2, NULL);
if
(NT_SUCCESS(status)) {
for
(
size_t
i = 0; i < buffer->Count && !is_found; i++) {
SYSTEM_MODULE_ENTRY *module_entry = &buffer->Module[i];
for
(
size_t
j = 0; j < 5 ; j++) {
const
char
*module_name;
switch
(j) {
case
0:
module_name =
reinterpret_cast
<
const
char
*>(FACE_SICE_NAME);
break
;
case
1:
module_name =
reinterpret_cast
<
const
char
*>(FACE_SIWVID_NAME);
break
;
case
2:
module_name =
reinterpret_cast
<
const
char
*>(FACE_NTICE_NAME);
break
;
case
3:
module_name =
reinterpret_cast
<
const
char
*>(FACE_ICEEXT_NAME);
break
;
case
4:
module_name =
reinterpret_cast
<
const
char
*>(FACE_SYSER_NAME);
break
;
}
if
(Loader_stricmp(module_name, module_entry->Name + module_entry->PathLength,
true
) == 0) {
is_found =
true
;
break
;
}
}
}
}
LoaderFree(buffer);
}
}
if
(sc_set_information_thread)
reinterpret_cast
<tNtSetInformationThread *>(syscall | sc_set_information_thread)(
thread
, ThreadHideFromDebugger, NULL, 0);
if
(sc_set_information_thread)
reinterpret_cast
<tNtSetInformationThread *>(syscall | sc_set_information_thread)(
thread
, ThreadHideFromDebugger, NULL, 0);
tNtOpenFile *open_file =
reinterpret_cast
<tNtOpenFile *>(LoaderGetProcAddress(ntdll,
reinterpret_cast
<
const
char
*>(FACE_NT_OPEN_FILE_NAME),
true
));
tNtCreateSection *create_section =
reinterpret_cast
<tNtCreateSection *>(LoaderGetProcAddress(ntdll,
reinterpret_cast
<
const
char
*>(FACE_NT_CREATE_SECTION_NAME),
true
));
tNtMapViewOfSection *map_view_of_section =
reinterpret_cast
<tNtMapViewOfSection *>(LoaderGetProcAddress(ntdll,
reinterpret_cast
<
const
char
*>(FACE_NT_MAP_VIEW_OF_SECTION),
true
));
tNtUnmapViewOfSection *unmap_view_of_section =
reinterpret_cast
<tNtUnmapViewOfSection *>(LoaderGetProcAddress(ntdll,
reinterpret_cast
<
const
char
*>(FACE_NT_UNMAP_VIEW_OF_SECTION),
true
));
tNtClose *close =
reinterpret_cast
<tNtClose *>(LoaderGetProcAddress(ntdll,
reinterpret_cast
<
const
char
*>(FACE_NT_CLOSE),
true
));
if
(!create_section || !open_file || !map_view_of_section || !unmap_view_of_section || !close) {
LoaderMessage(mtInitializationError, INTERNAL_GPA_ERROR);
return
LOADER_ERROR;
}
uint8_t *ckeck_list[] = {
reinterpret_cast
<uint8_t*>(create_section),
reinterpret_cast
<uint8_t*>(open_file),
reinterpret_cast
<uint8_t*>(map_view_of_section),
reinterpret_cast
<uint8_t*>(unmap_view_of_section),
reinterpret_cast
<uint8_t*>(close) };
for
(i = 0; i < _countof(ckeck_list); i++) {
if
(*ckeck_list[i] == 0xcc) {
if
(data.options() & LOADER_OPTION_CHECK_DEBUGGER) {
LoaderMessage(mtDebuggerFound);
return
LOADER_ERROR;
}
tmp_loader_data->set_is_debugger_detected(
true
);
}
}
tNtOpenFile *open_file =
reinterpret_cast
<tNtOpenFile *>(LoaderGetProcAddress(ntdll,
reinterpret_cast
<
const
char
*>(FACE_NT_OPEN_FILE_NAME),
true
));
tNtCreateSection *create_section =
reinterpret_cast
<tNtCreateSection *>(LoaderGetProcAddress(ntdll,
reinterpret_cast
<
const
char
*>(FACE_NT_CREATE_SECTION_NAME),
true
));
tNtMapViewOfSection *map_view_of_section =
reinterpret_cast
<tNtMapViewOfSection *>(LoaderGetProcAddress(ntdll,
reinterpret_cast
<
const
char
*>(FACE_NT_MAP_VIEW_OF_SECTION),
true
));
tNtUnmapViewOfSection *unmap_view_of_section =
reinterpret_cast
<tNtUnmapViewOfSection *>(LoaderGetProcAddress(ntdll,
reinterpret_cast
<
const
char
*>(FACE_NT_UNMAP_VIEW_OF_SECTION),
true
));
tNtClose *close =
reinterpret_cast
<tNtClose *>(LoaderGetProcAddress(ntdll,
reinterpret_cast
<
const
char
*>(FACE_NT_CLOSE),
true
));
if
(!create_section || !open_file || !map_view_of_section || !unmap_view_of_section || !close) {
LoaderMessage(mtInitializationError, INTERNAL_GPA_ERROR);
return
LOADER_ERROR;
}
uint8_t *ckeck_list[] = {
reinterpret_cast
<uint8_t*>(create_section),
reinterpret_cast
<uint8_t*>(open_file),
reinterpret_cast
<uint8_t*>(map_view_of_section),
reinterpret_cast
<uint8_t*>(unmap_view_of_section),
reinterpret_cast
<uint8_t*>(close) };
for
(i = 0; i < _countof(ckeck_list); i++) {
if
(*ckeck_list[i] == 0xcc) {
if
(data.options() & LOADER_OPTION_CHECK_DEBUGGER) {
LoaderMessage(mtDebuggerFound);
return
LOADER_ERROR;
}
tmp_loader_data->set_is_debugger_detected(
true
);
}
}
if
(
*
reinterpret_cast<uint8_t
*
>(virtual_protect)
=
=
0xcc
) {
if
(data.options() & LOADER_OPTION_CHECK_DEBUGGER) {
LoaderMessage(mtDebuggerFound);
return
LOADER_ERROR;
}
tmp_loader_data
-
>set_is_debugger_detected(true);
}
if
(
*
reinterpret_cast<uint8_t
*
>(virtual_protect)
=
=
0xcc
) {
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2024-6-22 14:19
被JoJoRun编辑
,原因: 勘误