简体   繁体   English

针对Windows 2000的Visual Studio 2012 Win32项目

[英]visual studio 2012 win32 project targeting windows 2000

I need to modify a very old project written some years ago in win32 that MUST run on windows 2000 server. 我需要修改几年前在win32中编写的非常老的项目,该项目必须在Windows 2000服务器上运行。

Having recently upgraded my computer i moved to visual studio 2012 and hence my problems. 最近升级了计算机后,我搬到了Visual Studio 2012,因此遇到了问题。

Ihave read a lot of posts here, and i kinda got confused. 我在这里读了很多文章,我有点困惑。

first using platform toolsets i need to install vs2008 & vs2010 right? 首先使用平台工具集,我需要安装vs2008和vs2010对吗? this is unacceptable. 这是无法接受的。

second, some posts say that i need to overwrite the function DecodePointer/EncodePointer . 第二,一些帖子说我需要覆盖DecodePointer / EncodePointer函数。

third, just be using the defines 第三,只是使用定义

#ifndef WINVER
#define WINVER 0x0501
#endif

#ifndef _WIN32_WINNT   // Specifies that the minimum required platform is Windows XP.
#define _WIN32_WINNT 0x0501
#endif

option 1 is not acceptable. 选项1不可接受。 which of the other 2 options work with vs2012??? 其他两个选项中的哪个与vs2012一起使用???

thanks to all. 谢谢大家。

The runtime for the VS2012 compiler supports targetting XP, but does not support earlier versions. VS2012编译器的运行时支持目标XP,但不支持早期版本。 In fact, on release, XP targetting was not supported and that was added in a later update. 实际上,在发行时,不支持XP定位,并且在以后的更新中添加了XP定位。 If you must support Win2k, you must use the toolset from an earlier version of VS that does support Win2k. 如果必须支持Win2k,则必须使用不支持Win2k的VS早期版本中的工具集。

Here's an expansion on Cody Gray's code for VS2017. 这是Cody Gray的VS2017代码的扩展。 It could use a few extra eyeballs to ensure it would work properly. 它可能会使用一些额外的工具以确保其正常工作。 As of now apps will start and run on NT4 built with VS2017. 截至目前,应用程序将在VS2017内置的NT4上启动并运行。 It will also work on Win98 if using MS Layer for Unicode (unicows). 如果使用MS Layer for Unicode(unicows),它也将在Win98上工作。

.386
.MODEL flat, stdcall

    USE_W_STRINGS EQU 1                 ; Flag on which GetModuleHandle version to use
    USE_WIN9X     EQU 1                 ; Flag on if to include Win9x specific requirements

   EXTRN STDCALL ImplementGetModuleHandleExW@12 : PROC
   EXTRN STDCALL ImplementSetFilePointerEx@20 : PROC

   ;; Declare functions that we will call statically
   IF USE_W_STRINGS
    EXTRN STDCALL _imp__GetModuleHandleW@4  : DWORD
   ELSE
    EXTRN STDCALL _imp__GetModuleHandleA@4  : DWORD
   ENDIF

   EXTRN STDCALL _imp__GetProcAddress@8    : DWORD


.DATA
   ;; Override the import symbols from kernel32.dll
   __imp__InitializeSListHead@4    DWORD DownlevelInitializeSListHead
   __imp__GetModuleHandleExW@12    DWORD DownlevelGetModuleHandleExW
   __imp__EncodePointer@4          DWORD DownlevelEncodeDecodePointer
   __imp__DecodePointer@4          DWORD DownlevelEncodeDecodePointer
   ;__imp__HeapSetInformation@16 dd DownlevelHeapSetInformation
   __imp__SetFilePointerEx@20      DWORD ImplementSetFilePointerEx@20

   EXTERNDEF STDCALL __imp__InitializeSListHead@4 : DWORD
   EXTERNDEF STDCALL __imp__GetModuleHandleExW@12 : DWORD
   EXTERNDEF STDCALL __imp__EncodePointer@4 : DWORD
   EXTERNDEF STDCALL __imp__DecodePointer@4 : DWORD
   ;EXTERNDEF STDCALL __imp__HeapSetInformation@16 : DWORD
   EXTERNDEF STDCALL __imp__SetFilePointerEx@20 : DWORD


   ; For Win9x support - need to change return value to TRUE for the crt startup
   IF USE_WIN9X
    __imp__InitializeCriticalSectionAndSpinCount@8 DWORD DownlevelInitializeCriticalSectionAndSpinCount
    EXTERNDEF STDCALL __imp__InitializeCriticalSectionAndSpinCount@8 : DWORD
   ENDIF

CONST SEGMENT
   IF USE_W_STRINGS
     kszKernel32            DB 'k', 00H, 'e', 00H, 'r', 00H, 'n', 00H, 'e', 00H, 'l', 00H, '3', 00H, '2', 00H, 00H, 00H
     kszAdvApi32            DB 'a', 00H, 'd', 00H, 'v', 00H, 'a', 00H, 'p', 00H, 'i', 00H, '3', 00H, '2', 00H, 00H, 00H
   ELSE
     kszKernel32            DB "kernel32", 00H
     kszAdvApi32            DB "advapi32", 00H
   ENDIF

   kszInitializeSListHead DB "InitializeSListHead", 00H
   kszGetModuleHandleExW  DB "GetModuleHandleExW", 00H

   kszInitializeCriticalSectionAndSpinCount DB "InitializeCriticalSectionAndSpinCount", 00H
   kszGetVersion DB "GetVersion", 00H

  ; Windows XP and Server 2003 and later have RtlGenRandom, which is exported as SystemFunction036.
  ; If needed, we could fall back to CryptGenRandom(), but that will be much slower
  ; because it has to drag in the entire crypto API.
  ; (See also: https://blogs.msdn.microsoft.com/michael_howard/2005/01/14/cryptographically-secure-random-number-on-windows-without-using-cryptoapi/)
;   kszSystemFunction036   DB "SystemFunction036", 00H
CONST ENDS

.CODE

   ; C++ translation:
   ;    extern "C" VOID WINAPI DownlevelInitializeSListHead(PSLIST_HEADER pHead)
   ;    {
   ;       const HMODULE hmodKernel32 = ::GetModuleHandleW(L"kernel32");
   ;       typedef decltype(InitializeSListHead)* pfnInitializeSListHead;
   ;       const pfnInitializeSListHead pfn = reinterpret_cast<pfnInitializeSListHead>(::GetProcAddress(hmodKernel32, "InitializeSListHead"));
   ;       if (pfn)
   ;       {
   ;          // call WinAPI function
   ;          pfn(pHead);
   ;       }
   ;       else
   ;       {
   ;          // fallback implementation for downlevel
   ;          pHead->Alignment = 0;
   ;       }
   ;    }
   DownlevelInitializeSListHead PROC
      ;; Get a handle to the DLL containing the function of interest.
      push  OFFSET kszKernel32
   IF USE_W_STRINGS
      call  DWORD PTR _imp__GetModuleHandleW@4  ; Returns the handle to the library in EAX.
   ELSE
      call  DWORD PTR _imp__GetModuleHandleA@4  ; Returns the handle to the library in EAX.
   ENDIF

       ;; Attempt to obtain a pointer to the function of interest.
      push  OFFSET kszInitializeSListHead       ; Push 2nd parameter (string containing function's name).
      push  eax                                 ; Push 1st parameter (handle to the library).
      call  DWORD PTR _imp__GetProcAddress@8    ; Returns the pointer to the function in EAX.

      ;; Test for success, and call the function if we succeeded.
      test  eax, eax                            ; See if we successfully retrieved a pointer to the function.
      je    SHORT FuncNotSupported              ; Jump on failure (ptr == 0), or fall through in the most-likely case.
      jmp   eax                                 ; We succeeded (ptr != 0), so tail-call the function.

      ;; The dynamic call failed, presumably because the function isn't available.
      ;; So do what _RtlInitializeSListHead@4 (which is what we jump to on uplevel platforms) does,
      ;; which is to set pHead->Alignment to 0. It is a QWORD-sized value, so 32-bit code must
      ;; clear both of the DWORD halves.
    FuncNotSupported:
      mov   edx, DWORD PTR [esp+4]      ; get pHead->Alignment
      xor   eax, eax
      mov   DWORD PTR [edx],   eax      ; pHead->Alignment = 0
      mov   DWORD PTR [edx+4], eax
      ret   4
   DownlevelInitializeSListHead ENDP


   ; C++ translation:
   ;     extern "C" BOOL WINAPI DownlevelGetModuleHandleExW(DWORD dwFlags, LPCTSTR lpModuleName, HMODULE* phModule)
   ;     {
   ;        const HMODULE hmodKernel32 = ::GetModuleHandleW(L"kernel32");
   ;        typedef decltype(GetModuleHandleExW)* pfnGetModuleHandleExW;
   ;        const pfnGetModuleHandleExW pfn = reinterpret_cast<pfnGetModuleHandleExW>(::GetProcAddress(hmodKernel32, "GetModuleHandleExW"));
   ;        if (pfn)
   ;        {
   ;           // call WinAPI function
   ;           return pfn(dwFlags, lpModuleName, phModule);
   ;        }
   ;        else
   ;        {
   ;           // fallback for downlevel: return failure
   ;           return FALSE;
   ;        }
   ;     }
   DownlevelGetModuleHandleExW PROC
      ;; Get a handle to the DLL containing the function of interest.
      push  OFFSET kszKernel32
   IF USE_W_STRINGS
      call  DWORD PTR _imp__GetModuleHandleW@4  ; Returns the handle to the library in EAX.
   ELSE
      call  DWORD PTR _imp__GetModuleHandleA@4  ; Returns the handle to the library in EAX.
   ENDIF

       ;; Attempt to obtain a pointer to the function of interest.
      push  OFFSET kszGetModuleHandleExW        ; Push 2nd parameter (string containing function's name).
      push  eax                                 ; Push 1st parameter (handle to the library).
      call  DWORD PTR _imp__GetProcAddress@8    ; Returns the pointer to the function in EAX.

      ;; Test for success, and call the function if we succeeded.
      test  eax, eax                            ; See if we successfully retrieved a pointer to the function.
      je    SHORT FuncNotSupported              ; Jump on failure (ptr == 0), or fall through in the most-likely case.
      jmp   eax                                 ; We succeeded (ptr != 0), so tail-call the function.

      ;; The dynamic call failed, presumably because the function isn't available.
      ;; The basic VS 2015 CRT (used in a simple Win32 app) only calls this function
      ;; in try_cor_exit_process(), as called from common_exit (both in exit.cpp),
      ;; where it uses it to attempt to get a handle to the module mscoree.dll.
      ;; Since we don't care about managed apps, that attempt should rightfully fail.
      ;; If this turns out to be used in other contexts, we'll need to revisit this
      ;; and implement a proper fallback.
    FuncNotSupported:
      jmp   ImplementGetModuleHandleExW@12

   DownlevelGetModuleHandleExW ENDP

   DownlevelEncodeDecodePointer proc
      mov eax, [esp+4]
      ret 4
   DownlevelEncodeDecodePointer endp

;   DownlevelHeapSetInformation proc
;      mov eax, 1
;      ret 10h
;   DownlevelHeapSetInformation endp

;   DownlevelSystemFunction036 PROC
;      int 3   ; break --- stub unimplemented
;      ret 8
;   DownlevelSystemFunction036 ENDP


; Win9x section
IF USE_WIN9X

   ; here we need to return 1 for the crt startup code which checks the return value which on 9x is "none" (throws exception instead)
   ; This section will change the imported function on the first call for NT based OS so that every call doesn't require additional processing.

   DownlevelInitializeCriticalSectionAndSpinCount proc

      push  OFFSET kszKernel32
   IF USE_W_STRINGS
      call  DWORD PTR _imp__GetModuleHandleW@4  ; Returns the handle to the library in EAX.
   ELSE
      call  DWORD PTR _imp__GetModuleHandleA@4  ; Returns the handle to the library in EAX.
   ENDIF

       ;; Save copy of handle
      push  eax

       ;; Attempt to obtain a pointer to the function of interest.
      push  OFFSET kszInitializeCriticalSectionAndSpinCount ; Push 2nd parameter (string containing function's name).
      push  eax                                 ; Push 1st parameter (handle to the library).
      call  DWORD PTR _imp__GetProcAddress@8    ; Returns the pointer to the function in EAX.

      ;; Test for success, and call the function if we succeeded.
      test  eax, eax                            ; See if we successfully retrieved a pointer to the function.
      jz    SHORT FuncNotSupported              ; Jump on failure (ptr == 0), or fall through in the most-likely case.

      ;; save address and get back kernel32 handle
      xchg  dword ptr [esp],eax

       ;; Attempt to obtain a pointer to the function of interest.
      push  OFFSET kszGetVersion                ; Push 2nd parameter (string containing function's name).
      push  eax                                 ; Push 1st parameter (handle to the library).
      call  DWORD PTR _imp__GetProcAddress@8    ; Returns the pointer to the function in EAX.

      ;; See if function exists
      test  eax,eax
      jz    SHORT FuncNotSupported

      ;; call GetVersion
      call  eax

      ;; check if win9x
      test  eax,80000000h
      pop   eax                                 ; get back address of InitializeCriticalSectionAndSpinCount function
      jz    WinNT

      ;; for Win9x we need to call and then change return code
      push  [esp+8]                             ; Copy the 1st parameter (+4 for IP return address, +4 for fist param)
      push  [esp+8]                             ; Copy the 2nd parameter (recall esp moves on push)
      call  eax
      mov   eax,1
      ret   8

WinNT:
      ;; Change the call address for next calls and call directly
      mov   __imp__InitializeCriticalSectionAndSpinCount@8,eax
      jmp   eax


      ;; We should never end up here but if we do, fail the call
    FuncNotSupported:
      pop  eax
      xor  eax,eax
      ret  8

   DownlevelInitializeCriticalSectionAndSpinCount endp

ENDIF

END

There is a part 2 as well: 还有第二部分:

// Win9x:
//
// The CRT now uses several "W" versions of functions which is more practial to require
// the use of the Microsoft Layer for Unicode (MSLU) for Windows 9x to implement it.  The 
// unicows.dll (for 9x) should be placed in the program folder with the .exe if using it.
// unicows.dll is only loaded on 9x platforms. The way Win9x works without the MSLU is 
// that several "W" version of functions are located in kernel32.dll but they are just a
// stub that returns failure code. To implement the unicode layer (unicows) the unicode.lib
// must be linked prior to the other libs that should then linked in after unicode.lib.  
// The libraries are:
//
//    kernel32.lib advapi32.lib user32.lib gdi32.lib shell32.lib comdlg32.lib 
//    version.lib mpr.lib rasapi32.lib winmm.lib winspool.lib vfw32.lib 
//    secur32.lib oleacc.lib oledlg.lib sensapi.lib
//


#include <windows.h>

// pull items from ntdef.h
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
typedef _Return_type_success_(return >= 0) LONG NTSTATUS;

//
// implementation of replacement function for GetModuleHandleExW
//
extern "C" BOOL WINAPI ImplementGetModuleHandleExW(DWORD dwFlags, LPCWSTR lpModuleName, HMODULE* phModule)
{
  // check flag combinations
  if (phModule==NULL ||
      dwFlags & ~(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS) ||
      ((dwFlags & GET_MODULE_HANDLE_EX_FLAG_PIN) && (dwFlags & GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT)) ||
      (lpModuleName==NULL && (dwFlags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS))) {
    SetLastError(ERROR_INVALID_PARAMETER);
    return FALSE;
  }


  // check if to get by address
  if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS) {
    *phModule = NULL;

    typedef PVOID (NTAPI *LPFN_RtlPcToFileHeader)(PVOID PcValue, PVOID * BaseOfImage);

    const HMODULE hmodkernel32 = GetModuleHandleW(L"kernel32");
    const LPFN_RtlPcToFileHeader pfnRtlPcToFileHeader = reinterpret_cast<LPFN_RtlPcToFileHeader>(GetProcAddress(hmodkernel32, "RtlPcToFileHeader"));

    if (pfnRtlPcToFileHeader) {
      // use RTL function (nt4+)
      pfnRtlPcToFileHeader((PVOID)lpModuleName, (PVOID*)phModule);
    }
    else {
      // query memory directly (win9x)
      MEMORY_BASIC_INFORMATION mbi; 
      if (VirtualQuery((PVOID)lpModuleName, &mbi, sizeof(mbi)) >= offsetof(MEMORY_BASIC_INFORMATION, AllocationProtect)) {
        *phModule = reinterpret_cast<HMODULE>(mbi.AllocationBase);
      }
    }
  }
  else {
    // standard getmodulehandle - to see if loaded
    *phModule = GetModuleHandleW(lpModuleName);
  }


  // check if module found
  if (*phModule == NULL) {
    SetLastError(ERROR_DLL_NOT_FOUND);
    return FALSE;
  }

  // check if reference needs updating
  if ((dwFlags & GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT) == 0) {

    typedef NTSTATUS(NTAPI *LPFN_LdrAddRefDll)(ULONG Flags, PVOID BaseAddress);
    #define LDR_ADDREF_DLL_PIN   0x00000001

    const HMODULE hmodntdll = GetModuleHandleW(L"ntdll");
    const LPFN_LdrAddRefDll pfnLdrAddRefDll = reinterpret_cast<LPFN_LdrAddRefDll>(GetProcAddress(hmodntdll, "LdrAddRefDll"));

    if (pfnLdrAddRefDll) {
      // update dll reference
      if (!NT_SUCCESS(pfnLdrAddRefDll((dwFlags & GET_MODULE_HANDLE_EX_FLAG_PIN) ? LDR_ADDREF_DLL_PIN : 0, *phModule))) {
        SetLastError(ERROR_GEN_FAILURE);
        return FALSE;
      }
    }
    else if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_PIN) {
      SetLastError(ERROR_NOT_SUPPORTED);
      return FALSE;
    }
    else {
      WCHAR *filename;
      if ((filename=reinterpret_cast<WCHAR*>(VirtualAlloc(NULL, MAX_PATH*sizeof(WCHAR), MEM_COMMIT, PAGE_READWRITE)))!=NULL) {
        DWORD ret = GetModuleFileNameW(*phModule, filename, MAX_PATH);
        if (ret < MAX_PATH) {
          *phModule = LoadLibraryW(filename);
        }
        else *phModule = NULL;
        VirtualFree(filename, 0, MEM_RELEASE);
        // ensure load library success
        if (*phModule == NULL) {
          return FALSE;
        }
      }
      else {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
      }
    }
  }
  return TRUE;
}

//
// implementation of replacement function for SetFilePointerEx
//
extern "C" BOOL WINAPI ImplementSetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod)
{
  DWORD ret=SetFilePointer(hFile, liDistanceToMove.LowPart, &liDistanceToMove.HighPart, dwMoveMethod);
  if (ret == INVALID_SET_FILE_POINTER) {
    if (GetLastError() != NO_ERROR) {
      return FALSE;
    }
  }
  // check if provide file location
  if (lpNewFilePointer) {
    lpNewFilePointer->LowPart = ret;
    lpNewFilePointer->HighPart = liDistanceToMove.HighPart;
  }
  // success
  return TRUE;
}

VS 2008 works to target Windows 2000 out of the box, as you said. 如您所说,VS 2008可以将Windows 2000作为目标。 You don't need anything extra. 您不需要任何额外的东西。 If you prefer to use a newer version of the IDE, installing multiple versions of Visual Studio simultaneously (always install the oldest versions first, working "forward" in time) will allow you to, eg , work in VS 2010 but tell it to build using the VS 2008 toolchain. 如果您希望使用更新版本的IDE,则可以同时安装多个Visual Studio版本(始终首先安装最旧的版本,并及时进行“转发”), 例如 ,可以在VS 2010中工作,但告诉它进行构建使用VS 2008工具链。 You obviously won't be able to benefit from compiler features introduced with VS 2010, but you will get to use the newer IDE. 显然,您将无法从VS 2010引入的编译器功能中受益,但是您将可以使用较新的IDE。

You can use VS 2010 to target Windows 2000 with the EncodePointer/DecodePointer trick. 您可以使用VS 2010通过EncodePointer / DecodePointer技巧将Windows 2000定位为目标。 The issue here is that the VS 2010 C runtime library (CRT) requires these functions (it calls them internally), but these functions do not exist on versions of the OS prior to Windows XP SP2. 这里的问题是,VS 2010 C运行时库(CRT)需要这些函数(内部调用它们),但是在Windows XP SP2之前的OS版本中不存在这些函数。 However, if you write stubs and link your executable against those (while also statically linking to the CRT, so it will actually find and use those stubs), then you can run a VS 2010-compiled EXE on Windows 2000. Note that you will also need to set the minimum required version to 5.0 in the linker settings. 但是,如果编写存根并将它们链接到可执行文件(同时还静态链接到CRT,因此它实际上会找到并使用这些存根),则可以在Windows 2000上运行VS 2010编译的EXE。请注意,您将还需要在链接器设置中将最低要求版本设置为5.0。 (You'll get a link-time warning that this is not a valid, supported version when you do so, but you can simply ignore this warning. It does work, and the field does get properly set in the PE header.) This is undoubtedly a dirty trick, but I know that it works very well; (这样做时,您会得到一个链接时警告,它不是有效的受支持版本,但您可以忽略此警告。它确实起作用,并且确实在PE标头中设置了该字段。)无疑是一个肮脏的把戏,但我知道它的效果很好。 I do it in several of my projects. 我在几个项目中都这样做。 My stubs dynamically call the real EncodePointer/DecodePointer functions if they are available on the current OS, and if not, fall back to basically a no-op (giving up the security benefits on these downlevel OSes). 如果存根在当前操作系统上可用,我的存根会动态调用真正的EncodePointer / DecodePointer函数,如果没有,则基本上退回到无操作状态(放弃了这些下层OS的安全性优势)。 Suma has already covered this trick quite well in an answer to a related question . 苏马在回答相关问题时已经很好地涵盖了这个技巧

The WINVER and _WIN32_WINNT defines really don't have anything to do with this. WINVER_WIN32_WINNT定义确实与此无关。 They just control which function prototypes the Windows SDK headers actually define. 它们仅控制Windows SDK标头实际定义的功能原型。 The idea is that you set these to your target Windows version, and then you will only be able to statically link to functions that actually exist on that version of Windows. 想法是将这些设置为目标Windows版本,然后您将只能静态链接到该Windows版本上实际存在的功能。 You can still dynamically call (via GetModuleHandle/LoadLibrary → GetProcAddress) newer functions when running on an appropriate version of Windows, and gracefully fall back if they are not supported. 在适当版本的Windows上运行时,您仍然可以动态调用(通过GetModuleHandle / LoadLibrary→GetProcAddress)较新的函数,如果不支持,则可以适当地回退。 If you try to statically link to functions that do not exist, the loader will generate an error when you try to run your application. 如果您尝试静态链接到不存在的功能,则当您尝试运行应用程序时,加载程序将生成错误。 This is easy, though, because this is something that is entirely within your control as a developer. 不过,这很容易,因为作为开发人员,这完全在您的控制范围之内。 The problem is when the CRT (a library that you don't control) calls functions that don't exist, like EncodePointer. 问题是当CRT(您无法控制的库)调用不存在的函数(例如EncodePointer)时。 That's why the above-described workaround is required. 这就是为什么需要上述解决方法的原因。 The values of WINVER and _WIN32_WINNT have no actual effect on the compiler or the linker. WINVER_WIN32_WINNT的值对编译器或链接器没有实际影响。

You can probably get away with a similar trick for VS 2012. I started working on this a while back with VS 2015 using the built-in XP-targeting support, and I did manage to get a "Hello world" app running on Windows 2000. If anything, it should be easier with VS 2012 than VS 2015. But it was not easy, and it would probably be a support nightmare for any real-world application. 也许可以蒙混过关了类似的伎俩VS 2012年我开始做这项工作了一段时间后与VS 2015年使用内置在XP定向支持,我还是设法得到一个“Hello World”应用程序在Windows 2000上运行如果有的话,VS 2012比VS 2015应该更容易。但这并不容易,这可能是任何现实应用程序的支持梦night。 Still, it was a fun experiment, and it confirms what everyone already knows: the problem here is not the compiler or the linker. 尽管如此,这还是一个有趣的实验,它证实了每个人都已经知道的问题:这里的问题不是编译器或链接器。 The PE format is still the same; PE格式仍然相同; any compiler or linker that targets Win32 can build binaries that run on any version of Windows NT. 任何针对Win32的编译器或链接器都可以生成在任何版本的Windows NT上运行的二进制文件。 The problem is just the C runtime library attempting to call functions that do not exist on downlevel operating systems. 问题只是C运行时库试图调用下层操作系统上不存在的函数。

The way to go about testing this is to compile an EXE with VS 2012 using the above-described trick for EncodePointer/DecodePointer. 测试此方法的方法是使用上述EncodePointer / DecodePointer技巧,使用VS 2012编译EXE。 You will also, of course, need to make sure that you've set the minimum required version to 5.0 in the linker settings. 当然,您还需要确保已在链接器设置中将所需的最低版本设置为5.0。 (If this doesn't work, and it might not, you will need to manually change it as a post-build step using editbin.exe .) Then, simply try running that executable on Windows 2000. You will undoubtedly get an error message indicating that the application cannot start because of a missing statically-linked function. (如果这不起作用,也可能不起作用,那么您将需要使用editbin.exe构建后的步骤中手动进行更改。)然后,只需尝试在Windows 2000上运行该可执行文件即可。无疑,您会收到一条错误消息。指示由于缺少静态链接功能而导致应用程序无法启动。 You'll then need to research that function and stub it out, just as you had to do for EncodePointer/DecodePointer. 然后,您需要研究该功能并将其存根,就像对EncodePointer / DecodePointer所做的一样。 Chances are, it will be more difficult, because it is probably going to be a function that does meaningful work, which means that you can't simply NOP it out. 可能会更加困难,因为它可能会执行有意义的工作,这意味着您不能简单地将其淘汰。 Once you've fixed the dependency on that function, repeat the process again for each function that the W2K loader complains about. 解决了对该函数的依赖性后,请对W2K加载程序抱怨的每个函数再次重复该过程。 (You can also use Dependency Walker or equivalent utilities to obtain this information.) Once you work through all of the non-existent functions, you'll finally have an EXE that runs. (您也可以使用Dependency Walker或等效的实用程序来获取此信息。)一旦完成所有不存在的功能,您最终将运行一个EXE。

For VS 2015, I had to write stubs for InitializeSListHead, GetModuleHandleEx, and SystemFunction036 (which is the export name for RtlGenRandom), in addition to EncodePointer and DecodePointer. 对于VS 2015,除了EncodePointer和DecodePointer之外,我还必须为InitializeSListHead,GetModuleHandleEx和SystemFunction036(这是RtlGenRandom的导出名称)编写存根。 I expect you'll have a similar experience with VS 2012. Replacing the first two is actually relatively straightforward. 我希望您在VS 2012上也有类似的经验。替换前两个实际上相对简单。 For InitializeSListHead, I just reverse-engineered the corresponding functions on Windows XP, and wrote my own implementation for downlevel OS versions. 对于InitializeSListHead,我只是对Windows XP上的相应功能进行了反向工程,并为下层OS版本编写了自己的实现。 For GetModuleHandleEx, it's only called by the CRT in a context that enables support for managed apps. 对于GetModuleHandleEx,它仅由CRT在启用对托管应用程序的支持的上下文中调用。 Since I didn't care about those, I just turned it into a no-op that returns failure. 由于我不在乎这些,因此我将其变成了无操作的返回失败的操作。 SystemFunction036 (RtlGenRandom) is more difficult, but if you're not using rand (and you probably shouldn't be), then you don't need it either. SystemFunction036(RtlGenRandom)更加困难,但是如果您不使用rand(并且您可能不应该使用),那么您也不需要它。 I just stubbed it as a breakpoint ( int 3 ). 我只是将其存为断点( int 3 )。 You could also stub it to call CryptGenRandom. 您也可以将其存根以调用CryptGenRandom。 If you do better with code than prose, here is an approximation of the stubs I used in my "Hello world" app: 如果您在代码方面的表现胜于散文,这是我在“ Hello world”应用中使用的存根的近似值:

.386
.MODEL flat, stdcall


.DATA
   ;; Override the import symbols from kernel32.dll
   __imp__InitializeSListHead@4    DWORD DownlevelInitializeSListHead
   __imp__GetModuleHandleExW@12    DWORD DownlevelGetModuleHandleExW
   EXTERNDEF STDCALL __imp__InitializeSListHead@4 : DWORD
   EXTERNDEF STDCALL __imp__GetModuleHandleExW@12 : DWORD

   ;; Declare functions that we will call statically
   EXTRN STDCALL _imp__GetModuleHandleW@4  : DWORD
   EXTRN STDCALL _imp__GetProcAddress@8    : DWORD

CONST SEGMENT
   kszKernel32            DB 'k', 00H, 'e', 00H, 'r', 00H, 'n', 00H, 'e', 00H, 'l', 00H, '3', 00H, '2', 00H, 00H, 00H
   kszAdvApi32            DB 'a', 00H, 'd', 00H, 'v', 00H, 'a', 00H, 'p', 00H, 'i', 00H, '3', 00H, '2', 00H, 00H, 00H
   kszInitializeSListHead DB "InitializeSListHead", 00H
   kszGetModuleHandleExW  DB "GetModuleHandleExW", 00H

  ; Windows XP and Server 2003 and later have RtlGenRandom, which is exported as SystemFunction036.
  ; If needed, we could fall back to CryptGenRandom(), but that will be much slower
  ; because it has to drag in the entire crypto API.
  ; (See also: https://blogs.msdn.microsoft.com/michael_howard/2005/01/14/cryptographically-secure-random-number-on-windows-without-using-cryptoapi/)
   kszSystemFunction036   DB "SystemFunction036", 00H
CONST ENDS

.CODE

   ; C++ translation:
   ;    extern "C" VOID WINAPI DownlevelInitializeSListHead(PSLIST_HEADER pHead)
   ;    {
   ;       const HMODULE hmodKernel32 = ::GetModuleHandleW(L"kernel32");
   ;       typedef decltype(InitializeSListHead)* pfnInitializeSListHead;
   ;       const pfnInitializeSListHead pfn = reinterpret_cast<pfnInitializeSListHead>(::GetProcAddress(hmodKernel32, "InitializeSListHead"));
   ;       if (pfn)
   ;       {
   ;          // call WinAPI function
   ;          pfn(pHead);
   ;       }
   ;       else
   ;       {
   ;          // fallback implementation for downlevel
   ;          pHead->Alignment = 0;
   ;       }
   ;    }
   DownlevelInitializeSListHead PROC
      ;; Get a handle to the DLL containing the function of interest.
      push  OFFSET kszKernel32
      call  DWORD PTR _imp__GetModuleHandleW@4  ; Returns the handle to the library in EAX.

       ;; Attempt to obtain a pointer to the function of interest.
      push  OFFSET kszInitializeSListHead       ; Push 2nd parameter (string containing function's name).
      push  eax                                 ; Push 1st parameter (handle to the library).
      call  DWORD PTR _imp__GetProcAddress@8    ; Returns the pointer to the function in EAX.

      ;; Test for success, and call the function if we succeeded.
      test  eax, eax                            ; See if we successfully retrieved a pointer to the function.
      je    SHORT FuncNotSupported              ; Jump on failure (ptr == 0), or fall through in the most-likely case.
      jmp   eax                                 ; We succeeded (ptr != 0), so tail-call the function.

      ;; The dynamic call failed, presumably because the function isn't available.
      ;; So do what _RtlInitializeSListHead@4 (which is what we jump to on uplevel platforms) does,
      ;; which is to set pHead->Alignment to 0. It is a QWORD-sized value, so 32-bit code must
      ;; clear both of the DWORD halves.
   FuncNotSupported:
      mov   edx, DWORD PTR [esp+4]      ; get pHead->Alignment
      xor   eax, eax
      mov   DWORD PTR [edx],   eax      ; pHead->Alignment = 0
      mov   DWORD PTR [edx+4], eax
      ret   4
   DownlevelInitializeSListHead ENDP


   ; C++ translation:
   ;     extern "C" BOOL WINAPI DownlevelGetModuleHandleExW(DWORD dwFlags, LPCTSTR lpModuleName, HMODULE* phModule)
   ;     {
   ;        const HMODULE hmodKernel32 = ::GetModuleHandleW(L"kernel32");
   ;        typedef decltype(GetModuleHandleExW)* pfnGetModuleHandleExW;
   ;        const pfnGetModuleHandleExW pfn = reinterpret_cast<pfnGetModuleHandleExW>(::GetProcAddress(hmodKernel32, "GetModuleHandleExW"));
   ;        if (pfn)
   ;        {
   ;           // call WinAPI function
   ;           return pfn(dwFlags, lpModuleName, phModule);
   ;        }
   ;        else
   ;        {
   ;           // fallback for downlevel: return failure
   ;           return FALSE;
   ;        }
   ;     }
   DownlevelGetModuleHandleExW PROC
      ;; Get a handle to the DLL containing the function of interest.
      push  OFFSET kszKernel32
      call  DWORD PTR _imp__GetModuleHandleW@4  ; Returns the handle to the library in EAX.

       ;; Attempt to obtain a pointer to the function of interest.
      push  OFFSET kszGetModuleHandleExW        ; Push 2nd parameter (string containing function's name).
      push  eax                                 ; Push 1st parameter (handle to the library).
      call  DWORD PTR _imp__GetProcAddress@8    ; Returns the pointer to the function in EAX.

      ;; Test for success, and call the function if we succeeded.
      test  eax, eax                            ; See if we successfully retrieved a pointer to the function.
      je    SHORT FuncNotSupported              ; Jump on failure (ptr == 0), or fall through in the most-likely case.
      jmp   eax                                 ; We succeeded (ptr != 0), so tail-call the function.

      ;; The dynamic call failed, presumably because the function isn't available.
      ;; The basic VS 2015 CRT (used in a simple Win32 app) only calls this function
      ;; in try_cor_exit_process(), as called from common_exit (both in exit.cpp),
      ;; where it uses it to attempt to get a handle to the module mscoree.dll.
      ;; Since we don't care about managed apps, that attempt should rightfully fail.
      ;; If this turns out to be used in other contexts, we'll need to revisit this
      ;; and implement a proper fallback.
  FuncNotSupported:
      xor   eax, eax   ; return failure
      ret   12
   DownlevelGetModuleHandleExW ENDP


   DownlevelSystemFunction036 PROC
      int 3   ; break --- stub unimplemented
      ret 8
   DownlevelSystemFunction036 ENDP

END

Roy points out in a comment that Microsoft has provided an MIT-licensed implementation of SList that only requires InterlockedCompareExchange(). Roy在评论中指出, Microsoft提供了MIT许可的SList实现,实现仅需要InterlockedCompareExchange()。 This will make your job slightly easier, as you won't have to reverse-engineer any of the SList functions as I did. 这将使您的工作稍微容易一些,因为您不必像我一样对任何SList函数进行反向工程。

It goes without saying that you should avoid at all costs MFC, ATL, and other libraries whose source code is outside of your control. 不用说,您应该不惜一切代价避免使用MFC,ATL和其他源代码超出您控制范围的库。 They will drag in dependencies on functions that are not available on downlevel versions of the operating system, causing even more work for you. 它们将依赖于较低版本的操作系统上不可用的功能,从而为您带来更多工作。 You will really need to limit yourself to raw Win32, meaning that the only library you'll have to worry about is the CRT. 您确实需要将自己限制为原始Win32,这意味着您唯一需要担心的库是CRT。

Whew! ew! That should get you started. 那应该让您开始。 If, instead of being intrigued, you're massively intimidated by this, you almost certainly have no business with hacks such as this. 如果您对此深深着迷,而不是被它吸引住了,那么您几乎可以肯定与这种黑客毫无关系。 Use an older version of the compiler. 使用较旧版本的编译器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用Direct8 dll编译/调试Visual Studio 2012 Win32项目 - How can I compile/debug Visual Studio 2012 Win32 project with Direct8 dlls Windows 8的Visual Studio 2012不显示Win32控制台应用程序选项 - Visual Studio 2012 for windows 8 not showing win32 console application option Visual Studio:如何在“Win32”项目中获取输出 - Visual Studio: how to get output in “Win32” project 向安装项目Win32应用程序Visual Studio添加依赖项 - Adding dependencies to a setup project Win32 application Visual Studio 是否可以在Windows 8 OS和Visual Studio 2012上通过GUI创建完整的Vista win32应用程序? - Can I create full fledged Vista win32 applications with GUI using Windows 8 OS with Visual Studio 2012? 将C ++ Win32 Console项目类集成到Visual Studio 2008中的Visual C ++(Windows窗体应用程序)项目中 - Integrate C++ Win32 Console project classes into Visual C++ (Windows forms application) project in Visual Studio 2008 Visual Studio 2012项目使用MFC定位Windows XP - Visual Studio 2012 Project targeting Windows XP using MFC 工作目录Win32 Visual Studio - working directory win32 visual studio Visual Studio:“不是 Win32 应用程序”错误 - Visual Studio: "Is not a Win32 application" error 如何在Visual Studio 2012中为基于Win32 Dialog的应用程序启用“插入Activex控件”? - How to enable 'Insert Activex Control' in Visual Studio 2012 for a Win32 Dialog based application?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM