Win9x下有些系统函数只在Win16中存在,如GetFreeSystemResources
有些操作也只有在Win16环境中可以进行,如调用int 2fh等软中断服务,这时就需要调用Win16的DLL实现
Win9x下Win32程序可以使用Flat Thunk调用Win16的DLL,但官方的方法是使用Thunk Compiler,非常麻烦
使用Thunk Compiler的步骤(详见Q154093,注意早期MSDN将16位thunk dll的DllEntryPoint错写成DllMain):
1、需要编写一个32to16 thunk script(如果需要回调,还需要编写一个16to32 thunk script)
2、使用thunk compiler将32to16 thunk script编译成.asm(如果有16to32也需要编译)
3、按照要求使用MASM 6.1及以上版本将.asm汇编成32位和16位的两种.obj(如果有16to32也需要汇编)
4、按照要求编写16位thunk dll的DllEntryPoint和32位thunk dll的DllMain及各自的.def
5、编译链接16位和32位的thunk dll(链接对应版本的.obj,32位的thunk dll还要链接thunk32.lib)
6、在Win32程序中使用32位版本的thunk dll(在Win16程序中也可以使用16位版本的thunk dll)
实际上如果只是为了调用少量Win16函数,没必要这么麻烦,可以使用以下未文档化函数实现:
1、DWORD WINAPI LoadLibrary16(LPSTR) = kernel32.dll@35(按序号导出,获取方法见下文)
2、void WINAPI FreeLibrary16(DWORD) = kernel32.dll@36(按序号导出,获取方法见下文)
3、DWORD WINAPI GetProcAddress16(DWORD, LPSTR) = kernel32.dll@37(按序号导出,获取方法见下文)
4、QT_Thunk(按名称导出)(汇编函数,这个函数使用比较麻烦,见下文)
QT_Thunk使用比较麻烦------
1、QT_Thunk要求当前函数ebp-64到ebp是64字节的供QT_Thunk使用的缓冲区
2、edx指向要调用的16:16位函数指针
3、要使用16位PASCAL方式压入参数(push word ptr、顺序压入),call QT_Thunk后自动弹出
4、QT_Thunk不进行参数转换和地址映射,需手动进行
如果需要在16:16位分段地址和32位平坦地址之间映射,则需要使用MapLS、UnMapLS、MapSL等函数(按名称导出),需注意:
1、位于16位地址空间的16:16位分段地址需要GlobalFix后才会有不变的32位平坦地址(注意GMEM_FIXED和GlobalLock不起作用)
2、位于32位地址空间的地址在16位函数退出后可能因为进程切换而不再有效
也可以使用文档化的Generic Thunk的函数进行地址转换和16位内存分配
但是要注意Generic Thunk是为NTVDM设计的,在NT系统中Win32进程不能加载Win16的DLL,因此:
1、未包含与LoadLibrary16、FreeLibrary16、GetProcAddress16等价的函数
2、WOWCallback16和WOWCallback16Ex在Win9x的Win32进程中不可用(Win32程序必须使用Flat Thunk调用Win16的DLL函数)
3、Win9x的USER和GDI是16位的,因此不需要转换USER和GDI句柄,WOWHandle16和WOWHandle32始终返回相同值
4、Win9x下应使用WOWGetVDMPointerFix和WOWGetVDMPointerUnfix而不是WOWGetVDMPointer,除非已经GlobalFix
这些是可以在Win9x的Win32进程中使用的Generic Thunk函数(wownt32.h和wow32.lib):
WOWGetVDMPointer
WOWGetVDMPointerFix
WOWGetVDMPointerUnfix
WOWGlobalAlloc16
WOWGlobalAllocLock16
WOWGlobalFree16
WOWGlobalLock16
WOWGlobalLockSize16
WOWGlobalUnlock16
WOWGlobalUnlockFree16
WOWHandle16
WOWHandle32
另一个难点是Win9x的GetProcAddress拒绝解析kernel32.dll的导出序号,有两种方案:
1、手动解析kernel32.dll的PE文件头
2、使用下面的.def使用lib /def:xxx.def /out:xxx.lib生成一个导入库:
LIBRARY KERNEL32
EXPORTS
LoadLibrary16@4 @35
FreeLibrary16@4 @36
GetProcAddress16@8 @37
QT_Thunk和MapLS、UmMapLS、MapSL等函数可以:
1、直接链接thunk32.lib
2、在上面的导入库.def中声明
3、使用GetProcAddress获取地址
参考:
https://jacobfilipp.com/DrDobbs/articles/DDJ/1996/9614/9614c/9614c.htm
https://rgmroman.narod.ru/qtthunk.htm
https://rgmroman.narod.ru/flthunk.htm
获取未公开函数和16位函数指针:
cpp
typedef DWORD (WINAPI*PLoadLibrary16)(LPSTR);
typedef void (WINAPI*PFreeLibrary16)(DWORD);
typedef DWORD (WINAPI*PGetProcAddress16)(DWORD, LPSTR);
static PLoadLibrary16 pLoadLibrary16;
static PFreeLibrary16 pFreeLibrary16;
static PGetProcAddress16 pGetProcAddress16;
static FARPROC pQT_Thunk;
static DWORD huser;
static DWORD addrGetFreeSystemResources;
if ((GetVersion() & 0x80000000) && LOWORD(GetVersion()) >= 4) {
// Win9x GetProcAddress refuses to resolve kernel32.dll ordinals
HINSTANCE hk32;
PBYTE k32base;
PIMAGE_DOS_HEADER k32dos;
PIMAGE_NT_HEADERS k32nt;
PIMAGE_EXPORT_DIRECTORY k32exp;
PDWORD k32exparr;
hk32 = GetModuleHandle("KERNEL32.DLL");
k32base = (PBYTE)hk32;
k32dos = (PIMAGE_DOS_HEADER)k32base;
k32nt = (PIMAGE_NT_HEADERS)(k32base + k32dos->e_lfanew);
k32exp = (PIMAGE_EXPORT_DIRECTORY)(k32base +
k32nt->OptionalHeader.DataDirectory[
IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
k32exparr = (PDWORD)(k32base + k32exp->AddressOfFunctions);
pLoadLibrary16 = (PLoadLibrary16)(k32base + k32exparr[35 - k32exp->Base]);
pFreeLibrary16 = (PFreeLibrary16)(k32base + k32exparr[36 - k32exp->Base]);
pGetProcAddress16 = (PGetProcAddress16)(k32base + k32exparr[37 - k32exp->Base]);
pQT_Thunk = (FARPROC)GetProcAddress(hk32, "QT_Thunk");
huser = pLoadLibrary16("USER");
pFreeLibrary16(huser);
addrGetFreeSystemResources = pGetProcAddress16(huser, "GETFREESYSTEMRESOURCES");
}
使用QT_Thunk调用16位函数:
cpp
// use global / static variables to avoid accessing ebp
static WORD pcSys, pcGdi, pcUser;
pcSys = pcGdi = pcUser = 0;
if (pQT_Thunk) {
_asm push ebp // constructs a special ebp frame
_asm mov ebp, esp
_asm sub esp, 64 // QT_Thunk requires a 64-byte scratch buffer below ebp
_asm push word ptr 0 // GFSR_SYSTEMRESOURCES
_asm mov edx, addrGetFreeSystemResources
_asm call pQT_Thunk
_asm mov pcSys, ax
_asm push word ptr 1 // GFSR_GDIRESOURCES
_asm mov edx, addrGetFreeSystemResources
_asm call pQT_Thunk
_asm mov pcGdi, ax
_asm push word ptr 2 // GFSR_USERRESOURCES
_asm mov edx, addrGetFreeSystemResources
_asm call pQT_Thunk
_asm mov pcUser, ax
_asm leave // single instruction for 'mov esp, ebp' and 'pop ebp'
}
static char buf[1024];
wsprintf(buf, "System: %u%%\r\nGDI: %u%%\r\nUSER: %u%%", pcSys, pcGdi, pcUser);
MessageBox(NULL, buf, "Free System Resources", MB_ICONINFORMATION);