* Articles
* Source Code
* Technetcast
* Book Reviews
* Op-Eds
* Microprocessors
* Programmer's Vault
* Software Careers
* Mailing Lists
* Dr. Dobb's Store
* Customer Service

We're the dot in .com
American Cybernetics
DDJ Home Page About Dr. Dobb's Subscribe to DDJ About DDJ

Examining the Windows AARD Detection Code

A serious message--and the code that produced it

Andrew Schulman

Andrew is a contributing editor to DDJ, and coauthor of the books Undocumented DOS and Undocumented Windows. Portions of this article are excerpted from Undocumented DOS, Second Edition, (Addison-Wesley, 1993).

If you were one of the thousands of Windows 3.1 beta testers, and if you happened to be using DR DOS rather than MS-DOS, you probably butted heads with a seemingly innocuous, yet odd, error message like that in Figure 1. As you'll see, this message is a visible manifestation of a chunk of code whose implementation is technically slippery and evasive.

While it's impossible to gauge intent, the apparent purpose of this code is to lay down arbitrary technical obstacles for DOS-workalike programs. The message appears with the release labeled "final beta release (build 61)" (dated December 20, 1991), and with "pre-release build 3.10.068" (January 21, 1992). Similar messages (with different error numbers) are produced in builds 61 and 68 by MN.COM, SETUP.EXE, and by the versions of HIMEM.SYS, SMARTDRV.EXE, and MSD.EXE (Microsoft diagnostics) packaged with Windows.

Although the error is non-fatal--that is, the program can continue running--WIN.COM's default behavior is to terminate the program, rather than continue.

The message first appeared in build 61, a late-stage beta, and seemed to disappear in the final retail release of Windows 3.1. However, the code that generates the message is present in the retail release, albeit in quiescent form, and executes every time you run Windows 3.1.

It's significant that the message, which appeared when running on DR DOS (including Novell's "Novell DOS 7" beta), did not appear when running on MS-DOS or PC-DOS. This raises the question then, what causes the error message? As it turns out, finding the answer required substantial system-level sleuthing, an interesting challenge in its own right.

In this article, I'll summarize the results of chasing this chunk of source which I call the "AARD code" (after a plain-text signature that's buried within the otherwise-encrypted code). The full technical details of the chase--the run-time disassembly and decryption of the code--is the subject of a subsequent DDJ article. The raw information is available now in electronic form; see "Availability," page 3. Here, I'll present a pseudocode summary of the AARD code, then focus on the code's effects and implications rather than on precise details of its implementation.

Maybe It's a Bug?

Whether in spite or because of the books Undocumented DOS and Undocumented Windows, I've often had to publicly defend Microsoft against what I felt were acts of scapegoating from whining competitors (including Novell, Borland, Lotus, and Wordperfect), complaints which remind me of the way some Americans like to blame Japan for what are ultimately our own domestic problems.

In fact, much of Microsoft's practice, far from targeting competitor's applications, points in the opposite direction: Microsoft sometimes goes to extremes to maintain compatibility, even with competitor's mistakes (see, for example, the crazy GetAppCompatFlags() function discussed in Chapter 5 of Undocumented Windows).

Certainly, it's true that DOS workalikes such as DR DOS have to pretend to be an older version of DOS (DOS 3.31, for instance) if they want to run Windows Enhanced mode. This is because of an undocumented interface shared by the Windows Enhanced mode DOSMGR virtual device driver (VxD) inside WIN386.EXE and MS-DOS 5 and 6. To appear as more recent versions of DOS, would-be clones must reverse-engineer and implement this undocumented protocol.

So whenever I've heard accusations that Microsoft practices so-called "cruel coding" to keep Windows from running on DR DOS, I look at the facts: Windows 3.1 Enhanced mode does run on DR DOS. Standard mode does not run, but that's because of a DR DOS bug acknowledged by Novell (see Undocumented DOS, Second Edition).

Consequently, if you didn't know how the error message in Figure 1 was generated, it's reasonable to think that it's the manifestation of yet another bug in Novell DOS. (It wouldn't be the first time company N's bug has been misinterpreted as company M's "deliberate incompatibility.")

Defeating a Debugger

The first step in discovering why the error message appeared under DR DOS but not MS-DOS was to examine the relevant WIN.COM code. However, the WIN.COM code that produced this message turned out to be XOR encrypted, self-modifying, and deliberately obfuscated--all in an apparent attempt to thwart disassembly.

The code also tries to defeat attempts for a debugger to step through it. For example, Figure 2 shows a code fragment in which the INT 1 single-step interrupt is pointed at invalid code (the two bytes FFh FFh), which disables DEBUG. The same is done with INT 2 (nonmaskable interrupt) and INT 3 (debug breakpoint). However, since modern debuggers (I used Nu-Mega's Soft-ICE) run the debugger and debuggee in separate address spaces, the AARD code's revectoring of INTs 1-3 has no affect on the Soft-ICE debugger. In any case, these attempts to throw examination off-track are in themselves revealing.

For whatever reasons, while much of it is XOR encrypted, the code contains, as plain-text, a Microsoft copyright notice and the initials "AARD" and "RSAA," perhaps the programmer's initials.

A Gauntlet of Tests

Figure 3 shows a pseudocode summary of the disassembled code. In essence, this code (which, remember, is part of Windows, a product sold separately from MS-DOS) checks for genuine MS-DOS or PC-DOS. As seen in Figure 3, the AARD code relies heavily on undocumented DOS functions and data structures. The undocumented INT 21h Function 52h is called to get a pointer to the DOS internal SysVars structure, popularly known as the "List of Lists." SysVars contains pointers to other DOS internal data structures, such as the current directory structure (CDS) and system file table (SFT). The AARD code checks a number of these pointers in SysVars, ensuring that none are null.

Any moderately self-respecting DOS workalike should pass unscathed through this gauntlet of tests. Interestingly, however, when this code is incorporated in a device driver such as HIMEM.SYS, it fails under DR DOS 5 and 6. These versions of DR DOS do not contain a genuine CDS, and the simulated CDS is apparently not set up until after device-driver initialization time. Thus, the Windows 3.1 beta HIMEM.SYS produces a non-fatal error message under DR DOS 5 and 6. Similarly, the AARD code fails under the Windows NT beta, where the DPB pointer in SysVars is null. Finally, the code fails in an OS/2 DOS box, where the DOS version number is 10.0 or greater (for example, OS/2 2.1 masquerades as DOS 20.10).

The crucial and, appropriately, most obfuscated test, however, appears at the end of the AARD test gauntlet. This test, which was unraveled by Geoff Chappell ( first checks to see whether a network redirector (such as MSCDEX) is running. If a redirector is running, the AARD code checks that DOS's default upper case-map is located in the DOS data segment. If a redirector is not running, the code checks that the pointer to the first simulated-file control block (FCB-SFT) is located on a paragraph boundary; that is, it has a 0 offset. For ease of reference, this code is repeated in Figure 4.

All versions of MS-DOS pass this test; no version of DR DOS does.

To test whether this interpretation of the encrypted and heavily-obfuscated code is correct, I wrote MSDETECT.C (Listing One, page 89). This program (compiled with Microsoft C) performs the same tests as the original AARD code, but without the obfuscations, and with more informative "error" messages. My MSDETECT program succeeds under all versions of MS-DOS I tested (Compaq DOS 3.31, MS-DOS 5.0, MS-DOS 6.0), yet fails under all versions of DR DOS tested (DR DOS 5.0, DR DOS 6.0, beta Novell DOS 7). If running under DR DOS with a redirector, MSDETECT fails with the message "Default case map isn't in DOS data segment!". Otherwise it fails under DR DOS with the message "First FCB-SFT not located on paragraph boundary!".

A Gratuitous Gatekeeper

But what does "country information" like the DOS default upper case-map have to do with a network redirector? Why does a piece of Windows care whether this mapper is located in the DOS data segment? And why should it care whether the first FCB-SFT is located on a paragraph boundary? What kind of "errors" are these, anyway?

These are all reasonable questions. In fact, the address of the default upper case-map has nothing to do with the network redirector, and no other part of Windows cares about what particular form is taken by DOS's default case-map or first FCB-SFT pointers. The AARD code has no relation to the actual purpose of the five otherwise-unrelated programs into which it has been dropped. It appears to be a wholly arbitrary test, a gratuitous gatekeeper seemingly with no purpose other than to smoke out non-Microsoft versions of DOS, tagging them with an appropriately vague "error" message.

Suitably, the section of the AARD code that performs this crucial test (highlighted in Figure 4) is the most heavily XOR encrypted and obfuscated. The test in Figure 4 is the critical piece of information used by Windows to determine if it is running on MS-DOS, or on a DOS "emulator." But this code seems to have no technically-valid purpose, checking instead some rather unimportant aspects of DOS. In short, you can have an otherwise perfectly workable DOS, capable of running Windows, and yet not pass this test.

To see if the case-map and FCB-SFT tests serve a technically useful purpose, I used Microsoft's SYMDEB debugger to slightly alter ("denormalize") DOS's pointers to the default case-map and the FCB-SFT. As you may recall, it's possible to change a real-mode segment:offset pointer without necessarily changing what location it points to. In real mode, a single memory location can be addressed by different pointers; there are many combinations of different segment and offset values that all resolve to the same physical address and are therefore equivalent. Windows (and all other software I ran) was unaffected by my change to these pointers. As Figure 5 shows, the only software that noticed was my MSDETECT and the AARD code in WIN.COM.

In other contexts (such as MSD's need to identify the operating system), it would be perfectly legitimate to walk internal DOS data structures to see that they were the same as would be expected under genuine MS-DOS. However, that WIN.COM and other programs incorporating AARD code don't make any use of the information gained in this way, other than to print the non-fatal error message, suggests a deliberate incompatibility, rather than a legitimate need to know some information about the underlying DOS.

The very non-fatality of the "error" further underscores the fact that it isn't Windows's legitimate business to care whether it's running on genuine MS-DOS. If the program can continue running despite the detected "error," then how much of an error was it to begin with? It seems that the only "error" is that the user is running Windows on someone else's version of DOS.

Does Beta Code Really Matter?

The non-fatal error message appeared only in two widely-distributed beta builds of Windows. But since the retail version of Windows 3.1 doesn't produce it, this is just dead history, right?

Not quite. Anyone with a copy of Windows 3.1 can hex dump WIN.COM (or WIN.CNF, from which WIN.COM is built during Windows setup) and see the error message (including the mention of "beta support") and the AARD and RSAA signatures. Using DEBUG, you can try your hand at unassembling the AARD code at offset 3CE2h in WIN.COM. In other words, the crazy-looking AARD code paraphrased in Figure 3 executes everytime you run Windows. The AARD code remains in Windows SETUP and in the Windows version of SMARTDRV.EXE (it appears to have been removed from HIMEM and MSD).

It's perfectly natural for software to contain vestigial remnants of past implementations. For example, WIN.COM also refers to the short-lived MSDPMI utility from the Microsoft C 7.0 beta. But in the case of the AARD code, new instructions were added to the AARD portion of Windows 3.1 retail WIN.COM--instructions that weren't present in the beta.

In the retail version of WIN.COM, the AARD code contains additional instructions as well as a control byte. The control byte determines whether or not the error message appears; this byte is currently 0. As shown in Figure 6, when running the retail WIN.COM under DR DOS, you can easily use DEBUG to turn on the control byte, and the message is issued just as under the beta versions. Changing the single byte at offset 16D4h in WIN.COM triggers the printing of the message, running on DR DOS or an MS-DOS in which the FCB and/or case-map pointers have been suitably denormalized.

This opens the door for Microsoft to reenable this byte in the retail shipping version in the future, if it chooses. There's no indication that Microsoft plans to do so, but it remains that neither the code nor warning message were removed--and, in fact, code was added. (I wonder to what extent you can dismiss something if it's only present in a beta and not in the retail version. The sheer size of Microsoft's beta test programs are significant product releases in themselves. I'm not sure of the number of Windows 3.1 beta sites, but a Microsoft article on the earlier DOS 5.0 beta claimed over 7000 beta sites. The Windows NT beta program reportedly shipped 70,000 units to influential developers and corporate beta testers. The size of the Windows 3.1 beta program was likely somewhere in between.)

So What?

A non-fatal error message in a beta version--that's it? If you have an axe to grind with Microsoft, you may have expected some more nakedly robber-baronesque behavior. If this is the worst that can be found, perhaps things aren't so bad after all. However, other examples of similar behavior have surfaced, including a warranty-related error message in QuickC and Microsoft C 6.0 (discussed in Chapter 4 of Undocumented DOS, Second Edition).

While it's difficult to second-guess the precise goal of the encrypted and obfuscated AARD code, its results are clear enough. Windows beta sites that used DR DOS rather than MS-DOS might have been scared into not using DR DOS. ("Doctor, every time I do this I get a non-fatal warning." "Then stop doing it.")

The effect of the AARD code is to create a new and highly artificial test of DOS compatibility. The obfuscations and encryptions make it difficult to even determine what is being tested. An indication that the AARD code's obfuscation is successful is the fact that Novell's most recent version of DR DOS (that is, Novell DOS 7) fails the test, even though it is otherwise far more compatible with MS-DOS than previous versions.

Microsoft's Initial Response

I've presented the substance of these findings to Microsoft, at both engineering and management levels. At press time, a detailed response was not forthcoming, perhaps due to the ongoing FTC investigation. It's likely that a subsequent issue of DDJ will contain a more specific response. However, a high-level manager at Microsoft repeatedly told me that the company is "agnostic" regarding DR DOS. He added, "They [Novell] claim 100 percent compatibility, but DR DOS is full of bugs. If DR DOS has problems running Windows, Novell should fix them."

The implication is that if a Windows/DR DOS user gets an error message that a Windows/MS-DOS user doesn't, then by definition it is Novell's fault and proof that DR DOS isn't "100 percent DOS compatible." The problem with this is that, as Figure 5 shows, the AARD code's test for DOS compatibility is 100 percent artificial. By Microsoft's definition, only MS-DOS or something byte-for-byte identical with MS-DOS (and therefore in violation of copyright) is "100 percent DOS compatible."

As for "agnostic," this seems unlikely given the effort required to write this tricky code. Its presence in five otherwise-unrelated programs also suggests a fairly concerted effort, as it is unlikely that five so different programs are all maintained by the same person. In fact, the programs probably fall under the domain of several different product managers or divisions.

Undocumented Interfaces and the Industry

The AARD code once again raises the issue of undocumented interfaces in the software industry. Because it is relatively easy for competitors to be compatible with a documented interface, companies try to create artificial kingdoms by selectively documenting only parts of their product interfaces. You have to wonder if that's the case here. Whenever an application calls on undocumented DOS services and uses data structures internal to DOS, as many successful applications now do, it ties itself more closely to the MS-DOS binary--to a particular sequence of bytes--rather than to the more-general DOS standard.

Furthermore, Microsoft apparently encourages this reliance on undocumented interfaces through "selectively documented" interfaces whereby Microsoft selectively allows some of its competitors and/or customers access to an interface, while denying similar access to other companies and to the rest of the developer community. There are numerous instances of this, including the XMS 3.0 specification, the Global EMM Import specification, and the LoadHi code for Windows 3.1.

Much of the discussion revolving around the need for a "Chinese Wall" at Microsoft has focused on the apparent absence of any genuine wall between the applications and operating-systems groups. The AARD code suggests that, if there's a need for an application/operating-systems wall, there may be the need for one between MS-DOS and Windows too. No one would dispute that DOS and Windows have to work together smoothly, but that togetherness needs to be open.

"Chinese Walls" are good engineering practice. They're what software engineering calls "firewalls": narrow and well-documented interfaces. But the standard software-engineering texts fail to mention that to properly document interfaces is also to throw them open to potential competition, and that, conversely, undocumented interfaces are a way of creating and reinforcing an enviable monopoly position. Hopefully, Microsoft's AARD code, which is not only an undocumented interface but--something new--an encrypted one, is not intended to stifle competition unfairly.

Systems Rivalry and the Courts

While I'm (mercifully) not an attorney, I have found that some of the legal literature surrounding the issue of "deliberate incompatibilities" to be fascinating reading. PC-centric readers may well be surprised that many of the issues surrounding Microsoft's travails with the U.S. Federal Trade Commission have been dealt with before in cases involving companies such as Eastman Kodak and IBM. "Deliberate incompatibilities" forms a fairly well-established part of antitrust law, going under monikers such as "non-price predation" and "predatory innovation."

One issue in the FTC's investigation of Microsoft was the relation between two of Microsoft's operating-systems products, Windows and MS-DOS. In particular, the FTC's Bureau of Competition tried to determine whether Microsoft had "done something" to Windows to deliberately keep it from running with Novell's DR DOS, which competes with MS-DOS.

Despite the relative insignificance of DR DOS in the market, this is an important question. MS-DOS and Windows are sold as separate products. While Microsoft wants to make MS-DOS a better platform for Windows, creating an artificial tie between Windows and MS-DOS for the sole purpose of hurting Novell would constitute unfair competition.

The two crucial words here are "sole" and "artificial." Surely, Microsoft should be allowed to improve Windows, even in ways that might ultimately hurt DR DOS. This is a legitimate part of the competitive process, and whining about "predatory innovation" has rightly been rejected by the courts. For example, many manufacturers of so-called "plug compatibles" tried in the '70s to have the courts characterize IBM's System/360 as "predatory innovation." The courts rejected these claims (along eventually with all of U.S. v. IBM, 1969-82), thereby "effectively requiring plaintiffs to prove that the defendant's design had no redeeming virtue for consumers" (Stephen F. Ross, Principles of Antitrust Law, Foundation Press, 1993). A detailed analysis from IBM's perspective, Folded, Spindled, and Mutilated: Economic Analysis and U.S. vs. IBM by Franklin Fisher et al. (MIT Press, 1983) makes sobering reading for anyone who might think there is some kind of open-and-shut case against Microsoft. Good luck, Novell.

Cases involving Eastman Kodak also have many parallels to Microsoft. In general the courts have sided with Kodak against competitors, although a recent Supreme Court case (Kodak v. Image Technical) went the other way. For a look at how Kodak has possibly created deliberate incompatibilities, see "Structural Monopoly, Technological Performance, and Predatory Innovation: Relevant Standards under Section 2 of the Sherman Act" by James W. Brock (American Business Law Journal, Fall 1983). Likewise, Antitrust Economics on Trial: A Dialogue on the New Laissez-Faire by Brock and Walter Adams (Princeton University Press, 1991) contains a useful section on "the Predation 'Problem'."

An apparently important article for the FTC is "Anticompetitive Exclusion: Raising Rivals' Costs to Achieve Power over Price" by Thomas Krattenmaker and Steve Salop (Yale Law Journal, December 1986). Merely the phrase "raising rivals' costs" is a useful handle for anyone trying to ponder Microsoft's current role in the software industry.

A far more interesting article is "Predatory Systems Rivalry: A Reply" by Ordover, Sykes, and Willig (Columbia Law Review, June 1983), which describes "systems rivalry" as follows:

Suppose that company A manufactures a product system with two components, A1 and A2, each sold separately. Company A has monopoly power over A1, but company B competes in the market for the second component with its compatible offering, B2. Thus, consumers initially can use a product system comprised of either A1 and A2 or A1 and B2. Company A now introduces a new product system, A1' and A2', which serves roughly the same function for consumers as the old product system. Component B2, however, is incompatible with A1'. Furthermore, company A discontinues the sale of A1 or else reprices A1 substantially higher than before. As a consequence, consumers switch to the new product system and company B is driven from the market for component two.

When, if ever, should the antitrust laws sanction company A for driving B out of the market?

There's a clear comparison here to Windows (A1), MS-DOS (A2), and DR DOS (B2). The scenario discussed in the article, which has nothing to do with operating-system software, underscores the fact that there's really nothing new in the questions surrounding Microsoft.

Normally, A driving B out of the market is what competition is all about. That's goal of competition, and should be protected. So how can you tell when this ceases to be honest competition, and becomes predation? "Predatory Systems Rivalry" provides a good summary:

...the plaintiff must bear the burden of proof on this issue. To establish the illegitimacy of R&D expenses by a preponderance of the evidence, the plaintiff would most likely need a "smoking gun"--a document or oral admission that clearly reveals the innovator's culpable state of mind at the time of the R&D decision. Alternatively, the plaintiff could prevail if the innovation involves such trivial design changes that no reasonable man could believe that it had anything but an anticompetitive purpose.

Figure 1: Typical message generated by the AARD code, produced, in this case from SETUP.EXE.


Figure 2: The AARD code attempts to disable a debugger, by pointing INT 1 (single step) at invalid code (the two bytes FFh FFh). The same operation is performed with INT 2 (nonmaskable interrupt) and INT 3 (breakpoint). This disassembly is from the Windows 3.1 retail version of WIN.COM.

C:\DDJ\AARD>debug \win31\
-u 3d0a
;;; Note that setting DS to 0; going to fiddle with intr vect table
7055:3D0A 33C0           XOR    AX,AX
7055:3D0C 8ED8           MOV    DS,AX
;;; ...
7055:3D12 A10400         MOV    AX,[0004]     ; get INT 1 offset
7055:3D15 2EA3D034       MOV    CS:[34D0],AX  ; save away
7055:3D19 A10600         MOV    AX,[0006]     ; get INT 1 segment
7055:3D1C 2EA3D234       MOV    CS:[34D2],AX  ; save away
7055:3D20 BBAC3F         MOV    BX,3FAC       ; set new intr handler offset
7055:3D23 891E0400       MOV    [0004],BX
7055:3D27 8C0E0600       MOV    [0006],CS     ; set new intr handler segment
-u 3fac
6B30:3FAC FFFF          ??? DI                ; the new intr handler
6B30:3FAE CF            IRET                  ;    is invalid code!

Figure 3: Pseudocode of AARD code, as found in WIN.COM

move (and fixup) code from 2D19h to 4E0h
call code at 4E0h
    call AARD code at 39B2h:
        -- see below
    IF (AX doesn't match 2000h)
        AND IF (control_byte is non-zero)   ;; added in retail
            THEN overwrite BYTE at 4E0h to a RET instruction
; ...
IF (byte at 4E0h is a RET instruction)
    THEN issue non-fatal error message
 call AARD code at 39B2h:
    point INT 1, 2, 3 and at invalid code to confuse debuggers
    call undocumented INT 21h AH=52h to get SysVars ("List of Lists")
    copy 30h bytes to SysVars to stack
    copy first 4 bytes (DPB ptr) of copy of SysVars to stack
    IF DOS version >= 10.0 (i.e., OS/2)
        THEN don't set [bp+196h], so eventually OR AX, 2000h fails
        check fields in SysVars to ensure non-zero:
            SysVars[0] -- Disk Parameter Block (DPB)
            SysVars[4] -- System File Table (SFT)
            SysVars[8] -- Clock device
            SysVars[12h] -- Buffers header
            SysVars[16h] -- Current Directory Structure (CDS)
            SysVars[0Ch] -- CON device
            SysVars[22h] -- Device driver chain (NUL device next ptr)
        IF no SysVars fields are zero (MS-DOS, or WIN.COM in DR DOS)
            THEN set [bp+196h] so that eventually OR AX, 2000h succeeds
        ELSE some are zero (e.g., HIMEM.SYS in DR DOS)
            THEN don't set [bp+196h], so eventually OR AX, 2000h fails
        copy code
        jump to copied code
        copy and XOR code
        jump to copied and XORed code
        ;; the following crucial part was figured out by Geoff Chappell:
        IF a redirector is running (INT 2Fh AX=1100h)
            AND IF default upper-case map (INT 21h AH=38h) in DOS
                   data segment (undocumented INT 2Fh AX=1203h)
        OR IF no redirector
            AND IF FCB-SFT header (SysVars[1Ah]) offset == 0
            THEN DOS is considered okay
        ELSE (e.g., WIN.COM, SMARTDRV.EXE, etc. in DR DOS)
            THEN clear part of [bp+196h] so eventually OR AX, 2000h fails
    restore previous INT 1, 2, 3
    jump back to saved return address

Figure 4: The crucial AARD test for DOS legitimacy.

IF redirector running (INT 2Fh AX=1100)
   AND IF default upper-case map (INT 21h AH=38h) in DOS
           data segment (INT 2Fh AX=1203h)
OR IF no redirector
   AND IF FCB-SFT header at paragraph boundary (offset == 0)
THEN DOS is considered okay

Figure 5: The AARD test can be made to fail simply by changing the

outward form of the pointers it examines.
Microsoft Symbolic Debug Utility
Windows Version 3.00
(C) Copyright Microsoft Corp 1984-1990
Processor is [80386]
;;; The first FCB-SFT is stored in this configuration at 0116:0040,
;;; so "denormalize" the pointer at that location, changing it from
;;; 05E4:0000 to 05E0:0040.  This points to the same exact location,
;;; but since the offset isn't zero the AARD test fails.
-dd 0116:0040 0040
0116:0040  05E4:0000
-ed 0116:0040 05E0:0040
;;; Now normalize the pointer for the default case map.  I had to
;;; disassemble the code for INT 21h AH=38h to find where this is
;;; located.  The pointer is stored here at 0116:12A8.  Below, the
;;; pointer is changed from 0116:0CF5 to 01E5:0005.  This points
;;; to the same exact location, but the segment isn't 0116 (DOS data
;;; segment) anymore, so the AARD test fails.
-dd 0116:12a8 12a8
0116:12A8  0116:0CF5
-ed 0116:12A8 01E5:0005
Non-Fatal error detected: error #2726
Please contact Windows 3.1 beta support
Press ENTER to exit or C to continue
Default case map isn't in DOS data segment!

Figure 6: Enabling a single byte in the Windows 3.1 retail version of WIN.COM resurrects the AARD code's non-fatal error message under DR DOS.

DEBUG v1.40     Program Debugger.
Copyright (c) 1985,1992 Digital Research Inc. All rights reserved
CPU type is [i486 in virtual 8086 mode]
-d 16d4 16d4
2271:16D0              00
-e 16d4 1
Non-Fatal error detected: error #2726
Please contact Windows 3.1 beta support
Press ENTER to exit or C to continue
Program terminated.

by Andrew Schulman


/* MSDETECT.C -- Build program with Microsoft C:  cl msdetect.c. A replication
of Microsoft's MS-DOS detection code from Windows 3.1 WIN.COM, SMARTDRV.EXE,
HIMEM.SYS, SETUP.EXE. The original Microsoft code (with the initials "AARD") is
heavily XOR encrypted and obfuscated. Here the encryptions and obfuscations
have been removed. Andrew Schulman, May 1993, 617-868-9699, Geoff Chappell (
deciphered the original code's tests (upper case map segment, FCB-SFT) in the
case where the preliminary SysVars tests fail. Some of this material is
discussed in Geoff's forthcoming book, "DOS Internals" (Addison-Wesley, 1993).
The page numbers below are for the first edition of "Undocumented DOS"
(Addison-Wesley, 1990). The second edition will be out in September 1993. */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dos.h>

typedef int BOOL;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef void far *FP;

BYTE far *_dos_getsysvars(void);
FP _dos_getcasemap(void);
WORD _dos_getdataseg(void);
BOOL _dos_isredirector(void);

void fail(const char *s) { puts(s); exit(1); }
    BYTE far *sysvars;
    if ((sysvars = _dos_getsysvars()) == 0)
        fail("INT 21h AX=5200h returns 0!");
    if (_osmajor >= 0x0a)
        fail("DOS version >= 10; this is OS/2 (or early NT beta!)");
    #define SYSVARS(ofs)            (*((FP far *) &sysvars[ofs]))
    #define SYSVARS_TEST(ofs, msg)  if (! SYSVARS(ofs)) fail(msg)

    // these tests will pass under almost any DOS clone
    SYSVARS_TEST(0, "Disk Parameter Block (DPB) pointer in SysVars is 0!");
    SYSVARS_TEST(4, "System File Table (SFT) pointer in SysVars is 0!");
    SYSVARS_TEST(8, "CLOCK$ device pointer in SysVars is 0!");
    SYSVARS_TEST(0x12, "buffers header pointer in SysVars is 0!");
    SYSVARS_TEST(0x16, "Curr Directory Struct (CDS) ptr in SysVars is 0!");
    SYSVARS_TEST(0x0C, "CON device pointer in SysVars is 0!");
    SYSVARS_TEST(0x22, "Device chain pointer (from NUL) in SysVars is 0!");

    // the following tests fail under DR DOS 5 and 6 (and beta of Novell DOS 7)
    if (_dos_isredirector())
        FP casemap = _dos_getcasemap();
        if (FP_SEG(casemap) != _dos_getdataseg())
            fail("Default case map isn't in DOS data segment!");
        printf("case map @ %Fp\n", casemap);
        if (FP_OFF(SYSVARS(0x1A)) != 0)  // see Undocumented DOS, p. 519
            fail("First FCB-SFT not located on paragraph boundary!");
        printf("FCB-SFT ptr @ %Fp -> %Fp\n", sysvars+0x1a, SYSVARS(0x1A));
    // if get here, everything checks out
    puts("All tests check out: must be MS-DOS");
    return 0;
// undocumented function: see "Undocumented DOS", pp. 518-541
BYTE far *_dos_getsysvars(void)
    // could initialize ES:BX to 0:0 but the MS code doesn't do this
    _asm mov ax, 5200h
    _asm int 21h
    _asm mov dx, es
    _asm mov ax, bx
    // ES:BX retval moved into DX:AX
// see "Microsoft MS-DOS Programmer's Reference", p. 143
// formerly undocumented: see "Undocumented DOS", p. 599
BOOL _dos_isredirector(void)
    BYTE retval;
    _asm mov ax, 1100h
    _asm int 2fh
    _asm mov retval, al
    return (retval == 0xFF);
// undocumented function: see "Undocumented DOS", p. 627
WORD _dos_getdataseg(void)
    _asm push ds
    _asm mov ax, 1203h
    _asm int 2fh
    _asm mov ax, ds
    _asm pop ds
    // retval in AX
// get a far pointer to the default case map
// see "Microsoft MS-DOS Programmer's Reference", pp. 272-3
FP _dos_getcasemap(void)
    BYTE country_info[34];
    FP fp = (FP) country_info;
    _asm push ds
    _asm mov ax, 3800h
    _asm lds dx, dword ptr fp
    _asm int 21h
    _asm pop ds
    return *((FP far *) &country_info[18]);

Copyright © 1999 Dr. Dobb's Journal