typedef BOOL (__stdcall *ISWOW64PROCESS)(HANDLE hProcess, PBOOL Wow64Process);
typedef BOOL (__stdcall *WOW64ENABLEWOW64FSREDIRECTION)(BOOL Wow64FsEnableRedirection);
pGetSystemWow64DirectoryA = (PGET_SYSTEM_WOW64_DIRECTORY_A) GetProcAddress(hKernel32, GET_SYSTEM_WOW64_DIRECTORY_NAME_A_A);
pIsWow64Process = (ISWOW64PROCESS) GetProcAddress(hKernel32, "IsWow64Process");
pWow64EnableWow64FsRedirection = (WOW64ENABLEWOW64FSREDIRECTION) GetProcAddress(hKernel32, "Wow64EnableWow64FsRedirection");
fprintf(stderr, "%s -未知的二进制类型-%d/n", cmd, type);
if (pGetSystemWow64DirectoryA(syswow64, sizeof(syswow64)))
NtQueryInformationProcess()这个函数可返回大多数种类的信息,但返回的数据不能放入一个32位的寻址空间内,转换机制显然会把它替换成零。那么我们怎样收集信息呢?可以创建一些数据结构,并小心地把它们扩展到64位,然后传递给Wow64版本的函数。如下的一个程序演示了如何使用包括GetParentProcessId()和GetCurrentDirectoryExW()在内的这些API。实际上,NtQueryInformationProcess()也可查询到父进程ID,演示程序读取了远程32位或64位进程的进程环境块(PEB),并打印出当前工作目录;由此可看出,一个Wow64进程似乎有一个32位和64位的进程环境块。
头文件
nativeinterface.h
#pragma once
#include
#include
class NTProcessInformation
{
public:
NTProcessInformation(HANDLE hProcess = GetCurrentProcess());
NTProcessInformation(DWORD pid);
bool _declspec(property(get=getok)) ok;
__w64 ULONG_PTR __declspec(property(get=getpid)) pid;
__w64 ULONG_PTR __declspec(property(get=getppid)) ppid;
HANDLE __declspec(property(get=gethprocess)) hProcess;
bool __declspec(property(get=getusewow64apis)) UseWow64Apis;
VOID * __ptr64 __declspec(property(get=getpa)) PebBaseAddress;
inline ULONG_PTR getpid(void) {return x_Pid;}
inline ULONG_PTR getppid(void) {return x_PPid;}
inline VOID * __ptr64 getpa(void) {return x_PebAddress;}
inline bool getok(void) {return x_OK;}
inline bool getusewow64apis(void) {return x_UseWow64Apis;}
inline HANDLE gethprocess(void) {return x_hProcess;}
bool IsWow64Process(HANDLE hProcess = 0);
protected:
void CommonConstruct(void);
__w64 ULONG_PTR x_Pid;
__w64 ULONG_PTR x_PPid;
DWORD x_ExitStatus;
VOID * __ptr64 x_PebAddress;
HANDLE x_hProcess;
bool x_OK;
bool x_UseWow64Apis;
};
class NTPEB
{
public:
NTPEB(HANDLE hProcess = GetCurrentProcess());
NTPEB(DWORD pid);
NTPEB(NTProcessInformation &ntpi);
bool _declspec(property(get=getok)) ok;
bool __declspec(property(get=getusewow64apis)) UseWow64Apis;
HANDLE __declspec(property(get=gethprocess)) hProcess;
VOID * __ptr64 __declspec(property(get=getpp)) ProcessParameters;
inline VOID * __ptr64 getpp(void) {return x_ProcessParameters;}
inline bool getok(void) {return x_OK;}
inline bool getusewow64apis(void) {return x_UseWow64Apis;}
inline HANDLE gethprocess(void) {return x_hProcess;}
protected:
void CommonConstruct(NTProcessInformation &ntpi);
VOID * __ptr64 x_ProcessParameters;
bool x_OK;
bool x_UseWow64Apis;
HANDLE x_hProcess;
};
class NTProcessParameters
{
public:
NTProcessParameters(HANDLE hProcess = GetCurrentProcess());
NTProcessParameters(DWORD pid);
NTProcessParameters(NTPEB &peb);
bool _declspec(property(get=getok)) ok;
std::wstring _declspec(property(get=getcwd)) cwd;
inline std::wstring getcwd(void) {return x_pwd;}
inline bool getok(void) {return x_OK;}
protected:
void CommonConstruct(NTPEB &peb);
std::wstring x_pwd;
bool x_OK;
};
头文件实现文件
nativeinterface.cpp
#include
#include
#include "nativeinterface.h"
#include "ntdll_lite.h"
typedef BOOL (__stdcall *ISWOW64PROCESS)(HANDLE hProcess, PBOOL Wow64Process);
typedef NTSTATUS (__stdcall *NTQUERYINFORMATIONPROCESS)(IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength OPTIONAL);
#if !defined(_WIN64)
typedef NTSTATUS (__stdcall *NTWOW64QUERYINFORMATIONPROCESS64)(IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PSIZE_T64 ReturnLength OPTIONAL);
typedef NTSTATUS (__stdcall *NTWOW64READVIRTUALMEMORY64)( IN HANDLE ProcessHandle, IN PVOID64_ BaseAddress, OUT PVOID Buffer, IN SIZE_T64 BufferSize, OUT PSIZE_T64 NumberOfBytesRead OPTIONAL);
#endif
ISWOW64PROCESS pIsWow64Process = 0;
NTQUERYINFORMATIONPROCESS pNtQueryInformationProcess = 0;
#if !defined(_WIN64)
NTWOW64QUERYINFORMATIONPROCESS64 pNtWow64QueryInformationProcess64 = 0;
NTWOW64READVIRTUALMEMORY64 pNtWow64ReadVirtualMemory64 = 0;
#endif
static BOOL bLoaded = FALSE;
static BOOL LoadDlls(void)
{
HMODULE hKernel32 = GetModuleHandle("kernel32.dll");
HMODULE hNtDll = GetModuleHandle("ntdll.dll");
if ((0 == hKernel32 ) || (0 == hNtDll))
{return FALSE;}
pIsWow64Process = (ISWOW64PROCESS) GetProcAddress(hKernel32, "IsWow64Process");
pNtQueryInformationProcess = (NTQUERYINFORMATIONPROCESS) GetProcAddress(hNtDll, "NtQueryInformationProcess");
#if !defined(_WIN64)
pNtWow64QueryInformationProcess64 = (NTWOW64QUERYINFORMATIONPROCESS64) GetProcAddress(hNtDll, "NtWow64QueryInformationProcess64");
pNtWow64ReadVirtualMemory64 = (NTWOW64READVIRTUALMEMORY64) GetProcAddress(hNtDll, "NtWow64ReadVirtualMemory64");
#endif
bLoaded = TRUE;
return (0 != pNtQueryInformationProcess);
}
NTProcessInformation::NTProcessInformation(HANDLE hProcess)
: x_Pid(-1)
, x_PPid(-1)
, x_ExitStatus(STILL_ACTIVE)
, x_PebAddress(0)
, x_hProcess(hProcess)
, x_OK(false)
, x_UseWow64Apis(false)
{
CommonConstruct();
}
NTProcessInformation::NTProcessInformation(DWORD pid)
: x_Pid(-1)
, x_PPid(-1)
, x_ExitStatus(STILL_ACTIVE)
, x_PebAddress(0)
, x_hProcess(0)
, x_OK(false)
, x_UseWow64Apis(false)
{
x_hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
CommonConstruct();
}
void NTProcessInformation::CommonConstruct(void)
{
if (!bLoaded)
{
if (!LoadDlls())
{return;}
}
DWORD err;
PROCESS_BASIC_INFORMATION info;
ULONG realsize;
if (ERROR_SUCCESS != (err = pNtQueryInformationProcess(x_hProcess, ProcessBasicInformation, (PVOID) &info, sizeof(info), &realsize)))
{return;}
if (0 == info.PebBaseAddress)
{
#if defined(_WIN64)
return;
#else
PROCESS_BASIC_INFORMATION64 info64;
SIZE_T64 realsize64;
if (!IsWow64Process(GetCurrentProcess()))
{return;}
if (0 == pNtWow64QueryInformationProcess64)
{return;}
if (ERROR_SUCCESS != (err = pNtWow64QueryInformationProcess64(x_hProcess, ProcessBasicInformation, (PVOID) &info64, sizeof(info64), &realsize64)))
{return;}
x_Pid = (ULONG_PTR) info64.UniqueProcessId;
x_PPid = (ULONG_PTR) info64.InheritedFromUniqueProcessId;
x_ExitStatus = info64.ExitStatus;
x_PebAddress = info64.PebBaseAddress;
x_OK = true;
x_UseWow64Apis = true;
#endif
}
else
{
x_Pid = info.UniqueProcessId;
x_PPid = info.InheritedFromUniqueProcessId;
x_ExitStatus = info.ExitStatus;
x_PebAddress = info.PebBaseAddress;
x_OK = true;
}
}
bool NTProcessInformation::IsWow64Process(HANDLE hProcess)
{
if (pIsWow64Process)
{
BOOL IsWow64 = FALSE;
if (pIsWow64Process(hProcess ? hProcess : x_hProcess, &IsWow64))
{return IsWow64 ? true : false;}
}
return false;
}
NTPEB::NTPEB(HANDLE hProcess)
: x_ProcessParameters(0)
, x_OK(false)
, x_UseWow64Apis(false)
, x_hProcess(0)
{
NTProcessInformation info(hProcess);
CommonConstruct(info);
}
NTPEB::NTPEB(DWORD pid)
: x_ProcessParameters(0)
, x_OK(false)
, x_UseWow64Apis(false)
, x_hProcess(0)
{
NTProcessInformation info(pid);
CommonConstruct(info);
}
NTPEB::NTPEB(NTProcessInformation &ntpi)
: x_ProcessParameters(0)
, x_OK(false)
, x_UseWow64Apis(false)
, x_hProcess(0)
{
CommonConstruct(ntpi);
}
void NTPEB::CommonConstruct(NTProcessInformation &ntpi)
{
if (!bLoaded)
{
if (!LoadDlls())
{return;}
}
x_hProcess = ntpi.hProcess;
if (ntpi.UseWow64Apis)
{
#if defined(_WIN64)
return;
#else
PEB64 peb64;
SIZE_T64 realsize64;
NTSTATUS err;
if (ERROR_SUCCESS != (err = pNtWow64ReadVirtualMemory64(x_hProcess, ntpi.getpa(), &peb64, sizeof(PEB64), &realsize64)))
{return;}
x_UseWow64Apis = true;
x_ProcessParameters = peb64.ProcessParameters;
#endif
}
else
{
PEB peb;
SIZE_T realsize;
if (FALSE == ReadProcessMemory(x_hProcess, Ptr64ToPtr(ntpi.getpa()), &peb, sizeof(PEB), &realsize))
{return;}
x_ProcessParameters = peb.ProcessParameters;
}
x_OK = true;
}
NTProcessParameters::NTProcessParameters(HANDLE hProcess)
: x_OK(false)
{
NTPEB peb(hProcess);
CommonConstruct(peb);
}
NTProcessParameters::NTProcessParameters(DWORD pid)
: x_OK(false)
{
NTPEB peb(pid);
CommonConstruct(peb);
}
NTProcessParameters::NTProcessParameters(NTPEB &peb)
: x_OK(false)
{
CommonConstruct(peb);
}
void NTProcessParameters::CommonConstruct(NTPEB &peb)
{
if (!bLoaded)
{
if (!LoadDlls())
{return;}
}
if (peb.UseWow64Apis)
{
#if defined(_WIN64)
return;
#else
PROCESS_PARAMETERS64 ProcParams64;
SIZE_T64 realsize64;
NTSTATUS err;
if (ERROR_SUCCESS != (err = pNtWow64ReadVirtualMemory64(peb.hProcess, peb.getpp(), &ProcParams64, sizeof(PROCESS_PARAMETERS64), &realsize64)))
{return;}
SIZE_T64 len = ProcParams64.CurrentDirectory.DosPath.Length;
wchar_t *buffer = new wchar_t[(SIZE_T) (len+1)];
if (ERROR_SUCCESS != (err = pNtWow64ReadVirtualMemory64(peb.hProcess, ProcParams64.CurrentDirectory.DosPath.Buffer, buffer, len * sizeof(wchar_t), &realsize64)))
{return;}
buffer[realsize64/2] = '/0';
x_pwd = buffer;
delete [] buffer;
#endif
}
else
{
PROCESS_PARAMETERS ProcParams;
SIZE_T realsize;
if (FALSE == ReadProcessMemory(peb.hProcess, Ptr64ToPtr(peb.getpp()), &ProcParams, sizeof(PROCESS_PARAMETERS), &realsize))
{return;}
SIZE_T len = ProcParams.CurrentDirectory.DosPath.Length;
wchar_t *buffer = new wchar_t[len+1];
if (FALSE == ReadProcessMemory(peb.hProcess, ProcParams.CurrentDirectory.DosPath.Buffer, buffer, len*sizeof(wchar_t), &realsize))
{return;}
buffer[realsize/2] = '/0';
x_pwd = buffer;
delete [] buffer;
}
x_OK = true;
}
相关头文件
ntdll_lite.h
#pragma once
typedef LONG NTSTATUS;
typedef LONG KPRIORITY;
#if (_MSC_VER < 1300)
typedef ULONG *ULONG_PTR;
#endif
typedef enum _PROCESSINFOCLASS
{
ProcessBasicInformation,
ProcessQuotaLimits,
ProcessIoCounters,
ProcessVmCounters,
ProcessTimes,
ProcessBasePriority,
ProcessRaisePriority,
ProcessDebugPort,
ProcessExceptionPort,
ProcessAccessToken,
ProcessLdtInformation,
ProcessLdtSize,
ProcessDefaultHardErrorMode,
ProcessIoPortHandlers,
ProcessPooledUsageAndLimits,
ProcessWorkingSetWatch,
ProcessUserModeIOPL,
MaxProcessInfoClass
} PROCESSINFOCLASS;
typedef PVOID PPEB_LDR_DATA;
typedef PVOID PPEB_FREE_BLOCK;
#if !defined(_WIN64)
#if (_MSC_VER < 1300)
typedef unsigned __int64 PCHAR64;
typedef unsigned __int64 HANDLE64;
typedef unsigned __int64 PVOID64_;
#else
typedef char * __ptr64 PCHAR64;
typedef void * __ptr64 HANDLE64;
typedef void * __ptr64 PVOID64_;
#endif
typedef PVOID64_ PPEB_LDR_DATA64;
typedef PVOID64_ PPEB_FREE_BLOCK64;
typedef unsigned __int64 SIZE_T64, *PSIZE_T64;
#endif
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PCHAR Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
#if !defined(_WIN64)
typedef struct _UNICODE_STRING64 {
USHORT Length;
USHORT MaximumLength;
PCHAR64 Buffer;
} UNICODE_STRING64;
typedef UNICODE_STRING64 *PUNICODE_STRING64;
#endif
typedef struct _CURRENT_DIRECTORY {
UNICODE_STRING DosPath;
HANDLE Handle;
} CURRENT_DIRECTORY, *PCURRENT_DIRECTORY;
#if !defined(_WIN64)
typedef struct _CURRENT_DIRECTORY64 {
UNICODE_STRING64 DosPath;
HANDLE64 Handle;
} CURRENT_DIRECTORY64, *PCURRENT_DIRECTORY64;
#endif
typedef struct _PROCESS_PARAMETERS {
ULONG MaximumLength;
ULONG Length;
ULONG Flags;
ULONG DebugFlags;
HANDLE ConsoleHandle;
ULONG ConsoleFlags;
HANDLE StandardInput;
HANDLE StandardOutput;
HANDLE StandardError;
CURRENT_DIRECTORY CurrentDirectory;
UNICODE_STRING DllPath;
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
PVOID Environment;
ULONG StartingX;
ULONG StartingY;
ULONG CountX;
ULONG CountY;
ULONG CountCharsX;
ULONG CountCharsY;
ULONG FillAttribute;
ULONG WindowFlags;
ULONG ShowWindowFlags;
UNICODE_STRING WindowTitle;
UNICODE_STRING DesktopInfo;
UNICODE_STRING ShellInfo;
UNICODE_STRING RuntimeData;
} PROCESS_PARAMETERS, *PPROCESS_PARAMETERS;
#if !defined(_WIN64)
typedef struct _PROCESS_PARAMETERS64 {
ULONG MaximumLength;
ULONG Length;
ULONG Flags;
ULONG DebugFlags;
HANDLE64 ConsoleHandle;
ULONG ConsoleFlags;
HANDLE64 StandardInput;
HANDLE64 StandardOutput;
HANDLE64 StandardError;
CURRENT_DIRECTORY64 CurrentDirectory;
UNICODE_STRING64 DllPath;
UNICODE_STRING64 ImagePathName;
UNICODE_STRING64 CommandLine;
PVOID64_ Environment;
ULONG StartingX;
ULONG StartingY;
ULONG CountX;
ULONG CountY;
ULONG CountCharsX;
ULONG CountCharsY;
ULONG FillAttribute;
ULONG WindowFlags;
ULONG ShowWindowFlags;
UNICODE_STRING64 WindowTitle;
UNICODE_STRING64 DesktopInfo;
UNICODE_STRING64 ShellInfo;
UNICODE_STRING64 RuntimeData;
} PROCESS_PARAMETERS64
#if (_MSC_VER < 1300)
;
typedef unsigned __int64 PPROCESS_PARAMETERS64;
#else
, *PPROCESS_PARAMETERS64;
#endif
#endif
typedef struct _PEB {
BOOLEAN InheritedAddressSpace;
HANDLE Mutant;
PVOID ImageBaseAddress;
PPEB_LDR_DATA Ldr;
PPROCESS_PARAMETERS ProcessParameters;
PVOID SubSystemData;
PVOID ProcessHeap;
PVOID FastPebLock;
PVOID FastPebLockRoutine;
PVOID FastPebUnlockRoutine;
PVOID Spare[4];
PPEB_FREE_BLOCK FreeList;
ULONG TlsExpansionCounter;
PVOID TlsBitmap;
ULONG TlsBitmapBits[2];
PVOID ReadOnlySharedMemoryBase;
PVOID ReadOnlySharedMemoryHeap;
PVOID *ReadOnlyStaticServerData;
PVOID AnsiCodePageData;
PVOID OemCodePageData;
PVOID UnicodeCaseTableData;
LARGE_INTEGER CriticalSectionTimeout;
} PEB, *PPEB;
#if !defined(_WIN64)
typedef struct _PEB64 {
BOOLEAN InheritedAddressSpace;
HANDLE64 Mutant;
PVOID64_ ImageBaseAddress;
PPEB_LDR_DATA64 Ldr;
#if (_MSC_VER < 1300)
PPROCESS_PARAMETERS64 ProcessParameters;
#else
PROCESS_PARAMETERS64 *__ptr64 ProcessParameters;
#endif
PVOID64_ SubSystemData;
PVOID64_ ProcessHeap;
PVOID64_ FastPebLock;
PVOID64_ FastPebLockRoutine;
PVOID64_ FastPebUnlockRoutine;
PVOID64_ Spare[4];
PPEB_FREE_BLOCK64 FreeList;
ULONG TlsExpansionCounter;
PVOID64_ TlsBitmap;
ULONG TlsBitmapBits[2];
PVOID64_ ReadOnlySharedMemoryBase;
PVOID64_ ReadOnlySharedMemoryHeap;
PVOID64_ *ReadOnlyStaticServerData;
PVOID64_ AnsiCodePageData;
PVOID64_ OemCodePageData;
PVOID64_ UnicodeCaseTableData;
LARGE_INTEGER CriticalSectionTimeout;
} PEB64
#if (_MSC_VER < 1300)
;
typedef unsigned __int64 PPEB64;
#else
, * __ptr64 PPEB64;
#endif
#endif
typedef struct _PROCESS_BASIC_INFORMATION {
NTSTATUS ExitStatus;
PPEB PebBaseAddress;
ULONG_PTR AffinityMask;
KPRIORITY BasePriority;
ULONG_PTR UniqueProcessId;
ULONG_PTR InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;
typedef PROCESS_BASIC_INFORMATION *PPROCESS_BASIC_INFORMATION;
#if !defined(_WIN64)
typedef struct _PROCESS_BASIC_INFORMATION64 {
NTSTATUS ExitStatus;
PPEB64 PebBaseAddress;
ULONG64 AffinityMask;
KPRIORITY BasePriority;
ULONG64 UniqueProcessId;
ULONG64 InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION64;
typedef PROCESS_BASIC_INFORMATION64 *PPROCESS_BASIC_INFORMATION64;
#endif
主程序
#include
#include "nativeinterface.h"
#define DEBUG
BOOL GetCurrentDirectoryExW(HANDLE hProcess, wchar_t *buffer, SIZE_T buflen)
{
NTProcessParameters params(hProcess);
if (!params.ok)
{return false;}
std::wstring result = params.cwd;
SIZE_T len = min(buflen-1, result.length());
memcpy(buffer, result.c_str(), sizeof(wchar_t) * len);
buffer[len] = L'/0';
return TRUE;
}
ULONG_PTR GetParentProcessIdEx(HANDLE hProcess)
{
NTProcessInformation info(hProcess);
if (!info.ok)
{return -1;}
return info.ppid;
}
BOOL MyIsWow64Process(HANDLE hProcess)
{
NTProcessInformation info(hProcess);
if (!info.ok)
{return FALSE;}
return info.IsWow64Process() ? TRUE : FALSE;
}
#if defined(DEBUG)
void main(int argc, char **argv)
{
HANDLE hProcess;
if (1 == argc)
{hProcess = GetCurrentProcess();}
else
{
DWORD pid = atoi(argv[1]);
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (0 == hProcess)
{return;}
}
wchar_t path[4096];
printf("ppid = %d/n", GetParentProcessIdEx(hProcess));
printf(" 这 %s 是一个 Wow64 进程 /n", (MyIsWow64Process(hProcess) ? "" : " 不 "));
GetCurrentDirectoryExW(hProcess, path, sizeof(path));
printf(" 当前工作目前 = %ls/n", path);
}
#endif
可读取 64 位寻址空间——这意味着不但能读取其他 64 位进程的进程环境块( PEB ),而且还能观察到 Wow64 进程的 64 位部分。如NtWow64WriteVirtualMemory64()这些分配和修改虚拟内存的函数一样,可加载一个64位DLL到Wow64进程的64位部分。
Wow64程序面对的是虚拟化后的64位文件系统和注册表,但从一个32位系统发起的网络应用,如//server/admin$和ConnectRegistry(),可能面对的是一个真实的64位文件系统和注册表,所以,基于这些原因,copy 1234.dll //server/admin$/system32这行命令可能会达不到预期的目的。
本地64 位移植
在 Windows 平台上,需要多于 4GB 内存的程序越来越多,这些程序要么在 AWE 或 PAE 上苦苦挣扎,要么转而栖身到其他的 64 位平台,如 Solaris 。以下列出的一些原因,可能会使你选择重新编译生成本地 64 位二进制文件。
无论是静态还是动态的 32 位库,都不能链接到 64 位的可执行文件,反之亦然,但是如果提供中间件,或者计划部署到 64 位 Windows 上,那就需要移植了——最初我们是移植到 IA64 上,现在是 x64 。基于 COM 的 DLL 在此是个例外,它们在 32 位的宿主中已经被代理了,即使通过网络连接时也是如此,但最终你会发现由此带来的进程内性能损失得不偿失,最终还是要把组件移植到 64 位 Windows 上。
如果你要发布一个Internet Explorer的插件,将会发现,32位版本的插件在64位Internet Explorer上无法使用。64位的Windows同时带有32位和64位的Internet Explorer,并且是按照每个窗口一个进程这样配置的,所以在64位平台上两者都能运行。因此,对64位Windows,必须同时发布32位和64位的插件。
如果有一个 16 位的 Windows 程序,因为没有Windows on Windows on Windows (WoWoW?)诸如的方案,所以16位程序不可能运行在x64上(会有诸如提示:映像文件有效,但不适用于此计算机类型)。
设备驱动程序也亦然,32位的设备驱动程序无法工作在64位的Windows上。
也许移植的目标是 IA64 ,但会发现,程序并没有预期中的性能,或者是因为程序使用了 AWE 和 PAE ,两者在 IA64 平台上均不被支持,但看起来似乎本地编译的 IA64 二进制文件比 32 位效率要高得多。此现象主要针对第一代安腾处理器,对于第二代安腾处理器,情况要好多了。或许 Windows 使用的是 32 位软件二进制仿真器,而不是本地硬件仿真器。
移植到新平台上,工作量非常巨大,一项普通的移植都需要严格的计划和时间上巨大的开销。为了找到问题的范围,无论如何都要挑出一些有代表性的源代码,试编译运行。听起来好像很简单,只是敲入 make (或者nmake、devenv /build )就行了,实际上,要做的工作远远不只这些。
那些没有源代码的程序,有库可用吗?在移植中要使用什么平台呢?是使用目标平台还是类似 x86 的桌面平台?对新平台来说,源代码配置管理系统( SCM )可用吗?从哪可得到编译器和链接器?所有的工具都适合 64 位开发吗?构建环境是否支持在同一源代码中生成多平台应用,还是需要多做一份拷贝?实际上,敲入 make 需要更多的前端工作,但最简单的解决方案是,拷贝一份源代码,安装好Microsoft Platform SDK,使用x86 to x64跨平台交叉编译器,而把其他诸如开发环境、生成工具、SCM等等,统统扔在32位Windows中不管。这也许是最大的一个惊喜了,在链接可执行文件前,甚至都不需要一台64位电脑。
正因为不能把32位的静态或动态库,链接到64位程序,所以,如果没有全部库的源代码,或者64位版本的库还不存在,那么移植方案就得暂时搁下。你需要在64位目标平台上找到一些东西,替换掉这些库,要么说服生产商提供这些库,或者干脆不移植。
接下来一个最大的问题是开发环境——包括编译器、链接器、调试器、源代码管理系统、 make 和集成开发环境( IDE )。
从哪可以得到一个编译器呢?在本文写作时,GNU C/C++还不行。Intel已经发布了商业版本的编译器,分别对应于IA64(本地或跨平台)、IA32E(Intel称其为EM64T x64平台)和IA32;微软也有一对版本号为14.0的编译器(VC.NET 2005),分别对应于x86 to IA64和x64,而通过Platform SDK,提供了跨平台交叉编译功能。实际上在Visual Studio.NET 2005中,微软已经提供了本地x64和IA64编译器。
集成开发环境(IDE)不需要是64位的,你能在32位系统上跨平台交叉编译,或者在64位平台上运行32的IDE。
从哪可以得到一个调试器呢? Windbg 有针对以上三个平台的版本,但对普通人来说,它还是有点不太好用,除非是黑客。Visual Studio.NET 2005有一个远程调试器服务端可用于以上两个64位平台。它仍是一个32位程序,所以你能得到一个混合的实现,在当前x86桌面系统上开发,而在64位平台上远程调试。
那配置管理系统呢?也许你的SCM已经支持相应64位平台开发了,如果没有,那只有在32位系统上跨平台交叉编译,或者拷贝源代码到64位平台上。
现在我们可以准备生成程序了,所有为移植做的准备就是为了现在享受胜利的果实,但代码有多具移植性呢?
我们都知道sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long),但也要准备接受sizeof(long) != sizeof(void *)或者sizeof(int) != sizeof(void *)。大多数64位Unix系统定义sizeof(long) == sizeof(void *),这被称作LP64模型,此时long与指针都是64位的。Windows选择的是LLP64模型,此时对移植32位Windows源代码来说,sizeof(long long) == sizeof(void *)。这意味着,如果代码中把指针赋值给int(这是不可移植的,因为它不严谨且假定sizeof(int) == sizeof(long) == sizeof(void *)),或者把指针赋值给long,那就要在代码移植之前,把它们全部改正过来。记住,程序看起来也许运行得很正常,只要它位于64位寻址空间的低4GB位置,但malloc()和HeapAlloc()返回的值可能会在此之外,MapViewOfFile()也同样。
在很大程度上,编写良好的Win32代码只需稍作改动,便可通过编译。你可能会惊奇地发现,在很多地方,DWORD与PVOID几乎是同一样东西,只要认真遵循前面所提到的方法,源代码通过编译不是件难事,但是,记住在生成64位程序时使用-W3选项,对编译器的警告多加留意。
就以往的经验来说,需要为每一个 COM 组件生成一个新的 GUID ,但在 Windows x64 上,同一个组件的 32 位和 64 位版本(如Internet Explorer 工具栏 )可以共享一个 GUID ,并可在同一台电脑上,同时为它们的客户程序提供服务。事实上, COM 的实现是高度可伸缩性的,可以允许 32 位的组件被 64 位进程使用(反之亦然),或同时注册 32 位和 64 位的组件。换句话来说,如果你选择移植组件,可以保留 GUID 和接口定义;如果不移植(那些充当进程内服务的 IE 工具栏和插件必须要移植),多数情况下,它们都能正常运行,此时性能可能会受影响。
一些更棘手的问题都是围绕与 32 位程序共享二进制数据上,也许是通过文件,或者是共享内存、网络等等。在此有很多技术都可以用得上,但微软已经在它的编译器和头文件中内置了一些实用的工具。
微软编译器 cl 版本 13.0 或者更高(VS.NET 2003或后续版本 ),Platform SDK中的14.0版本,这些编译器都支持/Wp64选项。对每个源程序,只需简单地设置/Wp64选项,就可以看到5个(至少到目前为止)有用的警告;参见图1;以下的程序示范了这些警告,并且提供了一些有关移植的解决方法。
#include
#include
#include
#include
#include
#pragma comment(lib, "user32.lib")
void main(void)
{
void *ptr = 0;
int val;
intptr_t intptr;
size_t sizet;
HWND hWnd = GetDesktopWindow();
printf("%x/n", ptr);
printf("%p/n", ptr);
val = (int) ptr;
intptr = (intptr_t) ptr;
val = strlen("abc");
sizet = strlen("abc");
val = (int) hWnd;
val = HandleToUlong(hWnd);
hWnd = (HWND) UlongToHandle(val);
}
在头文件 BaseTsd.h 中,有一些可用的新内联函数和宏,如DWORD32、DWORD64、UINT_PTR、PointerToUlong()、Ptr64ToPtr()等。HWND在64位Windows上已是一个64位的变量,如果你想把它传递给一个32位进程,它的值会被截断,HandleToUlong()此时正好帮得上忙,但需注意的是,尽量不要让截断发生。*__ptr64强制一个指针变量占用64位,哪怕是在32位平台上,void * __ptr64 ptr就可强制在32位和64位平台上得到同样大小的变量,以保证二进制兼容;还有一个相应的__ptr32。显然,在我们发现8TB寻址空间不够用之前,是不会出现__ptr128的。
在把指针赋值给int时,尽量使用intptr_t,而不是int或long long。大多数编译器都提供了一个typedef,以保证对你的目标平台,变量有一个正确的大小。
删除源代码中的内联汇编语句,或者用一些“ C ”代码代替它,在生成程序时,也不要链接外部的汇编文件,因为__asm在IA64和x64编译器中会产生错误。为了确保二进制的兼容,请明智地决定联合体(Union)中变量的位置,以便结构数据对齐保持一致。参见以下程序:
#include
#include
#include
struct abc
{
HWND AWindow;
void * __ptr32 ThirtyTwoBitPointer;
void * __ptr64 SixtyFourBitPointer;
long ALong;
};
struct def
{
union
{
HWND AWindow;
long long reserved;
};
union
{
void * __ptr32 ThirtyTwoBitPointer;
long long reserved2;
};
void * __ptr64 SixtyFourBitPointer;
union
{
long ALong;
long long reserved3;
};
};
void main(void)
{
printf("%d/n", sizeof(abc));
printf("/tAWindow %p/n", offsetof(abc, AWindow));
printf("/t32bit pointer %p/n", offsetof(abc, ThirtyTwoBitPointer));
printf("/t64bit pointer %p/n", offsetof(abc, SixtyFourBitPointer));
printf("/tAlong %p/n", offsetof(abc, ALong));
printf("%d/n", sizeof(def));
printf("/tAWindow %p/n", offsetof(def, AWindow));
printf("/t32bit pointer %p/n", offsetof(def, ThirtyTwoBitPointer));
printf("/t64bit pointer %p/n", offsetof(def, SixtyFourBitPointer));
printf("/tAlong %p/n", offsetof(def, ALong));
}
IA64 平台,像多数 RISC 处理器一样,对数据对齐过分吹毛求疵,有以下代码:
unsigned char *ptr;
DWORD dword = (DWORD) (*(unsigned long long *) ptr);
此时使用一个memcpy(),也许是比赋值更好的选择。
也许你经常要为 32 位和 64 位生成不同代码,为此,微软的 64 位编译器定义了一个预处理变量_WIN64。微软的编译器同时也提供_M_IX86、_M_IA64、_M_AMD64,以便应付那些需要针对特定处理器生成代码的极端场合。
64位移植中最困难的事,恐怕就是代码生成环境了。如果你之前移植到其他的架构上(如Alpha、MIPS或PowerPC),可能会觉得很容易,平台提供的示例代码中,常含有一个调用cl和nmake的makefile,以便演示在不同平台上针对同一源代码如何编译,其思想是把平台与输出分离。但即使是把vc?.pdb文件放在正确位置,并且也把编译器临时目录分离,makefile在多平台同时生成程序时,还是会有问题。
如果你正在使用Visual Studio IDE,可以很容易地导出配置文件、指定输出目录$(OUTDIR)、按需调整好编译器,然后在目标平台上打开一个Platform SDK命令行窗口,用如下命令devenv /build /useenv myproject.sln "Win32 - Debug(X64)"来构建一个工程。可以考虑一下最新的VS.NET 2005和它的跨平台交叉编译器,只要在每一个配置中指定目标平台,就可以很容易地生成应用(或工程)。在AMD的官方网站上,有一个示例,演示了如何使用Visual Studio和Platform SDK,从现有的工程文件中,生成64位二进制程序。
最后当然还要说一下软件安装的问题,如果有一个面向32位、64位(IA64和x64)的混合安装程序,在一开始就需要仔细地计划。如果使用Windows Installer,那么针对每一个平台架构,都会有一个MSI文件。有趣的是,在64位的安装中,可使用32位的用户定制(Custom Action)DLL,这暗示着在此之下,有一对协同工作的32位和64位进程。但这不在本文讨论范畴之内。
结论
不是每个人都需要把他们的程序移植到本地64位平台上,事实上,如果依然作为运行在64位处理器上的32位程序,可能会更加小巧,方便部署——当然除了安腾处理器。目前唯一清楚的是,微软为它的32位仿真层做了大量卓越的工作,而64位处理器和主板,价格便宜量又足,它们会越来越受到欢迎,因此,不管是运行现有32位程序、或是新游戏、还是复杂的商业程序,我们都没有理由——不拥抱64位Windows!!