107bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov//===-- interception_linux.cc -----------------------------------*- C++ -*-===// 207bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov// 307bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov// The LLVM Compiler Infrastructure 407bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov// 507bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov// This file is distributed under the University of Illinois Open Source 607bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov// License. See LICENSE.TXT for details. 707bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov// 807bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov//===----------------------------------------------------------------------===// 907bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov// 1007bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov// This file is a part of AddressSanitizer, an address sanity checker. 1107bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov// 1207bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov// Windows-specific interception methods. 1307bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov//===----------------------------------------------------------------------===// 1407bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov 1507bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov#ifdef _WIN32 1607bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov 17bfa11b66d2d9e677be00164d44bcc0c9c6bc8f82Alexey Samsonov#include "interception.h" 1807bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov#include <windows.h> 1907bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov 2007bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanovnamespace __interception { 2107bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov 22bfa11b66d2d9e677be00164d44bcc0c9c6bc8f82Alexey Samsonovbool GetRealFunctionAddress(const char *func_name, uptr *func_addr) { 2307bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov const char *DLLS[] = { 2407bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov "msvcr80.dll", 2507bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov "msvcr90.dll", 2607bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov "kernel32.dll", 2707bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov NULL 2807bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov }; 29bfa11b66d2d9e677be00164d44bcc0c9c6bc8f82Alexey Samsonov *func_addr = 0; 30bfa11b66d2d9e677be00164d44bcc0c9c6bc8f82Alexey Samsonov for (size_t i = 0; *func_addr == 0 && DLLS[i]; ++i) { 31a8fbcd7163d199edaf4f3f71a05403b79207fc2fAlexey Samsonov *func_addr = (uptr)GetProcAddress(GetModuleHandleA(DLLS[i]), func_name); 3207bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov } 33bfa11b66d2d9e677be00164d44bcc0c9c6bc8f82Alexey Samsonov return (*func_addr != 0); 3407bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov} 352716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov 362716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov// FIXME: internal_str* and internal_mem* functions should be moved from the 372716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov// ASan sources into interception/. 382716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov 392716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanovstatic void _memset(void *p, int value, size_t sz) { 402716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov for (size_t i = 0; i < sz; ++i) 412716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov ((char*)p)[i] = (char)value; 422716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov} 432716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov 442716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanovstatic void _memcpy(void *dst, void *src, size_t sz) { 452716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov char *dst_c = (char*)dst, 462716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov *src_c = (char*)src; 472716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov for (size_t i = 0; i < sz; ++i) 482716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov dst_c[i] = src_c[i]; 492716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov} 502716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov 512716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanovstatic void WriteJumpInstruction(char *jmp_from, char *to) { 522716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov // jmp XXYYZZWW = E9 WW ZZ YY XX, where XXYYZZWW is an offset fromt jmp_from 532716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov // to the next instruction to the destination. 542716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov ptrdiff_t offset = to - jmp_from - 5; 552716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov *jmp_from = '\xE9'; 562716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov *(ptrdiff_t*)(jmp_from + 1) = offset; 572716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov} 582716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov 592d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic char *GetMemoryForTrampoline(size_t size) { 602716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov // Trampolines are allocated from a common pool. 612716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov const int POOL_SIZE = 1024; 622716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov static char *pool = NULL; 632716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov static size_t pool_used = 0; 642d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (!pool) { 652d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines pool = (char *)VirtualAlloc(NULL, POOL_SIZE, MEM_RESERVE | MEM_COMMIT, 662d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines PAGE_EXECUTE_READWRITE); 672d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // FIXME: Might want to apply PAGE_EXECUTE_READ access after all the 682d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // interceptors are in place. 692d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (!pool) 702d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return NULL; 712716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov _memset(pool, 0xCC /* int 3 */, POOL_SIZE); 722716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov } 732716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov 742d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (pool_used + size > POOL_SIZE) 752d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return NULL; 762716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov 772d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines char *ret = pool + pool_used; 782d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines pool_used += size; 792d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return ret; 802d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 812d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 822d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// Returns 0 on error. 832d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesstatic size_t RoundUpToInstrBoundary(size_t size, char *code) { 842d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines size_t cursor = 0; 852d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines while (cursor < size) { 862d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines switch (code[cursor]) { 872d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case '\x51': // push ecx 882d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case '\x52': // push edx 892d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case '\x53': // push ebx 902d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case '\x54': // push esp 912716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov case '\x55': // push ebp 922716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov case '\x56': // push esi 932716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov case '\x57': // push edi 942d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case '\x5D': // pop ebp 952d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines cursor++; 962d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines continue; 972d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case '\x6A': // 6A XX = push XX 982d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines cursor += 2; 992d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines continue; 1002d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case '\xE9': // E9 XX YY ZZ WW = jmp WWZZYYXX 1012d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines cursor += 5; 1022716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov continue; 1032716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov } 1042d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines switch (*(unsigned short*)(code + cursor)) { // NOLINT 1052716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov case 0xFF8B: // 8B FF = mov edi, edi 1062716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov case 0xEC8B: // 8B EC = mov ebp, esp 1072716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov case 0xC033: // 33 C0 = xor eax, eax 1082d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines cursor += 2; 1092716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov continue; 1102d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 0x458B: // 8B 45 XX = mov eax, dword ptr [ebp+XXh] 1112d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 0x5D8B: // 8B 5D XX = mov ebx, dword ptr [ebp+XXh] 1122716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov case 0xEC83: // 83 EC XX = sub esp, XX 1132d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines cursor += 3; 1142716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov continue; 1152716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov case 0xC1F7: // F7 C1 XX YY ZZ WW = test ecx, WWZZYYXX 1162d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines cursor += 6; 1172d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines continue; 1182d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 0x3D83: // 83 3D XX YY ZZ WW TT = cmp TT, WWZZYYXX 1192d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines cursor += 7; 1202716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov continue; 1212716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov } 1222d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines switch (0x00FFFFFF & *(unsigned int*)(code + cursor)) { 1232716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov case 0x24448A: // 8A 44 24 XX = mov eal, dword ptr [esp+XXh] 1245d71de26cedae3dafc17449fe0182045c0bd20e8Stephen Hines case 0x24448B: // 8B 44 24 XX = mov eax, dword ptr [esp+XXh] 1252716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov case 0x244C8B: // 8B 4C 24 XX = mov ecx, dword ptr [esp+XXh] 1262716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov case 0x24548B: // 8B 54 24 XX = mov edx, dword ptr [esp+XXh] 1272d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines case 0x24748B: // 8B 74 24 XX = mov esi, dword ptr [esp+XXh] 1282716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov case 0x247C8B: // 8B 7C 24 XX = mov edi, dword ptr [esp+XXh] 1292d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines cursor += 4; 1302716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov continue; 1312716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov } 1322716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov 1332716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov // Unknown instruction! 1342d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // FIXME: Unknown instruction failures might happen when we add a new 1352d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // interceptor or a new compiler version. In either case, they should result 1362d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // in visible and readable error messages. However, merely calling abort() 1375d71de26cedae3dafc17449fe0182045c0bd20e8Stephen Hines // leads to an infinite recursion in CheckFailed. 1382d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Do we have a good way to abort with an error message here? 1395d71de26cedae3dafc17449fe0182045c0bd20e8Stephen Hines __debugbreak(); 1402d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return 0; 1412716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov } 1422716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov 1432d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return cursor; 1442d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines} 1452d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 1462d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesbool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) { 1472d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines#ifdef _WIN64 1482d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines#error OverrideFunction is not yet supported on x64 1492d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines#endif 1502d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Function overriding works basically like this: 1512d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // We write "jmp <new_func>" (5 bytes) at the beginning of the 'old_func' 1522d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // to override it. 1532d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // We might want to be able to execute the original 'old_func' from the 1542d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // wrapper, in this case we need to keep the leading 5+ bytes ('head') 1552d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // of the original code somewhere with a "jmp <old_func+head>". 1562d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // We call these 'head'+5 bytes of instructions a "trampoline". 1572d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines char *old_bytes = (char *)old_func; 1582d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 1592d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // We'll need at least 5 bytes for a 'jmp'. 1602d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines size_t head = 5; 1612d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (orig_old_func) { 1622d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Find out the number of bytes of the instructions we need to copy 1632d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // to the trampoline and store it in 'head'. 1642d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines head = RoundUpToInstrBoundary(head, old_bytes); 1652d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (!head) 1662d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return false; 1672d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines 1682d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Put the needed instructions into the trampoline bytes. 1692d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines char *trampoline = GetMemoryForTrampoline(head + 5); 1702d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (!trampoline) 1712d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines return false; 1722d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines _memcpy(trampoline, old_bytes, head); 1732d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines WriteJumpInstruction(trampoline + head, old_bytes + head); 1742d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines *orig_old_func = (uptr)trampoline; 1752d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines } 1762716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov 1772d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Now put the "jmp <new_func>" instruction at the original code location. 1782d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // We should preserve the EXECUTE flag as some of our own code might be 1792d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // located in the same page (sic!). FIXME: might consider putting the 1802d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // __interception code into a separate section or something? 1812716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov DWORD old_prot, unused_prot; 1822d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (!VirtualProtect((void *)old_bytes, head, PAGE_EXECUTE_READWRITE, 183bfa11b66d2d9e677be00164d44bcc0c9c6bc8f82Alexey Samsonov &old_prot)) 1842716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov return false; 1852716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov 1862d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines WriteJumpInstruction(old_bytes, (char *)new_func); 1872716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov _memset(old_bytes + 5, 0xCC /* int 3 */, head - 5); 1882716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov 1892d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines // Restore the original permissions. 1902d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines if (!VirtualProtect((void *)old_bytes, head, old_prot, &unused_prot)) 1912716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov return false; // not clear if this failure bothers us. 1922716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov 1932716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov return true; 1942716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov} 1952716a61d085a8fdf13a099822720e320414cc4dcTimur Iskhodzhanov 19607bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov} // namespace __interception 19707bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov 19807bb9f1e3600195119aec1aae1aa48a6ed2f5febTimur Iskhodzhanov#endif // _WIN32 199