
众所周知Win32环境中无法调用DOS功能调用int 21h之类的软中断服务但是Win9x提供了增强的DOS功能调用int 21h并且某些功能如磁盘管理功能在Win9x必须通过int 21h调用虽然Win9x提供了可以调用软中断服务的\\.\vwin32的DeviceIoControl接口但功能十分受限这个接口只能调用int 13h/25h/26h以及ax4400h-4411h和ax730xh两个区间的int 21h试图调用其他区间的int 21h会直接失败那么Win9x下Win32程序能不能无限制地调用int 21h呢实际上是可以的参考Windows 95 System Programming SECRETS只需要使用kernel32.dll序号1-9的VxDCall函数调用Win32 VxD服务VWIN32_Int21Dispatch(0x002a0010)即可序号1-9的内部名称为VxDCall0-8或VxDCall0-32实际地址没有区别参数个数根据要调用的Win32 VxD服务而定第一个参数是要调用的Win32 VxD服务VWIN32_Int21Dispatch(0x002a0010)有2个额外参数分别是eax和ecx这些参数使用stdcall调用约定倒序压入其他int 21h参数放在寄存器中即可还有两个不常用的调用软中断服务的Win32 VxD服务它们都有两个额外参数eax和ecxVWIN32_Int31Dispatch(0x002a0029)DPMI服务功能调用VWIN32_Int41Dispatch(0x002a002a)WDEB386调试器通知功能调用简单来说只需获取VxDCall函数地址FARPROC pVxDCall;然后使用下面的内联汇编序列替换_asm int 21h即可其他参数使用寄存器传递_asm push ecx _asm push eax _asm push 0x002a0010 // 或002a0010h _asm call pVxDCall和DOS/Win16下的主要区别是1、缓冲区地址使用32位通用寄存器传递而不是使用16位段寄存器和16位通用寄存器传递2、文件句柄是32位Win32文件句柄而不是16位整数文件描述符0/1/2/3/4也不对应stdin/stdout/stderr/stdaux/stdprn句柄3、文件名是ANSI字符集除非使用SetFileApisToOEM();设置为OEM字符集出现2、3这些区别的主要原因是——Win9x会给Win32环境下调用的int 21h进行标记内部处理会走不同的路径以实现Win32相对DOS/Win16增强的特性参考Inside the Windows 95 File System主要的难点是获取VxDCall函数地址非常麻烦因为Win9x的GetProcAddress拒绝解析kernel32.dll的ordinal number可以使用下面的方法解析kernel32.dll的PE文件头以获取VxDCall函数地址static FARPROC pVxDCall; if ((GetVersion() 0x80000000) LOWORD(GetVersion()) 4) { // Win9x GetProcAddress refuses to resolve kernel32.dll ordinals PBYTE k32base; PIMAGE_DOS_HEADER k32dos; PIMAGE_NT_HEADERS k32nt; PIMAGE_EXPORT_DIRECTORY k32exp; PDWORD k32exparr; k32base (PBYTE)GetModuleHandle(KERNEL32.DLL); 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); pVxDCall (FARPROC)(k32base k32exparr[1 - k32exp-Base]); }创建名为mydir的目录BYTE biserr; char szPath[MAX_PATH]; lstrcpy(szPath, mydir); if (pVxDCall) { _asm mov eax, 7139h // long file name make directory _asm lea edx, szPath // dirname _asm push ecx _asm push eax _asm push 002a0010h // VWIN32_Int21Dispatch _asm call pVxDCall _asm setc biserr // eflags.cf is error }冲刷驱动器缓冲区和高速缓存BYTE biserr; if (pVxDCall) { _asm mov eax, 0x710d // reset drive _asm mov ecx, 1 // 0 flush buffer, 1 flush buffer invalidate cache, 2 remount drvspace _asm mov edx, 3 // 0 default drive, 1,2,3,... A,B,C,... _asm push ecx _asm push eax _asm push 0x002a0010 // VWIN32_Int21Dispatch _asm call pVxDCall _asm setc biserr // eflags.cf is error }