home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / MAGAZINE / DDJ9309.ZIP / 1993-SEP.ZIP / AARD.ASC next >
Text File  |  1993-07-26  |  9KB  |  273 lines

  1. _EXAMINING THE WINDOWS AARD DETECTION CODE_
  2. by Andrew Schulman
  3.  
  4.  
  5. [LISTING ONE]
  6.  
  7. /* MSDETECT.C -- Build program with Microsoft C:  cl msdetect.c. A replication
  8. of Microsoft's MS-DOS detection code from Windows 3.1 WIN.COM, SMARTDRV.EXE, 
  9. HIMEM.SYS, SETUP.EXE. The original Microsoft code (with the initials "AARD") is
  10. heavily XOR encrypted and obfuscated. Here the encryptions and obfuscations 
  11. have been removed. Andrew Schulman, May 1993, 617-868-9699, 
  12. 76320.302@compuserve.com. Geoff Chappell (geoffc@cix.compulink.co.uk) 
  13. deciphered the original code's tests (upper case map segment, FCB-SFT) in the 
  14. case where the preliminary SysVars tests fail. Some of this material is 
  15. discussed in Geoff's forthcoming book, "DOS Internals" (Addison-Wesley, 1993).
  16. The page numbers below are for the first edition of "Undocumented DOS" 
  17. (Addison-Wesley, 1990). The second edition will be out in September 1993. */
  18.  
  19. #include <stdlib.h>
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include <dos.h>
  23.  
  24. typedef int BOOL;
  25. typedef unsigned char BYTE;
  26. typedef unsigned short WORD;
  27. typedef unsigned long DWORD;
  28. typedef void far *FP;
  29.  
  30. BYTE far *_dos_getsysvars(void);
  31. FP _dos_getcasemap(void);
  32. WORD _dos_getdataseg(void);
  33. BOOL _dos_isredirector(void);
  34.  
  35. void fail(const char *s) { puts(s); exit(1); }
  36. main()
  37. {
  38.     BYTE far *sysvars;
  39.     if ((sysvars = _dos_getsysvars()) == 0)
  40.         fail("INT 21h AX=5200h returns 0!");
  41.     if (_osmajor >= 0x0a)
  42.         fail("DOS version >= 10; this is OS/2 (or early NT beta!)");
  43.     #define SYSVARS(ofs)            (*((FP far *) &sysvars[ofs]))
  44.     #define SYSVARS_TEST(ofs, msg)  if (! SYSVARS(ofs)) fail(msg)
  45.         
  46.     // these tests will pass under almost any DOS clone
  47.     SYSVARS_TEST(0, "Disk Parameter Block (DPB) pointer in SysVars is 0!");
  48.     SYSVARS_TEST(4, "System File Table (SFT) pointer in SysVars is 0!");
  49.     SYSVARS_TEST(8, "CLOCK$ device pointer in SysVars is 0!");
  50.     SYSVARS_TEST(0x12, "buffers header pointer in SysVars is 0!");
  51.     SYSVARS_TEST(0x16, "Curr Directory Struct (CDS) ptr in SysVars is 0!");
  52.     SYSVARS_TEST(0x0C, "CON device pointer in SysVars is 0!");
  53.     SYSVARS_TEST(0x22, "Device chain pointer (from NUL) in SysVars is 0!");
  54.     
  55.     // the following tests fail under DR DOS 5 and 6 (and beta of Novell DOS 7)
  56.     if (_dos_isredirector())
  57.     {
  58.         FP casemap = _dos_getcasemap();
  59.         if (FP_SEG(casemap) != _dos_getdataseg())
  60.             fail("Default case map isn't in DOS data segment!");
  61.         printf("case map @ %Fp\n", casemap);
  62.     }
  63.     else
  64.     {
  65.         if (FP_OFF(SYSVARS(0x1A)) != 0)  // see Undocumented DOS, p. 519
  66.             fail("First FCB-SFT not located on paragraph boundary!");
  67.         printf("FCB-SFT ptr @ %Fp -> %Fp\n", sysvars+0x1a, SYSVARS(0x1A));
  68.     }
  69.     // if get here, everything checks out
  70.     puts("All tests check out: must be MS-DOS");
  71.     return 0;
  72. }
  73. // undocumented function: see "Undocumented DOS", pp. 518-541
  74. BYTE far *_dos_getsysvars(void)
  75. {
  76.     // could initialize ES:BX to 0:0 but the MS code doesn't do this
  77.     _asm mov ax, 5200h
  78.     _asm int 21h
  79.     _asm mov dx, es
  80.     _asm mov ax, bx
  81.     // ES:BX retval moved into DX:AX
  82. }
  83. // see "Microsoft MS-DOS Programmer's Reference", p. 143
  84. // formerly undocumented: see "Undocumented DOS", p. 599
  85. BOOL _dos_isredirector(void)
  86. {
  87.     BYTE retval;
  88.     _asm mov ax, 1100h
  89.     _asm int 2fh
  90.     _asm mov retval, al
  91.     return (retval == 0xFF);
  92. }
  93. // undocumented function: see "Undocumented DOS", p. 627
  94. WORD _dos_getdataseg(void)
  95. {
  96.     _asm push ds
  97.     _asm mov ax, 1203h
  98.     _asm int 2fh
  99.     _asm mov ax, ds
  100.     _asm pop ds
  101.     // retval in AX
  102. }
  103. // get a far pointer to the default case map
  104. // see "Microsoft MS-DOS Programmer's Reference", pp. 272-3
  105. FP _dos_getcasemap(void)
  106. {
  107.     BYTE country_info[34];
  108.     FP fp = (FP) country_info;
  109.     _asm push ds
  110.     _asm mov ax, 3800h
  111.     _asm lds dx, dword ptr fp
  112.     _asm int 21h
  113.     _asm pop ds
  114.     return *((FP far *) &country_info[18]);
  115. }
  116.  
  117.  
  118.  
  119.  
  120. Figure 2: The AARD code attempts to disable a debugger, by pointing INT
  121. 1 (single step) at invalid code (the two bytes FFh FFh).  The same
  122. operation is performed with INT 2 (nonmaskable interrupt) and INT 3
  123. (breakpoint). This disassembly is from the Windows 3.1 retail
  124. version of WIN.COM.
  125.  
  126. C:\DDJ\AARD>debug \win31\win.com
  127. -u 3d0a
  128. ;;; Note that setting DS to 0; going to fiddle with intr vect table
  129. 7055:3D0A 33C0           XOR    AX,AX 
  130. 7055:3D0C 8ED8           MOV    DS,AX 
  131. ;;; ...
  132. 7055:3D12 A10400         MOV    AX,[0004]     ; get INT 1 offset
  133. 7055:3D15 2EA3D034       MOV    CS:[34D0],AX  ; save away
  134. 7055:3D19 A10600         MOV    AX,[0006]     ; get INT 1 segment
  135. 7055:3D1C 2EA3D234       MOV    CS:[34D2],AX  ; save away
  136. 7055:3D20 BBAC3F         MOV    BX,3FAC       ; set new intr handler offset
  137. 7055:3D23 891E0400       MOV    [0004],BX 
  138. 7055:3D27 8C0E0600       MOV    [0006],CS     ; set new intr handler segment
  139.  
  140. -u 3fac
  141. 6B30:3FAC FFFF          ??? DI                ; the new intr handler
  142. 6B30:3FAE CF            IRET                  ;    is invalid code!
  143.  
  144.  
  145.  
  146.  
  147. Figure 3: Pseudocode of AARD code, as found in WIN.COM
  148.  
  149. move (and fixup) code from 2D19h to 4E0h
  150. call code at 4E0h
  151.     call AARD code at 39B2h:
  152.         -- see below
  153.     IF (AX doesn't match 2000h)
  154.         AND IF (control_byte is non-zero)   ;; added in retail
  155.             THEN overwrite BYTE at 4E0h to a RET instruction
  156. ; ...
  157. IF (byte at 4E0h is a RET instruction)
  158.     THEN issue non-fatal error message
  159.  
  160.  call AARD code at 39B2h:
  161.     point INT 1, 2, 3 and at invalid code to confuse debuggers
  162.     call undocumented INT 21h AH=52h to get SysVars ("List of Lists")
  163.     copy 30h bytes to SysVars to stack
  164.     copy first 4 bytes (DPB ptr) of copy of SysVars to stack
  165.     IF DOS version >= 10.0 (i.e., OS/2)
  166.         THEN don't set [bp+196h], so eventually OR AX, 2000h fails
  167.     ELSE
  168.         check fields in SysVars to ensure non-zero:
  169.             SysVars[0] -- Disk Parameter Block (DPB)
  170.             SysVars[4] -- System File Table (SFT)
  171.             SysVars[8] -- Clock device
  172.             SysVars[12h] -- Buffers header
  173.             SysVars[16h] -- Current Directory Structure (CDS)
  174.             SysVars[0Ch] -- CON device
  175.             SysVars[22h] -- Device driver chain (NUL device next ptr)
  176.         IF no SysVars fields are zero (MS-DOS, or WIN.COM in DR DOS)
  177.             THEN set [bp+196h] so that eventually OR AX, 2000h succeeds
  178.         ELSE some are zero (e.g., HIMEM.SYS in DR DOS)
  179.             THEN don't set [bp+196h], so eventually OR AX, 2000h fails
  180.         copy code
  181.         jump to copied code
  182.         copy and XOR code
  183.         jump to copied and XORed code
  184.         ;; the following crucial part was figured out by Geoff Chappell:
  185.         IF a redirector is running (INT 2Fh AX=1100h)
  186.             AND IF default upper-case map (INT 21h AH=38h) in DOS
  187.                    data segment (undocumented INT 2Fh AX=1203h)
  188.         OR IF no redirector
  189.             AND IF FCB-SFT header (SysVars[1Ah]) offset == 0
  190.             THEN DOS is considered okay
  191.         ELSE (e.g., WIN.COM, SMARTDRV.EXE, etc. in DR DOS)
  192.             THEN clear part of [bp+196h] so eventually OR AX, 2000h fails
  193.     restore previous INT 1, 2, 3
  194.     jump back to saved return address
  195.  
  196.  
  197.  
  198.  
  199. Figure 4: The crucial AARD test for DOS legitimacy.
  200.  
  201. IF redirector running (INT 2Fh AX=1100)
  202.    AND IF default upper-case map (INT 21h AH=38h) in DOS
  203.            data segment (INT 2Fh AX=1203h)
  204. OR IF no redirector
  205.    AND IF FCB-SFT header at paragraph boundary (offset == 0)
  206. THEN DOS is considered okay
  207.  
  208.  
  209.  
  210.  
  211. Figure 5: The AARD test can be made to fail simply by changing the
  212. outward form of the pointers it examines.
  213.  
  214. C:\UNDOC2\CHAP1>symdeb
  215. Microsoft Symbolic Debug Utility
  216. Windows Version 3.00
  217. (C) Copyright Microsoft Corp 1984-1990
  218. Processor is [80386]
  219.  
  220. ;;; The first FCB-SFT is stored in this configuration at 0116:0040,
  221. ;;; so "denormalize" the pointer at that location, changing it from
  222. ;;; 05E4:0000 to 05E0:0040.  This points to the same exact location,
  223. ;;; but since the offset isn't zero the AARD test fails.
  224.  
  225. -dd 0116:0040 0040
  226. 0116:0040  05E4:0000
  227. -ed 0116:0040 05E0:0040
  228.  
  229. ;;; Now normalize the pointer for the default case map.  I had to
  230. ;;; disassemble the code for INT 21h AH=38h to find where this is
  231. ;;; located.  The pointer is stored here at 0116:12A8.  Below, the
  232. ;;; pointer is changed from 0116:0CF5 to 01E5:0005.  This points
  233. ;;; to the same exact location, but the segment isn't 0116 (DOS data
  234. ;;; segment) anymore, so the AARD test fails.
  235.  
  236. -dd 0116:12a8 12a8
  237. 0116:12A8  0116:0CF5
  238. -ed 0116:12A8 01E5:0005
  239. -q
  240.  
  241. C:\WINB61>win
  242. Non-Fatal error detected: error #2726
  243. Please contact Windows 3.1 beta support
  244. Press ENTER to exit or C to continue
  245.  
  246. C:\UNDOC2\CHAP1>msdetect
  247. Default case map isn't in DOS data segment!
  248.  
  249.  
  250.  
  251. Figure 6: Enabling a single byte in the Windows 3.1 retail version
  252. of WIN.COM resurrects the AARD code's non-fatal error message under
  253. DR DOS.
  254.  
  255. C:\DRDOS6>debug win.com
  256. DEBUG v1.40     Program Debugger.
  257. Copyright (c) 1985,1992 Digital Research Inc. All rights reserved
  258.  
  259. CPU type is [i486 in virtual 8086 mode]
  260. -d 16d4 16d4
  261. 2271:16D0              00
  262. -e 16d4 1
  263. -g
  264. Non-Fatal error detected: error #2726
  265. Please contact Windows 3.1 beta support
  266. Press ENTER to exit or C to continue
  267. Program terminated.
  268. -q
  269.  
  270.  
  271.  
  272.  
  273.