Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Design

Undocumented Corner


JUN94: UNDOCUMENTED CORNER

UNDOCUMENTED CORNER

OS/2 for Windows: IBM's Patch-O-Rama

Art Rothstein, Roger Alley, and others

Art Rothstein and Roger Alley are software developers in the San Francisco area. They can be contacted on CompuServe at 70353,47 and 71163,2407, respectively.


Introduction

by Andrew Schulman

The commercial viability of IBM's OS/2 depends on its ability to run Windows applications. For some time, to run Win apps, every copy of OS/2 included a modified version of Windows, WIN-OS/2. IBM's agreement with Microsoft gave it source code for Windows 3.10; IBM modified the Windows 3.10 source to create WIN-OS/2.

Modifications are necessary because the normal retail version of Windows can't run as a well-behaved client under OS/2. While Microsoft recommends that Win apps follow certain rules, Windows itself observes almost no rules at all--it is what the industry (including Microsoft) calls an "ill-behaved application."

For example, while Windows provides DOS protected-mode interface (DPMI) services--it is a DPMI "host"--the Windows kernel does not itself consistently use DPMI services--it is not a DPMI "client". Instead, it directly manipulates protected-mode structures, such as the local descriptor table (LDT). There has been some speculation as to why Windows is not a DPMIclient: Is it only performance considerations? In any case, IBM had to modify the Windows source code, in part, to makeWIN-OS/2 a well-behaved DPMIclient.

By including a version of Windows with OS/2, IBM paid Microsoft royalties for every copy of OS/2 sold. This was not only expensive, but gave Microsoft the inside on OS/2 sales. Given the competition between Microsoft's Windows and IBM's OS/2, IBM was in an awkward position.

Since so many PCs already have Windows, in late 1993 IBM came out with "OS/2 for Windows" (OS2fW)--OS/2 without Windows--which uses the Windows already on your PC to run Win apps under OS/2. This is a neat solution: The user gets to run Win apps, and IBM doesn't have to pay Microsoft for including this ability in OS/2.

But, since Windows can't run as is under OS/2, OS2fW must modify the copy of Windows a user has installed on the machine. How? That's the subject of this month's "Undocumented Corner." The short answer: IBM patches Windows in memory. The in-memory patches have the same net effect as the source-code changes made in WIN-OS/2.

Patching someone else's code is often thought to be the ultimate hack. Quarterdeck patched Windows 3.0 Standard mode to force VCPI clienthood, Adobe patched Windows to make it use ATM, and, naturally, Windows itself massively patches MS-DOS. Now IBM is patching Windows.

Patching requires reverse engineering. Even with access to the source code, IBM had to reverse engineer something, to find the offsets where the patches go, and the number of bytes to patch. This is "deep" reverse engineering, to use Microsoft's terminology from the recent Stac vs. Microsoft case (see the May 1994 "Undocumented Corner").

IBM's patches are custom-tailored for Windows 3.10. Microsoft has recently come out with Windows 3.11. There is also a downloadable patch file for turning a 3.10 system into 3.11. Guess what: OS2fW doesn't work with Windows 3.11.

OS2fW will install without complaint over Windows 3.11, but when you attempt to load a 3.11 session, you get an OS/2 SYS3176 illegal-instruction exception ("A program in this session encountered a problem and cannot continue"), and the session fails. The failure is not graceful, and the error message does not refer to the Windows version number.

Upon the discovery of OS2fW/Windows 3.11 incompatibility, OS/2 enthusiasts immediately claimed that Microsoft deliberately broke OS2fW. Apparently, some initial test versions of 3.11 did not break OS2fW, only the final released version did. IBM's Dave Whittle stated on CompuServe's Canopus forum that Windows 3.11's "primary feature is that it renders Windows unable to run under OS/2 for Windows."

Microsoft says that OS2fW depends upon fixed locations in Windows 3.10, so that even the most trivial bug fixes in Windows would throw off OS2fW patches. In response, IBM representatives claimed that the OS2fW uses "smart" patching that isn't dependent on fixed locations of code. As you'll see here, the OS2fW patch engine unfortunately is not "smart" and does depend heavily (on the order of 100 separate patches) upon fixed locations in Windows 3.10. As noted by Dan Gillmor of the Detroit Free Press, "I haven't seen any evidence that IBM's patch contained that much intelligence--just IBM's assurances that it did."

All of this was discussed in the Undocumented Corner area in the DDJ Forum (GO DDJFORUM) on CompuServe. Because this online conversation yielded so much solid information, this month we have an epistolary "Undocumented Corner." We'll just listen in from when Larry Seltzer of PC Week first raised the question, to when Art Rothstein and Roger Alley figure out exactly how OS2fW patches Windows. This is a heavily edited transcript of the online conversation; the entire thread is available electronically (see "Availability," page 3).

How's It Work?

19-Feb-94 17:52:44
Fm: Larry Seltzer [PCWLabs]
There is a patch available for making a Win 3.1 into a Win 3.11. The filename is WW0981.EXE and it's a hair under 600,000 bytes.

I heard from someone whose brother knows a guy who installed it (not literally, it's just that I haven't seen it myself) that one of the files the patch modifies is KRNL386.EXE. Yet, Microsoft claims Windows 3.11 is just a "driver revision." Spencer Katt got a tip this week that the patch breaks OS2fW. Now mind you, OS2fW isn't Microsoft's problem, but the situation would be pretty awful for OS2fW if the rumor's true. After all, MS says all the OEMs have 3.11.

19-Feb-94 22:31:10
Fm: Andrew Schulman
Remember that, even if Windows 3.11 "breaks" OS2fW, it doesn't mean that there was any attempt to "thwart" OS2fW. Not every incompatibility is a deliberate incompatibility!

I'd feel a lot more sure about all of this if I first understood how OS2fW works. What aspects of Windows 3.1 does it rely on? Does it patch, or what? How does it differ from the old WIN-OS/2 modifcations to Windows source?

23-Feb-94 01:41:51
Fm: Larry Seltzer [PCWLabs]
I'm amazed, but it appears as if OS2fW makes no modifications to KRNL386.EXE.

23-Feb-94 22:47:57
Fm: Tim Farley
Larry, have you checked both statically and dynamically? It might not patch the file on disk, but instead, as it loads into memory. QEMM used to do this to support Standard mode in Win 3.0.

24-Feb-94 09:18:41
Fm: Andrew Schulman
The bit about QEMM patching Windows 3.0 Standard mode is important. Geoff Chappell describes it on pp. 562--565 of DOS Internals. Briefly: "QEMM modifies the DOSX code so that it complies with VCPI (one Quarterdeck spokesperson has apparently referred to this as 'we ram VCPI down Windows' throat')."

I would think this is the kind of thing OS2fW would have to do, though in their case it would be forcing KRNL386, etc. to be DPMI compatible. Windows of course is a DPMI host, but it's not a DPMI client. (Sort of the opposite of that guy in the "Hair Club for Men" commercials: "I'm not only the Hair Club president, I'm also a client.")

The issue of Windows not being DPMI compliant is discussed in Undocumented DOS, second edition, pp. 27 and 142: "the kernel bypasses DPMI. This means that IBM must hack Windows to create a slightly [??] different WIN-OS/2". So I wonder how OS2fW works.

25-Feb-94 15:40:15
Fm: Larry Seltzer [PCWLabs]
If IBM is patching Windows in memory, MS would have to freeze Win 3.1 forever not to break OS2fW.

Finally, Some Answers

10-Mar-94 05:35:20
Fm: Art Rothstein
I installed OS2fW on a machine tonight and looked around a little. Windows is started from WINOS2.COM, supplied by IBM. Some "shallow" disassembly <grin> suggested that WINOS2 loads KRNL 386.EXE via DOS function 4B01h [Load but don't execute] and patches it in memory.

There are eight small DLLs with .SCR [script] suffixes. Each corresponds to a Windows DLL. There is USERS.SCR, GDIS.SCR, MOUSES.SCR, etc. Each one's LibMain calls the same entry point in FIXMGR.DLL.

11-Mar-94 01:14:18
Fm: Art Rothstein
I did a little more work, dumping the kernel's code segments from memory in Windows 3.1 and in OS2fW, then examining the differences. By my count there are 60 places [in KRNL386 alone] where the code is patched to do a far jump, probably to a patch module. Most of the patches are very dependent on the exact code, e.g., the number of residual bytes to NOP after the far JMP. These patches could not survive more than a trivial change to KRNL386.EXE. It's no wonder they are incompatible with Windows 3.11.

15-Mar-94 10:57:50
Fm: Art Rothstein
I have uploaded OS2DIF.ZIP in DDJFORUM data library 3. There are four DIFF.B0? files in this archive, one for each global heap code segment of KERNEL. We wrote a program that used TOOLHELP functions to find each such code segment and dump it to disk. We ran the program under retail Windows 3.1 and under OS2fW. We used the DOS FC command to compare the resulting files byte for byte. For example,
[LISTING ONE, PAGE 130] from WIN.B01, is an in-memory comparison of two fragments from KRNL386, under Windows 3.10 vs. OS/2 for Windows.

We used Sourcer to disassemble KRNL386.EXE and started matching disassembled instructions to patched instructions. We have almost completed this exercise for segment 1. Most patches take the form of a far-jump instruction, a 5-byte sequence beginning with 0EAH. Segment 1 has 47 such patches. Segment 2 has 12. Segments 3 and 4 have none.

Our current focus is to determine how the patches are applied, in particular to understand the type of validation performed prior to application of a patch. The code of interest appears to be at offset 0C55H in WINOS2.COM's code segment. Among the data structures used are a module table, which has the signature 'MH' at offset 26H; and a script table, which has the signature 'SH' at offset 1AH. The eight .SCR files (COMMS, CONTROLS, GDIS, MOUSES, TIMERS, USERS, VGAS and WINFILES), which are actually DLLs, contain the same structures and appear to be processed by the same code in WINOS2.

15-Mar-94 20:32:31
Fm: Andrew Schulman
Information Week (March 14) has an article on OS/2 for Windows. Rogers Weed, Microsoft's marketing product manager for Windows, is quoted saying (rightly, I think) that IBM should bear the burden of future compatibility with Windows. "But Weed suggests that IBM may not be up to the task. Since OS/2 relies on a fixed image of Windows in memory to run correctly, Weed notes that 'any change to Windows will require that IBM change OS2fW to keep up.' Because IBM, as of last September, no longer has access to the Windows source code, it now must reverse engineer Windows every time the code changes so its products can remain compatible."

It came out during Stac vs. Microsoft that IBM reverse engineered the preload interface in MS-DOS 6--the same preload interface that Stac reverse engineered, and that Microsoft claims is a trade secret. Now it turns out that IBM is going to have to reverse engineer Windows, if they aren't doing so already.

The obvious question is whether Microsoft will next go after IBM for doing the exact same thing Stac did. IBM's patching of Windows obviously involves far "deeper" reliance on specific Microsoft code than anything Stac did! [And IBM couldn't get specific bytes and offsets by looking at the Windows 3.10 source code.]

Secondly, it's interesting that IBM is engaged in reverse engineering, while the company's lawyers are actively trying to have it outlawed.

Analyzing the Patches

17-Mar-94 17:21:13
Fm: Andrew Schulman
Art, here are few quick comments, based on your diff files, about what IBM is patching in the Windows kernel:

KRNL386 seg #1: The variable at 1.0032 (labelled data_0368 in the Sourcer disassembly you show) [LISTING ONE] is a writeable selector to the LDT. KRNL386 creates this using a call at 1.C3B0 to INT 2Fh function 168Ah (see Pietrek, Windows Internals, pp. 16, 18, 90). In Undocumented Windows (entries for GetSelectorBase and SetSelectorBase), this variable is called WIN_LDT. The actual name in Windows, Pietrek explains, is very confusing: "GDTDsc." In any case, we now know from your DIFF.B01 that OS2fW replaces some references to WIN_LDT/GDTDsc with far jumps.

Your DIFF.B01 shows ten patches involving 1.0032. My KRNL386.LST shows many more references than that: at least 32. And those are just the ones that Sourcer has correctly resolved. There are many, many more that Sourcer didn't link up properly. :-)

It's confusing that we don't see many more differences between 3.10 and OS2fW. Maybe the WIN_LDT selector is still valid but read-only?

KRNL386 seg #1, offset 80E2h: They're NOPing out a call to 2F/1689, which is the KERNEL idle call (Pietrek, pp. 418--420). KRNL386 generates a ton of these calls, one every time it gets to the idle portion of the internal Reschedule routine.

19-Mar-94 01:35:47
Fm: Roger Alley
Description of areas being patched by OS/2:

I've mainly relied on Matt Pietrek's Windows Internals (WI). The following function names are from that book, and the page number where a routine's general description appears is noted.

Art's listing shows that cs:[0032] contains 0117h, probably a valid handle, but the R/W status can't be determined. I'm pretty sure it's valid and can be read. For example, 1.2261 is an unpatched function called from various locations which uses cs:[0032] to read descriptor information.

Here are some of the patches: [Only a few of the patches Roger analyzed are shown here; the full thread is available electronically; see page 3.]

1.13FC: In GlobalPageLock (1.13E9, WI p. 159), replacing the call to DPMI function 4 (a supposedly obsolete [undocumented] function which locks the pages of the selector in bx) with a jump to C7:155D. I guess IBM's DPMI did not implement this function.

1.1E18: In (the undocumented) AllocSelectorArray (1.1DF5, WI p. 89). This routine gets a sequential list of LDT selectors (via Get_Sel), and then sets the DATA and PRESENT access-rights flags in each of their descriptors. The patch replaces the instruction which sets the flags, and the following instruction which moves bx to the next selector, with a jump to C7:1665. [In a later message, Roger shows that the jumped-to replacement code calls DPMI int 31h function 9.]

1.2017: In Free_Sel (1.2009, WI p. 94). The patch is replacing a test of GDTDsc (WIN_LDT, cs:[0032]) with a jump to C7:16B3. My guess is that 16B3 is just setting the Z flag, as the next instruction (jz Free_to_DPMI) will jump around the descriptor mucking.

More on the Patch Engine

19-Mar-94 13:20:45
Fm: Art Rothstein
[It's been suggested that OS2fW might be sensitive to changes in the size of Windows executable files.] The issue is not the length of the file, but the length of the segments that will be patched. The first script table for each module table contains a nonzero WORD length at offset 4 if WINOS2.COM should validate the segment limit. At least for GDI.EXE, USER .EXE, TIMER.DRV, COMM.DRV and WINFILE.EXE, the value in the script table matches exactly the size of one of the module's code segments in Retail 3.1, as reported by EXEHDR. Aware that code segments are allocated from the global heap, the patch machine massages the value from the script table before comparing it with the value returned by LSL for the target selector. If the comparison is not exact, WINOS2.COM will not apply any of the patches in that script table.

19-Mar-94 22:10:52
Fm: Art Rothstein
WINOS2.COM loads KRNL386.EXE via DOS function 4B01h, then executes its patch machine on a short script (from DS:050B). The script copies 8 bytes of instructions from the kernel's initialization code to a patch area in WINOS2.COM, then replaces these instructions with a far jump to the patch area. To see the instructions patched, run DEBUG KRNL386. EXE and U 75 L 8. At the end of the patch code is a far jump back to the first instruction in the patched KRNL386 code.

This patch code, at CS:12FB in WINOS2. COM, executes the patch machine on another short script (from DS:04BF). The effect of this patch is to insert over 400 bytes of instructions after the ADD AX,10H in segment 1 of KRNL386. The original kernel code appears to be initializing DPMI services. As part of this lengthy patch, WINOS2.COM executes the patch machine on a long script (from DS:17C7) that places 71 distinct jumps from the kernel to code in WINOS2.COM.

One of the many patches applied from the DS:17C7 script is at offset B488 in kernel segment 1, where the kernel processes parameters from the BOOT section of SYSTEM.INI. The patch code loads FIXMGR.DLL if it is not already loaded. In its LibInit routine, FIXMGR loads 8 DLLs. All the filenames have the SCR (as in SCRipt) extension. The prefixes are GDIS, USERS, MOUSES, CONTROLS, WINFILES, COMMS, TIMERS and VGAS. The LibInit of each routine calls the REGISTERSCRIPT entry point in FIXMGR, which in turn calls a routine in WINOS2.COM (its address was passed to FIXMGR's LibInit routine) that adds the respective DLL's module table to the singly linked module table chain. Another patch applied from the DS:17C7 script is at offset 759B in kernel segment 1 [in a subroutine used by FarSegmentLoad; WI p. 260]. This patch runs the module table chain, looking for one that matches the current selector. If it finds a matching module table, it runs the patch machine. [LISTING TWO, page 130, shows the format of the script table, and of each patch block type.]

As an example, [LISTING THREE, page 130] shows the data for the first script in the bootstrap process, from the script table at DS:050B in WINOS2.COM.

Analyzing More Patches

20-Mar-94
Fm: Roger Alley
Some additional areas patched by OS/2. [Again, just a few of Roger's descriptions for KRNL386 are shown here.]

1.214C: In PrestorChangeSelector (1.213E, WI p. 97). This patch is right at the start of the routine--only the epilog, register saving, and ds loaded from GDTDsc have been done. The code for loading es with GDTDsc, setting si to the source descriptor, and then masking off the bottom three bits is replaced with a jump to 16E5.

1.21C1: In AKA (1.21A3, WI p. 99). The code from "Save aliasSelector in BX" through and including "if (isData) Turn on the CODE bit in the alias descriptor" is replaced with a jump to C7:16E5. The code being replaced copies and then manipulates a selector.

1.269F: In Get_Blotto (1.2674), which get the "blotto" selector, used to zero out memory segments.

1.2735: In SetSelectorBase (1.2725, WI p. 97). The patch replaces a save and subsequent load of ds (with GDTDsc) with a jump to C7:182A. [The jumped-to code ends up calling DPMI INT 31h function 7 (Set Selector Base). This is important, not only because OS/2 can't allow Windows to write to the LDT, but also because testing by Art Rothstein showed that OS/2's implementation of DPMI prevents certain operations allowed by Windows, such as mapping the linear address of the Global Descriptor Table [GDT] into a program's address space.]

1.277D: In SetSelectorLimit (1.276D, WI p. 95). The routine loads bx with the selector, saves ds, and loads ds with GDTDsc. The next two instructions, which strip the flag bits from bx and load ax with LOWORD(limit), are replaced with a jump to C7:1839.

1.299D: In Get_Arena_Pointer32 (1.297C). This routine takes a handle and returns the 32-bit offset to its arena (by looking it up in the Selector Table--see WI, p. 110). The routine is allowed to construct the offset into the Selector Table, and then the patch replaces the instructions which add in the base of the Selector Table, and move the arena pointer to eax, with a jump to C7:1880.

1.2A47: In xLockLinearRegion (1.2A33), called by GReAlloc. This routine takes a linear address and size, and simply passes them to DPMI-Lock Linear Region. The patch replaces the mov ax,0600/int 31 with a jump to C7:1896.

1.2B66: In DPMIProc (1.2B42). This routine is just a DPMI wrapper that catches functions 000B, 000C, 0007, 0006, and 0009 and processes those itself (by reading or writing from/to the LDT). The patch allows all calls with ah != 0, or ax == 000B, to process normally. It replaces the check for al == 0C (Set Descriptor), and the next 2 instructions, with a jump to C7:18F6.

1.2BAD: In DPMIProc also, replacing the test for al = 09 (Set Descriptor Access Rights), and the next 2 instructions, with a jump to C7:18FB. Because of the presence of this and the previous patch, I would guess that only these 2 functions (Set Descriptor, Set Descriptor Access Rights) are being overridden.

1.408A: In GAlloc (at 1.4025, WI p. 128), this code replaces a DPMI function 0004 call (at the bottom of p. 129) with a jump to C7:1900.

22-Mar-94 14:02:57
Fm: Roger Alley 71163,2407

Here's a listing for the few patches which I have fully analyzed. [Up to now, Roger had shown what Windows 3.10 code OS2fW would overwrite with far jump instructions to OS/2 segment C7h; in the descriptions that follow, he examines what happens at the jumped-to code in segment C7h.]

1.1E18: In undocumented AllocSelectorArray (1.1DF5, WI p. 89). This routine gets a sequential list of LDT selectors (via Get_Sel), and then sets the DATA and PRESENT flags in each of those selectors. The patch replaces the instruction which sets the flags directly in Kernel's copy of the LDT with a call to DPMI Set Descriptor Access Rights. [LISTING FOUR, PAGE 130.]

1.1E3C: In Get_Sel (1.1E2B, WI p. 90). This patch replaces "if (*FirstFreeSel == --1) goto try_DPMI" with "goto try_DPMI." This has the effect of always using DPMI, and skipping a lot of descriptor-mucking code. [LISTING FIVE, PAGE 130.]

1.1FB4: In Alloc_Sel (1.1F55). This patch changes some descriptor mucking. The original code just made the changes directly to Win_LDT. The new code copies the descriptor from Win_LDT into local storage, makes the changes, and then issues DPMI Set Descriptor. Note that, on entry to this code, the contents of ax are the same as the value on the top of the stack, and es:di points to a descriptor in local storage. [LISTING SIX, PAGE 130.]

1.2017: In Free_Sel (1.2009, WI p. 94). The patch effectively replaces the statement "if (GDTDsc == 0) goto Free_to_DPMI" with "goto Free_to_DPMI," skipping the direct descriptor-mucking code and using DPMI instead.

So What Changed in Windows 3.11?

24-Mar-94 06:25:13
Fm: Roger Alley

I have completed a first pass over the differences between Windows 3.10 and 3.11 for KRNL386.EXE. [This Windows 3.11 KRNL386.EXE is identical to the one in Windows for Workgroups 3.11.] The file seems to have been regenerated by a rebuild. The segments begin at different offsets in the file, and the new file contains the VERSIONINFO resource. The differences represent minor changes.

Data segment: The string "Incorrect MS-DOS version. MS-DOS 3.1 or greater required" was replaced with a very long string that began with "Windows for Workgroups 3.11 could not start. Make sure_". The rest of the string states that MS-DOS 3.30 is required, and that "files=" has to be at least 20. This seemed to be the only change in the data segment, and it threw the addresses off D5h bytes. A later paragraph alignment returned 5 bytes, so addresses after that were off D0h bytes.

Kernel segments 2 and 3: No changes.

Kernel segment 1: GetExePtr (WI p. 475) now checks if the passed-in handle is a valid selector. Because GetExePtr is called from various routines, and the parameter passed in can be one of many different things (such as a module handle, an instance handle, a memory handle), it's possible that an invalid parameter may not be caught in the normal validation, and thus GetExePtr could receive a garbage value. If that value was odd, but not a valid selector, KERNEL would GP fault (at 1.4E84). This fixes the bug by verifying that the parameter, if odd, is a valid selector.

At this point, 5 bytes have been added.

There's a null byte, which does not seem to be used, inserted in front of UnlinkObject. [This aligns the routine on a word boundary, but there's no particular reason to align this one when so many other routines are unaligned.]

At this point, we're +6.

The routine at 5A34, called indirectly by OpenFile, has a number of (unanalyzed) changes to it.

Amazingly, we're even again (this saved a lot of time).

The routine at 88C8, called from PreloadResources, replaces a call to a routine which does nothing (at 52EF) with a test and then, possibly, a call to the routine at 5302 (previously 52FC). I'm not sure what the effect of this is.

Now +14 (decimal).

At (old) A820, there's a large table, which is now 2 bytes larger, possibly because of a paragraph align.

Now +16.

In InitDosVarP (WI p. 22), part of the kernel initialization, the program now checks for at least DOS 3.30 instead of DOS 3.10.

That's it, pretty minor from what I can tell so far. [Of course, there are other modules, such as USER.EXE.]

What's It All Mean?

23-Mar-94 16:05 EST
Fm: Art Rothstein
After discussing IBM's business reasons for creating OS2fW (even the name of the product is inspired), you should launch into a discussion of the technical problems such a product must solve. Among the problems that come to mind are modification of descriptor tables; direct screen modification when a WinApp or Windows itself (yes, you can do this) runs in a window on the PM desktop; spawning of DOS applications. OS/2 must also provide replacements for system services that enhanced mode Windows requests from VMM, such as DPMI.

The fact that IBM solved these technical problems, and that they got it right the first time, is a minor miracle. The sheen on the miracle fades, however, when we try to run OS2fW with Windows 3.11, Microsoft's so-called refresh release of 3.1. ("Windows 3.11. It's not an upgrade. It's just our way of letting you know who's boss.")

You should reflect on the sheer number of patches and on their dependence on the exact location of Windows 3.1 code and the exact instructions at those locations. Keeping up with Microsoft's changes, even if Microsoft does not intentionally create obstacles, can keep at least a few IBM developers constantly busy.

While the patch machine does validate the size of most patched segments, it does not validate instructions. Most patch programs, going back to IBM's Super Zap utility on MVS, make replacement contingent on successful validation.

[LISTING ONE]: In-memory comparison of two fragments from KRNL386: Windows 3.0 vs. OS2fW.

00000030: 87 2F    DGROUP
00000032: 17 C7
00000033: 01 00
 ...
000013FC: EA B8  1.13FC  B8 0004           mov ax,4
000013FD: 5D 04  1.13FF  CD 31             int 31h
000013FE: 15 00
000013FF: C7 CD
00001400: 00 31
 ...
00002017: EA 2E  1.2017  2E: 83 3E 0032 00 cmp cs:data_0368,0 ; (1.0032=0)
00002018: B3 83
00002019: 16 3E
0000201A: C7 32
0000201C: 90 00
 ...

[LISTING TWO]: OS2fW patching-script format and patch block types (Art Rothstein, 19-March-1994).

Script table
  0- 1 Offset in this segment of next script table, 0 if none
  2- 3 Which segment of this module patches refer to:
  4- 5 Segment limit to check, 0 if none
  6- 7 Offset of patch data
  8- 9 Offset of validation table, FFFF if none
 0A-0B Selector of patchee for block types 06 and 07
 0C-0D Selector of patchee for block type 04
 12-13 Offset to place in jump instruction patches
 14-15 Selector of patcher for block types 04 and 06
 16-17 Selector of patcher for block type 07
 18-19 Unknown
 1A-1B 'SH' signature

Patch data is a set of contiguous blocks.  Each block has a 2-byte header.
  0- 0 Block type
      01 Change internal state
      02 Set patchee offset for subsequent block types 04, 06 and 07.
      03 Determine patchee offset to use in a subsequent block type 04.
      04 Make patcher return to patchee.
      05 Determine patchee offset to use in subsequent block types 06 and 07.
      06 Save patchee instructions in patcher, replace with NOPs.
      07 Make patchee jump to patcher (EA offset segment).
      08 May not be interesting.
      FF End of patch data
  1- 1 Number of bytes remaining in the block after this byte

The remaining bytes in a patch block depend on the patch type:

Type 01 Change internal state
  2- 3 New internal state

Type 02 Set patchee offset for subsequent block types 04, 06 and 07.
 0A-0B 0001
 0C-0D Patchee offset.

Type 03 Determine patchee offset to use in a subsequent block type 04.
 08-09 Value to use unconditionally, unless FFFF.
 0A-0B Value to pass for massaging if FFFF in 08-09.

Type 04 Create instructions in patcher to return to patchee.
  2- 3 Instruction type
      0001 Far jump (EA offset segment)
      0002 IRET (CF)
      0003 Far jump (PUSH segment, PUSH offset, RETF)
  4- 5 Offset in patcher
 The segment of the patchee comes from [BX+0C].
 The segment of the patcher comes from [BX+14].


 The offset in the patchee is the sum of offsets derived from the
  preceding block types 02 and 03.  For the kernel, at least, block
  type 02 always contributes a zero.

Type 05 Determine patchee offset to use in subsequent block types 06 and 07.
 08-09 Value to use unconditionally, unless FFFF.
 0A-0B Value to pass for massaging if FFFF in 08-09.

Type 06 Save patchee instructions in patcher, replace with NOPs.
  2- 3 Offset in patcher
  4- 5 Number of bytes to patch
 The segment of the patchee comes from [BX+0A].
 The segment of the patcher comes from [BX+14].
 The offset in the patchee is the sum of offsets derived from the
  preceding block types 02 and 05.  For the kernel, at least, block
  type 02 always contributes a zero.

Type 07 Make patchee jump to patcher (EA offset segment).
  1- 2 0001
  2- 4 Offset in patcher
 The segment of the patchee comes from [BX+0A].
 The segment of the patcher comes from [BX+16].
 The offset in the patchee is the sum of offsets derived from the
  preceding block types 02 and 05.  For the kernel, at least, block
  type 02 always contributes a zero.

[LISTING THREE]: A sample OS2fW patch script.

01 02  01 00
03 0A  FF 00 FF 00 FF 00 FF FF 7D 00
04 04  01 00 35 13
05 0A  FF 00 FF 00 FF 00 FF FF 75 00
06 04  FB 12 08 00
07 04  01 00 FB 12
FF 00

[LISTING FOUR]: AllocSelectorArray.

1:1E18      <B>Old code</B>                      <B>New code</B>

            ; BX is an LDT offset
            mov   [bx+5],cx               mov   ax,0009h
                                          or    bx,7  ; make into selector
                                          int   31h

[LISTING FIVE]: Get_Sel.

1:1E3C      <B>Old code</B>                      <B>New code</B>

            mov   ax,[si]                 mov   ax,[si]
            inc   ax                      inc   ax
            jz    try_DPMI                cmp   ax,ax
                                          jz    try_DPMI

[LISTING SIX]: Alloc_Sel.

1:1FB4      <B>Old code</B>                      <B>New code</B>

            and   bl,F8h                  add   sp,2
            pop   [bx+5]                  cld
            mov   ax,[bp+4]               movsd
            mov   [bx],ax                 movsd
            mov   [bx+7],cl               sub   si,8
                                          sub   di,8
                                          mov   es:[di+5],ax
                                          mov   ax,[bp+4]
                                          mov   es:[di],ax
                                          mov   es:[di+7],cl
                                          or    bx,7
                                          mov   ax,000Ch
                                          int   31

Copyright © 1994, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.