home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1994 #1
/
monster.zip
/
monster
/
MAGAZINE
/
DDJ9309.ZIP
/
1993-SEP.ZIP
/
AARD.ASC
next >
Wrap
Text File
|
1993-07-26
|
9KB
|
273 lines
_EXAMINING THE WINDOWS AARD DETECTION CODE_
by Andrew Schulman
[LISTING ONE]
/* 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,
76320.302@compuserve.com. Geoff Chappell (geoffc@cix.compulink.co.uk)
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); }
main()
{
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);
}
else
{
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]);
}
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\win.com
-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
ELSE
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.
C:\UNDOC2\CHAP1>symdeb
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
-q
C:\WINB61>win
Non-Fatal error detected: error #2726
Please contact Windows 3.1 beta support
Press ENTER to exit or C to continue
C:\UNDOC2\CHAP1>msdetect
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.
C:\DRDOS6>debug win.com
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
-g
Non-Fatal error detected: error #2726
Please contact Windows 3.1 beta support
Press ENTER to exit or C to continue
Program terminated.
-q