The famous MSCDEX, created in the days of MS-DOS glory , provided the required functional capabilities to programmers, despite its multiple drawbacks. It ensured with a sufficient level of comprehensiveness all of the capabilities of the drives that existed at that time. For example, the reading of individual sectors was carried out by the 1508h function of the INT 2Fh interrupt. If it was necessary to go to the raw level, it was always possible to ask MSCDEX to pass the ATAPI packet directly. This task was accomplished by the 1510h function of the same interrupt (see the Interrupt List by Ralf Brown, if you need more detailed information).
Curiously enough, the functional capabilities of the newer OS, Windows 9 x , are incomparably weaker. For instance, under this more powerful OS, it is rather problematic to go down to the sector level without encountering a large number of problems. To all appearances , the system architects have decided that the sector level is something unneeded and, furthermore, system-dependent. Therefore, according to their point of view, proper applications must be developed as fully portable, and must use only standard Win32 API calls ”all other calls are illegal.
Meanwhile, to support backward compatibility with programs written for MS-DOS and Windows 3.1, the Windows 95 operating system supports MSCDEX interface. As a result of performance considerations, this interface isn t implemented in native MSCDEX, which could be missing on the disk. Rather, these functions are implemented in a CD-ROM driver that executes in 32-bit protected mode. This means that all of the required functionality is present in the system. Consequently, there remains a hope to get hands over it in some way or another. Naturally, this problem can be easily solved at the kernel level, but writing a custom driver just to provide an interface to the existing one is very inefficient.
Fortunately, there is a documented and ready-to-use interface between Win32 applications and the MSCDEX driver in Windows 9 x . Unfortunately, it is awkwardly implemented. In general, because the MSCDEX interface is callable only in V86-mode, Win32 applications must thunk to a 16-bit DLL, and the 16-bit DLL must use the DOS Protected Mode Interface (DPMI) Simulate Real Mode Interrupt function to call its functions.
Note | DPMI (DOS Protected Mode Interface) is the interface designed specially to enable developers of protected-mode MS-DOS applications to use the functions of 16-bit operating system such as MS-DOS itself. |
Particularly, you are interested in the 1508h function ” DPMI Simulate Real Mode Interrupt, which allows for the calling of real-mode interrupts from the protected mode. By calling the emulator of the MSCDEX driver via its native INT 2Fh interrupt, you can do whatever you like with the drive, since the MSCDEX interface, as was have mentioned before, is very powerful.
Thus, you can predict the following programming route: Win32 application ” 16-bit DLL ”DMPI Simulate RM Interrupt ”MSCDEX ”CDFS. Isn t it too bulky? Or isn t it better to use ASPI (because it is present in Windows 95) or undertake the development of a custom driver? Nevertheless, even if you are not planning to control the drive via MSCDEX, it is useful to know about the existence of such a method to communicate with hardware, especially if you plan to crack someone else s programs. In this case, setting breakpoints to API functions won t produce anything, because reading sectors is carried out via INT 31h (DMPI) and INT 2Fh interrupts. Unfortunately, setting breakpoints directly to these interrupts results in a large number of garbage debugger popups. Using filters is unlikely to be efficient because the number of possible variations is too large. It would be much better to search for interrupt calls in the disassembled program listing!
Additional information on this topic can be found in the Q137813 article included in the MSDN documentation supplied along with Microsoft Visual Studio. The title of this article is How Win32 Applications Can Read CD-ROM Sectors in Windows 95 . A complete listing of DMPI- and MSCDEX-functions can be found in the InterruptList composed by Ralf Brown. Thus, you shouldn t encounter any difficulties when using this technique. (Although, it is rather difficult nowadays to find a compiler capable of generating 16-bit code and linker for Windows 3.1! By the way, Microsoft Visual Studio 6.0 is not suitable for this purpose any more, since beginning with one of its earlier versions, it lacks the capability for creating projects for MS-DOS/Windows 3.1.)
Provided below is a key fragment quoted from MSDN. This example illustrates the technique for calling real-mode interrupts from 16-bit DLL executed in the Windows environment.
BOOL FAR PASCAL MSCDEX_ReadSector(BYTE bDrive, DWORD StartSector, LPBYTE RMlpBuffer) { RMCScallStruct; BOOLfResult; // Prepare DPMI Simulate Real Mode Interrupt call structure with // the register values used to make the MSCDEX Absolute read call. // Then, call MSCDEX using DPMI and check for errors in both the DPMI call // and the MSCDEX call. BuildRMCS (scallStruct); callStruct.eax = 0x1508; // MSCDEX "ABSOLUTE READ" function callStruct.ebx = LOWORD(RMlpBuffer); // Buffer offset for reading a sector callStruct.es = HIWORD(RMlpBuffer); // Buffer segment for reading a sector callStruct.ecx = bDrive; // Drive letter 0=A, 1=B, 2=C, etc. callStruct.edx = 1; // Read one sector callStruct.esi = HIWORD(StartSector); // Number of the sector to be read // (most significant word) callStruct.edi = LOWORD(StartSector); // Number of the sector to be read // (least significant word) // Calling the real-mode interrupt if (fResult = SimulateRM_Int (0x2F, &callStruct)) fResult = !(callStruct.wFlags & CARRY_FLAG); return fResult; } BOOL FAR PASCAL SimulateRM_Int(BYTE bIntNum, LPRMCS lpCallStruct) { BOOL fRetVal = FALSE; // Assume failure __asm { push di ; Saving the DI register mov ax, 0300h ; DPMI Simulate Real Mode Interrupt mov bl, bIntNum ; Number of the real-mode interrupt for the call mov bh, 01h ; Bit 0=1 ; all other bits must be set to zero xor cx, cx ; Do not copy anything from the PM stack to RM stack. les di, lpCallStruct ; Pointer to the structure with registers values int 31h ; Gateway to DMPI jc END1 ; If error, jump to END1. mov fRetVal, TRUE ; Everythings OK. END1: pop di ; Restoring the DI register } // Return return (fRetVal); }