6

我需要修改几年前在 win32 中编写的一个非常古老的项目,该项目必须在 windows 2000 服务器上运行。

最近升级了我的电脑后,我搬到了 Visual Studio 2012,因此出现了我的问题。

我在这里阅读了很多帖子,我有点困惑。

首先使用平台工具集我需要安装 vs2008 和 vs2010 对吗?这是无法接受的。

其次,有些帖子说我需要覆盖函数 DecodePointer/EncodePointer 。

第三,只是使用定义

#ifndef WINVER
#define WINVER 0x0501
#endif

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

选项 1 是不可接受的。其他 2 个选项中的哪一个与 vs2012 一起使用???

谢谢大家。

4

3 回答 3

5

VS2012 编译器的运行时支持以 XP 为目标,但不支持早期版本。事实上,在发布时,不支持 XP 目标,这是在以后的更新中添加的。如果必须支持 Win2k,则必须使用支持 Win2k 的早期 VS 版本的工具集。

于 2013-10-22T11:40:39.820 回答
5

这是 Cody Gray 的 VS2017 代码的扩展。它可以使用一些额外的眼球来确保它能够正常工作。截至目前,应用程序将在使用 VS2017 构建的 NT4 上启动和运行。如果使用 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

还有第 2 部分:

// 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;
}
于 2018-11-29T21:49:36.857 回答
3

正如您所说,VS 2008 可以开箱即用地针对 Windows 2000。你不需要任何额外的东西。如果您更喜欢使用较新版本的 IDE,同时安装多个版本的 Visual Studio(始终先安装最旧的版本,及时“向前”工作)将允许您,例如,在 VS 2010 中工作,但告诉它构建使用 VS 2008 工具链。您显然无法从VS 2010 引入的编译器功能中受益,但您将可以使用更新的 IDE。

您可以使用 VS 2010 通过 EncodePointer/DecodePointer 技巧来定位 Windows 2000。这里的问题是 VS 2010 C 运行时库 (CRT) 需要这些函数(它在内部调用它们),但这些函数在 Windows XP SP2 之前的操作系统版本上不存在。但是,如果您编写存根并将可执行文件链接到那些(同时也是静态的)链接到 CRT,因此它实际上会找到并使用这些存根),然后您可以在 Windows 2000 上运行 VS 2010 编译的 EXE。请注意,您还需要在链接器设置中将所需的最低版本设置为 5.0。(当您这样做时,您会收到一个链接时警告,指出这不是一个有效的、受支持的版本,但您可以简单地忽略此警告。它确实有效,并且该字段确实在 PE 标头中正确设置。)无疑是一个肮脏的把戏,但我知道它很管用;我在我的几个项目中都这样做。如果它们在当前操作系统上可用,我的存根会动态调用真正的 EncodePointer/DecodePointer 函数,如果没有,则回退到基本上无操作(放弃这些低级操作系统的安全优势)。Suma 在对相关问题的回答中已经很好地涵盖了这个技巧.

和定义真的与此无关WINVER_WIN32_WINNT它们只是控制 Windows SDK 标头实际定义的函数原型。这个想法是您将这些设置为您的目标 Windows 版本,然后您将只能静态链接到该版本 Windows 上实际存在的函数。在适当版本的 Windows 上运行时,您仍然可以动态调用(通过 GetModuleHandle/LoadLibrary → GetProcAddress)较新的函数,如果它们不受支持,则可以优雅地回退。如果您尝试静态链接到不存在的函数,加载程序将在您尝试运行应用程序时生成错误。不过,这很容易,因为这完全在您作为开发人员的控制范围内。问题是当 CRT(一个库,你不要控制)调用不存在的函数,例如 EncodePointer。这就是为什么需要上述解决方法的原因。WINVER和的值对_WIN32_WINNT编译器或链接器没有实际影响。

对于 VS 2012,您可能会使用类似的技巧。我不久前开始使用 VS 2015 使用内置的 XP 定位支持来解决这个问题,并且我确实设法在 Windows 2000 上运行了一个“Hello world”应用程序. 如果有的话,VS 2012 应该比 VS 2015 更容易。但这并不容易,而且对于任何现实世界的应用程序来说,它都可能是支持噩梦。尽管如此,这是一个有趣的实验,它证实了每个人都已经知道的:这里的问题不是编译器或链接器。PE格式还是一样的;任何以 Win32 为目标的编译器或链接器都可以构建在任何版本的 Windows NT 上运行的二进制文件。问题只是 C 运行时库试图调用下层操作系统上不存在的函数。

进行测试的方法是使用上述 EncodePointer/DecodePointer 技巧使用 VS 2012 编译 EXE。当然,您还需要确保在链接器设置中将所需的最低版本设置为 5.0。(如果这不起作用,并且可能不起作用,您将需要手动更改它作为构建后步骤,使用editbin.exe.) 然后,只需尝试在 Windows 2000 上运行该可执行文件。毫无疑问,您会收到一条错误消息,指出应用程序由于缺少静态链接功能而无法启动。然后,您需要研究该函数并将其存根,就像对 EncodePointer/DecodePointer 所做的那样。很有可能,它会更困难,因为它可能会成为一个做有意义工作的函数,这意味着你不能简单地 NOP 出来。一旦您修复了对该函数的依赖关系,请为 W2K 加载程序抱怨的每个函数再次重复该过程。(您也可以使用 Dependency Walker 或等效实用程序来获取此信息。)一旦您完成了所有不存在的功能,您最终将拥有一个可以运行的 EXE。

对于 VS 2015,除了 EncodePointer 和 DecodePointer 之外,我还必须为 InitializeSListHead、GetModuleHandleEx 和 SystemFunction036(这是 RtlGenRandom 的导出名称)编写存根。我希望您对 VS 2012 会有类似的体验。替换前两个实际上相对简单。对于 InitializeSListHead,我只是在 Windows XP 上对相应的函数进行了逆向工程,并为低级 OS 版本编写了自己的实现。对于 GetModuleHandleEx,它仅由 CRT 在支持托管应用程序的上下文中调用。因为我不在乎那些,我只是把它变成了一个返回失败的空操作。SystemFunction036 (RtlGenRandom) 更难,但如果你不使用 rand (你可能不应该使用),那么你也不需要它。我只是将它作为断点存根(int 3)。您也可以将其存根以调用 CryptGenRandom。如果你的代码比散文做得更好,这里是我在“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 在评论中指出,微软提供了一个 MIT 许可的 SList 实现,它只需要 InterlockedCompareExchange()。这将使您的工作稍微容易一些,因为您不必像我那样对任何 SList 函数进行逆向工程。

不用说,您应该不惜一切代价避免使用 MFC、ATL 和其他源代码超出您控制范围的库。他们将拖累对操作系统低级版本不可用的功能的依赖,从而为您带来更多工作。您确实需要将自己限制为原始 Win32,这意味着您唯一需要担心的库是 CRT。

哇!那应该让你开始。如果您对此感到非常害怕,而不是对此感到好奇,那么您几乎可以肯定与这样的黑客无关。使用旧版本的编译器。

于 2016-10-05T04:52:56.320 回答