64位VMWare检测代码触发断点

问题描述 投票:0回答:2

我正在 .NET 2.0 C# 控制台项目中进行 VMWare 检测。检测代码在 C 中作为 DLL 中的导出函数实现,该函数使用 P/Invoke 从 C# 代码调用。 C# 代码被编译为 x86 和 x64 成两个单独的可执行文件。 C DLL 也针对这两个平台进行编译。这是导出的函数:

PUBLIC Int32 __declspec(nothrow) WINAPI GetVMType ()
{
    Int32 nVMWareType = 0;

    try
    {
        if ( !IsInVMWare ( nVMWareType ) )
        {
            ... // allocate memory, write data, etc.
        }
    }
    catch ( ... )
    {
        nVMWareType = -1;
    }

    return ( nVMWareType );
} // <-- breakpoint happens here

我有以下使用内联汇编进行 VMWare 检测的代码:

PRIVATE Bool IsInVMWare ( Int32& nType )
{
    Bool bResult = false;
    Int32 nVersion = -1;
    nType = -1;

    __try
    {
        #ifndef _WIN64 // 32-bit detection
        __asm
        {
            push    edx
            push    ecx
            push    ebx

            mov     eax, 'VMXh'
            mov     ebx, 0          // anything but 'VMXh'
            mov     ecx, 10         // get VMWare version
            mov     edx, 'VX'       // port number
            in      eax, dx         // read port
            cmp     ebx, 'VMXh'     // is it a reply from VMWare?
            je      lblInVMWare

            xor     ecx, ecx        // not in VMWare - clear return value

        lblInVMWare:
            mov     [nVersion], ecx // vmware product type
            pop     ebx
            pop     ecx
            pop     edx
        }
        #else
        nVersion = GetVMWareVersion (); // 64-bit detection
        #endif

        nType = nVersion;

        if ( nType > 0 )
            bResult = true;
    }
    __except ( EXCEPTION_EXECUTE_HANDLER )
    {
        bResult = false;
    }

    return ( bResult );
}

64位检测代码在汇编中实现为

GetVMWareVersion()

PUBLIC GetVMWareVersion
    .CODE
    ALIGN   8

GetVMWareVersion PROC

    push    rbp
    mov     rbp, rsp

    push    rdx
    push    rcx
    push    rbx

    mov     rax, 'VMXh'
    mov     rbx, 0       ; anything but 'VMXh'
    mov     rcx, 10      ; get VMWare version
    mov     rdx, 'VX'    ; port number
    in      rax, dx      ; read port
    cmp     ebx, 'VMXh'  ; is it a reply from VMWare?
    je      $0@GetVMWareVersion

    xor     rax, rax     ; not in VMWare - clear return value
    jmp     $1@GetVMWareVersion

$0@GetVMWareVersion:
    mov     rax, rcx     ; VMWare product type

$1@GetVMWareVersion:
    pop     rbx
    pop     rcx
    pop     rdx

    mov     rsp, rbp
    pop     rbp
    ret

GetVMWareVersion ENDP

END

32 位检测代码在非 VM Windows 7 上运行良好。当 64 位版本运行时(相同环境),它会触发具有以下调用堆栈的

DebugBreak()
调用:

KernelBase.dll!DebugBreak() + 0x2 bytes
[Frames below may be incorrect and/or missing, no symbols loaded for KernelBase.dll]
mscorwks.dll!PreBindAssembly() + 0x9ce69 bytes
mscorwks.dll!PreBindAssembly() + 0x9d28e bytes
mscorwks.dll!CreateApplicationContext() + 0x769d bytes
mscorwks.dll!StrongNameTokenFromPublicKey() + 0x64f8 bytes
mscorwks.dll!StrongNameTokenFromPublicKey() + 0x66ff bytes
mscorwks.dll!CreateApplicationContext() + 0x7f62 bytes
ntdll.dll!vsprintf_s() + 0x12b bytes
ntdll.dll!RtlUnwindEx() + 0x852 bytes
ntdll.dll!KiUserExceptionDispatcher() + 0x2e bytes
mscorwks.dll!IEE() + 0xd285 bytes
cccccccccccccccc()
0000000000d78180()
cccccccccccccccc()

当我在 Visual Studio 中调试 C DLL 时,输出窗口中也有这个:

First-chance exception at 0x000007feedbdfad1 (mscorwks.dll) in MyApp.exe:
    0xC0000005: Access violation reading location 0xffffffffffffffff.

每隔一段时间(并非总是)我也会在事件日志中看到此信息:

.NET Runtime version 2.0.50727.5456 - Fatal Execution Engine Error
    (000007FEEDB27916) (80131506)

我完全不知道为什么会发生这种情况。我做了很多搜索,但找不到任何似乎适用于这个问题的东西。一些网站建议切换到 .NET 4.0,但这不是一个选择。

我分析了

GetVMType ()
中的所有代码 - 它进行了一些内存分配并将数据写入动态分配的数组中,但该代码是正确的:所有内存都被释放并且没有内存被错误地覆盖。

如果我修改64位汇编代码以跳过

in
指令,则不会触发断点。作为 32 位代码运行时,这不是问题。

调用的 C# 程序有一个顶级异常处理程序和一个用于

UnhandledException
事件的事件处理程序,但是当发生此问题时,应用程序只是退出 - 没有任何处理程序被调用。

有人知道这个设置可能有什么问题吗?我花了几个小时进行调试并试图了解发生了什么,但当 P/Invoke 调用在

in
指令执行后返回时,.NET 内部似乎出现了一些问题。

c# c visual-studio-2008 exception assembly
2个回答
1
投票

我的猜测是你的函数损坏了寄存器。

在真实硬件(非虚拟机)上运行可能会在“in rax, dx”处触发异常。如果发生这种情况,则控制权将传递给异常处理程序,该处理程序设置结果,但不恢复寄存器。这种行为将是调用者完全意想不到的。例如,它可以将某些内容保存到 EBX/RBX 寄存器中,然后调用您的 asm 代码,您的 asm 代码执行“mov RBX, 0”,它执行,捕获异常,设置结果,返回 - 然后调用者突然意识到他保存的数据不再位于 EBX/RBX 中!如果 EBX/RBX 中存储了一些指针 - 你将会严重崩溃。任何事都有可能发生。

当然,您的 asm 代码会保存/恢复寄存器,但只有在没有引发异常时才会发生这种情况。 IE。如果您的代码在虚拟机上运行。然后你的代码执行其正常的执行路径,不会引发异常,寄存器将正常恢复。但如果出现异常 - 您的 POP 将被跳过,因为执行将传递给异常处理程序。

如果这是真的,那么你的 32 位代码也不起作用。如果它有效——那么它的发生只是巧合。 IE。纯属偶然。这种可能性是,如果 32 位代码的可用寄存器较少,有人在调用您的 asm 代码之前就已经保存/恢复了寄存器。

正确的代码可能应该在 try/ except 块之外执行 PUSH/POP,而不是在内部。


0
投票
.data
    LFlag DWORD ?

.code

QueryVMWareBackdoorAssembly PROC
    push rax
    push rbx
    push rcx
    push rdx
    mov eax, 'VMXh'
    mov ecx, 0Ah
    mov dx, 'VX'
    in eax, dx
    mov LFlag, ebx
    pop rdx
    pop rcx
    pop rbx
    pop rax
    ret
QueryVMWareBackdoorAssembly ENDP

END

将其用作 extern C 函数,并捕获 EXCEPTION_EXECUTE_HANDLER。如果捕获异常,则表明 VMWare 不存在。这是针对 x64 asm 的。

© www.soinside.com 2019 - 2024. All rights reserved.