Win9x下用Win32程序调用Win16函数 Win9x下有些系统函数只在Win16中存在如GetFreeSystemResources有些操作也只有在Win16环境中可以进行如调用int 2fh等软中断服务这时就需要调用Win16的DLL实现Win9x下Win32程序可以使用Flat Thunk调用Win16的DLL但官方的方法是使用Thunk Compiler非常麻烦使用Thunk Compiler的步骤详见Q154093注意早期MSDN将16位thunk dll的DllEntryPoint错写成DllMain1、需要编写一个32to16 thunk script如果需要回调还需要编写一个16to32 thunk script2、使用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及各自的.def5、编译链接16位和32位的thunk dll链接对应版本的.obj32位的thunk dll还要链接thunk32.lib6、在Win32程序中使用32位版本的thunk dll在Win16程序中也可以使用16位版本的thunk dll实际上如果只是为了调用少量Win16函数没必要这么麻烦可以使用以下未文档化函数实现1、DWORD WINAPI LoadLibrary16(LPSTR) kernel32.dll35按序号导出获取方法见下文2、void WINAPI FreeLibrary16(DWORD) kernel32.dll36按序号导出获取方法见下文3、DWORD WINAPI GetProcAddress16(DWORD, LPSTR) kernel32.dll37按序号导出获取方法见下文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.libWOWGetVDMPointerWOWGetVDMPointerFixWOWGetVDMPointerUnfixWOWGlobalAlloc16WOWGlobalAllocLock16WOWGlobalFree16WOWGlobalLock16WOWGlobalLockSize16WOWGlobalUnlock16WOWGlobalUnlockFree16WOWHandle16WOWHandle32另一个难点是Win9x的GetProcAddress拒绝解析kernel32.dll的导出序号有两种方案1、手动解析kernel32.dll的PE文件头2、使用下面的.def使用lib /def:xxx.def /out:xxx.lib生成一个导入库LIBRARY KERNEL32 EXPORTS LoadLibrary164 35 FreeLibrary164 36 GetProcAddress168 37QT_Thunk和MapLS、UmMapLS、MapSL等函数可以1、直接链接thunk32.lib2、在上面的导入库.def中声明3、使用GetProcAddress获取地址参考https://jacobfilipp.com/DrDobbs/articles/DDJ/1996/9614/9614c/9614c.htmhttps://rgmroman.narod.ru/qtthunk.htmhttps://rgmroman.narod.ru/flthunk.htm获取未公开函数和16位函数指针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位函数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 // this is a global / static variable _asm call pQT_Thunk _asm mov ecx, ebp _asm mov ebp, [ebp] // use parent ebp to access local variables _asm mov pcSys, ax _asm mov ebp, ecx _asm push word ptr 1 // GFSR_GDIRESOURCES _asm mov edx, addrGetFreeSystemResources // this is a global / static variable _asm call pQT_Thunk _asm mov ecx, ebp _asm mov ebp, [ebp] // use parent ebp to access local variables _asm mov pcGdi, ax _asm mov ebp, ecx _asm push word ptr 2 // GFSR_USERRESOURCES _asm mov edx, addrGetFreeSystemResources // this is a global / static variable _asm call pQT_Thunk _asm mov ecx, ebp _asm mov ebp, [ebp] // use parent ebp to access local variables _asm mov pcUser, ax _asm mov ebp, ecx _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);