虚拟机反检测技术:拦截

目录

  • 拦截检测方法

  • 1. 检查系统功能中是否设置了拦截

  • 2. 通过鼠标拦截检查用户的点击中是否存在特定字符串

  • 2.1. Windows Vista+

  • 识别标志

  • 反制措施

  • 归功于

拦截检测方法
这里描述的技术利用拦截来检测用户的存在,或者作为检查是否安装了一些不寻常的主机操作系统拦截的手段。
1. 检查系统功能中是否设置了拦截
恶意软件在特定地址读取内存,以检查Windows API函数是否被拦截。
这种方法是基于这样一个事实,即仿真环境最有可能钩住这些功能,以便能够在伪装期间收集数据和统计数据。
要检查的常用函数:

  • ReadFile

  • DeleteFile

  • CreateProcessA/W

通过以下函数读取内存:

  • ReadProcessMemory

  • NtReadVirtualMemory

然后可以使用不同的算法进行检查:

  • 将前两个字节与\x8B\xFF(mov edi,edi)进行比较 —kernel32函数的典型出现方式。

  • 将前N个字节与\xCC-软件断点(int 3)进行比较,该断点未直接与拦截连接,但仍有可疑行为。

  • 将前N个字节与\xE9(调用)或\xEB(jmp指令)进行比较-用于重定向执行的典型指令。

  • 正在检查执行重定向的push/ret组合
    等等

要计算每一种可能的比较是非常困难的,所以应用程序行为中不正常的一般迹象是读取操作系统库所在的内存。如果更准确地说:读取 “有趣 “的函数所在的内存。
检测拦截的样本:

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
HOOK_TYPE IsHooked(LPCVOID lpFuncAddress, DWORD_PTR *dwAddressOffset) {
    LPCBYTE lpBytePtr = (LPCBYTE)lpFuncAddress;
 
    if (lpBytePtr[0] == 0xE9) {
        *dwAddressOffset = 1;
        return HOOK_RELATIVE;    // E9 jmp is relative.
    else if (lpBytePtr[0] == 0x68 &&  lpBytePtr[5] == 0xC3) {
        *dwAddressOffset = 1;
        return HOOK_ABOLSUTE;    // push/ret is absolute.
    }
 
    return HOOK_NONE;            // No hook.
}
 
LPVOID lpFunction = ...;
DWORD_PTR dwOffset = 0;
LPVOID dwHookAddress = 0;
 
HOOK_TYPE ht = IsHooked(lpFunction, &dwOffset);
if (ht == HOOK_ABSOLUTE) {
    // 1. Get the pointer to the address (lpFunction + dwOffset)
    // 2. Cast it to a DWORD pointer
    // 3. Dereference it to get the DWORD value
    // 4. Cast it to a pointer
    dwHookAddress = (LPVOID)(*(LPDWORD)((LPBYTE)lpFunction + dwOffset));
else if (ht == HOOK_RELATIVE) {
    // 1. Get the pointer to the address (lpFunction + dwOffset)
    // 2. Cast it to an INT pointer
    // 3. Dereference it to get the INT value (this can be negative)
    INT nJumpSize = (*(PINT)((LPBYTE)lpFunction  + dwOffset);
    // 4. E9 jmp starts from the address AFTER the jmp instruction
    DWORD_PTR dwRelativeAddress = (DWORD_PTR)((LPBYTE)lpFunction + dwOffset + 4));
    // 5. Add the relative address and jump size
    dwHookAddress = (LPVOID)(dwRelativeAddress + nJumpSize);
}

 

解除拦截函数的样本:

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
// Parse the PE headers.
PIMAGE_DOS_HEADER pidh = (PIMAGE_DOS_HEADER)lpMapping;
PIMAGE_NT_HEADERS pinh = (PIMAGE_NT_HEADERS)((DWORD_PTR)lpMapping + pidh->e_lfanew);
 
// Walk the section headers and find the .text section.
for (WORD i = 0; i < pinh->FileHeader.NumberOfSections; i++) {
    PIMAGE_SECTION_HEADER pish = (PIMAGE_SECTION_HEADER)((DWORD_PTR)IMAGE_FIRST_SECTION(pinh) + 
                                 ((DWORD_PTR)IMAGE_SIZEOF_SECTION_HEADER * i));
    if (!strcmp(pish->Name, ".text")) {
        // Deprotect the module's memory region for write permissions.
        DWORD flProtect = ProtectMemory(
            (LPVOID)((DWORD_PTR)hModule + (DWORD_PTR)pish->VirtualAddress),    // Address to protect.
            pish->Misc.VirtualSize,                        // Size to protect.
            PAGE_EXECUTE_READWRITE                         // Desired protection.
        );
 
        // Replace the hooked module's .text section with the newly mapped module's.
        memcpy(
            (LPVOID)((DWORD_PTR)hModule + (DWORD_PTR)pish->VirtualAddress),
            (LPVOID)((DWORD_PTR)lpMapping + (DWORD_PTR)pish->VirtualAddress),
            pish->Misc.VirtualSize
        );
 
        // Reprotect the module's memory region.
        flProtect = ProtectMemory(
            (LPVOID)((DWORD_PTR)hModule + (DWORD_PTR)pish->VirtualAddress),    // Address to protect.
            pish->Misc.VirtualSize,                        // Size to protect.
            flProtect                                      // Revert to old protection.
        );
    }
}

 

2. 通过鼠标拦截检查用户的点击中是否存在特定字符串

恶意软件设置鼠标拦截来检测点击(或更多)如果它发生。如果是这样的话,恶意软件会将主机视为一个常规的主机,即最终用户在屏幕后面,而不是虚拟环境。如果没有检测到鼠标点击,那么很可能是一个虚拟环境。
使用的函数:

  • SetWindowsHookExA/W (WH_MOUSE_LL, …)

  • GetAsyncKeyState

代码样本 (SetWindowsHookExA):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
HHOOK g_hhkMouseHook = NULL;
 
LRESULT CALLBACK mouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
  switch (wParam)
  {
  case WM_MOUSEMOVE:
    // ...
    break;
  case WM_NCLBUTTONDOWN:
    // ...
    break;
  case WM_LBUTTONUP:
    UnhookWindowsHookEx(g_hhkMouseHook);
    CallMaliciousCode();
    ExitProcess(0);
  }
  return CallNextHookEx(g_hhkMouseHook, nCode, wParam, lParam);
}
 
g_hhkMouseHook = SetWindowsHookEx(WH_MOUSE_LL, mouseHookProc, GetModuleHandleA(NULL), NULL);

 

代码样本 (GetAsyncKeyState):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
std::thread t([]()
{
  int count = 0;
  while (true)
  {
    if (GetAsyncKeyState(VK_LBUTTON) || GetAsyncKeyState(VK_RBUTTON) || GetAsyncKeyState(VK_MBUTTON))
    {
      if (++count == 2)
        break;
    }
    Sleep(100);
  }
  CallMaliciousCode();
});
t.join();

 

识别标志
没有为这个规避组提供识别标志,因为很难在以某种规避技术为目标的代码和 “合法使用 “的代码之间做出区别。
反制措施

  • 与函数拦截检查:设置内核模式钩子;第二个解决方案是使用堆栈路由来实现函数拦截。
  • 与通过拦截进行的鼠标点击检查相比:使用鼠标移动伪装模块。
© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容