俺と Virtual PC と検出
(おもに OS/2 ゲスト向けに)動作環境が VirtualPC 内かどうか検出したくなったので、このへん見つつとりあえず Win32 用の検出コード書こうとしたら mingw の gcc は構造化例外構文をサポートしてなかったでござる。なんということでしょう、OS/2 にたどり着くどころか Win32 の時点で悲しみが大きすぎる。
しかたないので API 経由で例外ハンドラを一時的に突っ込んでみた。うまくいったらおなぐさみ。
とりあえず Win32 と OS/2 用バイナリとか : undervpc_20091104.zip
しかしアレです、基本的にはたかだか4バイトの未定義命令を呼び出したいだけなのに、調べないといけないことが多すぎて(gcc のインラインアセンブラの書き方、構造化例外の低レベル API)いやになった。しかも正しいのかどうか自信が持てないという。
一応ソース つ
/* Detect running under VirtualPC. reference: http://www.codeproject.com/KB/system/VmDetect.aspx compiler: gcc only. target: Win32 (x86 32bit) and OS/2 (32bit) only. License: "You can use/modify/redistribute it freely BUT NO WARRANTY!". */ #if defined(_WIN32) #include <windows.h> #elif defined(__OS2__) #define INCL_DOS #define INCL_DOSERRORS #define INCL_DOSEXCEPTIONS #include <os2.h> #else #include <errno.h> #include <signal.h> #include <unistd.h> #endif #include <stdio.h> #include <stdlib.h> #include <string.h> struct vpc_backdoor_regpack { unsigned r_eax; unsigned r_ebx; unsigned r_ecx; unsigned r_edx; }; extern int is_under_vpc(void); extern int vpc_invoke_backdoor(struct vpc_backdoor_regpack *rp_return, const struct vpc_backdoor_regpack *rp_in); int vpc_invoke_backdoor(struct vpc_backdoor_regpack *rp_return, const struct vpc_backdoor_regpack *rp_in) { unsigned ri_eax, ri_ebx, ri_ecx, ri_edx; unsigned ro_eax, ro_ebx, ro_ecx, ro_edx; int result = 0; ri_eax = rp_in->r_eax; ri_ebx = rp_in->r_ebx; ri_ecx = rp_in->r_ecx; ri_edx = rp_in->r_edx; __asm__ __volatile__ ("; \ movl %4, %%eax; movl %5, %%ebx; movl %6, %%ecx; movl %7, %%edx; \ .byte 0x0f, 0x3f, 0x07, 0x0b; \ movl %%eax, %0; movl %%ebx, %1; movl %%ecx, %2; movl %%edx, %3 \ " : "=m"(ro_eax), "=m"(ro_ebx), "=m"(ro_ecx), "=m"(ro_edx) : "m"(ri_eax), "m"(ri_ebx), "m"(ri_ecx), "m"(ri_edx) : "%eax", "%ebx", "%ecx", "%edx", "cc" ); rp_return->r_eax = ro_eax; rp_return->r_ebx = ro_ebx; rp_return->r_ecx = ro_ecx; rp_return->r_edx = ro_edx; return result; } #if defined(_WIN32) static LONG CALLBACK isvpc_except_w32(LPEXCEPTION_POINTERS ep) { if (ep->ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION && *(LPDWORD)(ep->ExceptionRecord->ExceptionAddress) == 0x0b073f0fU) { ep->ContextRecord->Ebx = 0xffffffff; ep->ContextRecord->Eip += 4; return EXCEPTION_CONTINUE_EXECUTION; } return EXCEPTION_CONTINUE_SEARCH; } #elif defined(__OS2__) static ULONG _System isvpc_except_os2(PEXCEPTIONREPORTRECORD ep, PEXCEPTIONREGISTRATIONRECORD er, PCONTEXTRECORD ctx, PVOID pv) { if (ep->ExceptionNum == XCPT_ILLEGAL_INSTRUCTION && *(ULONG *)(ep->ExceptionAddress) == 0x0b073f0fUL) { ctx->ctx_RegEbx = 0xffffffffUL; ctx->ctx_RegEip += 4; return XCPT_CONTINUE_EXECUTION; } return XCPT_CONTINUE_SEARCH; } #endif int is_under_vpc(void) { #if defined(_WIN32) LPTOP_LEVEL_EXCEPTION_FILTER topexp; #elif defined(__OS2__) EXCEPTIONREGISTRATIONRECORD xcpt = { 0, isvpc_except_os2 }; #endif struct vpc_backdoor_regpack rp; memset(&rp, 0, sizeof(rp)); #if defined(_WIN32) { /* DO_ATOMIC_ENTER */ topexp = SetUnhandledExceptionFilter(isvpc_except_w32); vpc_invoke_backdoor(&rp, &rp); SetUnhandledExceptionFilter(topexp); } /* DO_ATOMIC_LEAVE */ #elif defined(__OS2__) { /* DO_ATOMIC_ENTER */ DosSetExceptionHandler(&xcpt); vpc_invoke_backdoor(&rp, &rp); DosUnsetExceptionHandler(&xcpt); } /* DO_ATOMIC_LEAVE */ #else # error unsupported target. #endif return rp.r_ebx == 0; } #if defined(MAKE_COMMAND) int main(int argc, char **argv) { int opt_err = 0, opt_help = 0, opt_check = 0, opt_version = 0, opt_quiet = 0; int i; int isvpc; for(i=1; i<argc; ++i) { if (strcmp(argv[i], "/?")==0 || strcmp(argv[i], "-?")==0 || strcmp(argv[i], "-h")==0 || strcmp(argv[i], "--help")==0) opt_help = 1; else if (strcmp(argv[i], "-V")==0 || strcmp(argv[i], "--version")==0) opt_version = 1; else if (strcmp(argv[i], "-q")==0 || strcmp(argv[i], "--quiet")==0) opt_quiet = 1; else if (strcmp(argv[i], "-c")==0 || strcmp(argv[i], "--check")==0) opt_quiet = 1; else { opt_err = 1; break; } } if (opt_version || opt_help || opt_err) { fprintf(opt_err ? stderr : stdout, "undervpc version 0.00 ("__DATE__")\n"); if (opt_help || opt_err) { fprintf(opt_err ? stderr : stdout, "Usage: undervpc [OPTION]\n" "Check whether the environment is running under Virtual PC\n" "\n" "Options:\n" " -c, --check Return exit code " "(0=under VPC, 1=not under VPC).\n" " -V, --version Print version.\n" " -q, --quiet Do not print the result.\n" " -h, --help Print this help.\n" ); } return opt_err ? 127 : 0; } isvpc = is_under_vpc(); if (!opt_quiet) { printf("%s,I'm%srunning under VPC.\n", isvpc ? "Yes" : "No", isvpc ? " " : " not "); } return opt_check ? (!isvpc) : 0; } #endif
ちなみに cygwin だとこの方法はダメでした(ハンドラ勝手に弄れないようになってるんすかねえ…)。