Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

233 lines
10 KiB

This is an overview document describing how to create module patches
in Windows 95.
Legal aspects:
--------------
While we are obviously using this module-patching technology to
make already-shipping apps run or run better, it is possible
for us to make a mistake and wrongly patch a module. For example,
there might be two similar versions of an app which need to be
patched differently, but we only know about one of them. Due to the
obvious liability concerns, we need to obtain authorization from
the app vendor for each module patch we put into the registry.
The letter must specify the name of the module, the number of the
segment taking the patch, the bytes being replaced, and the new
bytes taking their place. The letter can be from any responsible
person at the vendor, eg. an engineer who is familiar with the code.
At the same time, talking to the vendor will help us find out how
many different versions there are of an app, so we can figure out
how to patch each one.
See brianrey if you have a module patch you want to check in.
It would be a good idea to get the ball rolling on getting the
letter as soon as possible after you know what the patch is, since
it can sometimes take a while to get these things taken care of.
We already get similar letters for app-hack bits.
How to check in a patch:
------------------------
Since the patch values go in the registry, and since the initial registry
contents are owned by setup, you must work with Andy Hill to get your
module patches checked in.
A common mistake when adding app-patches to msbase.inx is to leave out
field 4 (field 3 if you are a computer), which are flags. The reason
for the confusion is the flag that means "This is a binary regkey" is 01,
and the opcode for "Change" is also 01, so everything just shifts down
one step and nobody gets hurt. Except that the patch doesn't work.
eg: If you want to add a key whose value is <01,09,70,00,02,ff,76,eb,15>
WRONG
HKLM,"SYSTEM\...",Change,01,09,70,00,02,ff,76,eb,15
BETTER
HKLM,"SYSTEM\...",Change,1,01,09,70,00,02,ff,76,eb,15
How to make a patch:
--------------------
All definitions of the structures used below are in core\kernel\patch.asm.
The loader knows to look in the registry for patch data if the MCF_MODPATCH
bit is set in the [ModuleCompatibility] section in win.ini.
eg.
[ModuleCompatibility]
GENERIC=0x0002
All registry keys and values for module-patching are stored
under HKEY_LOCAL_MACHINE under REGSTR_PATH_APPPATCH:
(from dev\sdk\inc\regstr.h)
#define REGSTR_PATH_APPPATCH "System\\CurrentControlSet\\Control\\SessionManager\\AppPatches"
The actual patches are values stored in the registry under
REGSTR_PATH_APPPATCH\<mod_name>\<signature>\<segment_number>
where
mod_name = the module name
signature = a signature string identifying the module version
segment_number = the hex number of the segment receiving the patches
Signatures:
-----------
Signatures are strings of ANSI characters representing hex numbers
in nibble-swapped byte format (what you see in wdeb386 if you type
db). Blanks and commas are ignored, and may be included for readability.
The easiest way to create a signature is to run dev\tools\binw\gensig.
eg. If you want to patch segment 3 in foo.dll, run "gensig foo.exe 3"
and a tight signature will be written to stdout.
type-1 and -2 signatures:
A type-1 signature specifies a list of byte sequences with byte offsets
(type-2 has word offsets) which must all match for the signature to match.
A 0 byte-count marks the end of the list.
A match-any-file signature is "0100".
eg. signature = "01 02,00,4e,45 02,3e,0a,03 00" is a type-1 signature which
|| || || || || || || || || ||
type --------++ || || || || || || || || ||
|| || || || || || || || ||
byte count------++ || || || || || || || ||
offset-------------++ || || || || || || ||
match bytes-----------++-++ || || || || ||
|| || || || ||
byte count------------------++ || || || ||
offset-------------------------++ || || ||
match bytes-----------------------++-++ ||
||
terminating byte count------------------++
matches if the 2 bytes starting at offset 0 in the exe header match
4e,45 ("NE") and if the 2 bytes starting at offset 3e match 0a,03.
Note- DO NOT include a match on NE in your signature. This is just
for illustrative purposes. The windows version and size(s) of the
segment(s) being patched are good candidates.
type-3, -4 and -5 signatures:
Type-3, -4 and -5 signatures are similar to type-1 and -2 signatures,
except that the offsets are offsets in the module file. Type-3 takes
word offsets, type-4 takes 3-byte offsets, and type-5 takes dword offsets.
eg. signature = "03,03,67,05,c2,0a,00,00"
|| || || || || || || ||
type---------++ || || || || || || ||
|| || || || || || ||
byte count------++ || || || || || ||
file offset--------++-++ || || || ||
match bytes--------------++-++-++ ||
||
terminating byte count------------++
matches if the 3 bytes at offset 567h in the file match c2,0a,00.
type-6, -7 and -8 signatures:
Type-6, -7 and -8 signatures specify the file size of the matching
module. The number of bytes used to specify the matching file size
is 2, 3 or 4 respectively.
eg. signature = "06,d0,0c"
|| || ||
type---------++ || ||
file size-------++-++
matches if the file size is 0cd0h.
type-ff signatures:
Type-ff signatures are meta-signatures and consist of a list of the
other types of signatures. Each list element consists of a byte count
and a sub-signature. A 0 byte-count ends the list.
eg. signature = "ff 06,01,02,3e,0a,03,00 03,06,d0,0c 00"
|| || || || || || || || || || || || ||
type---------++ || || || || || || || || || || || ||
|| || || || || || || || || || || ||
byte count------++ || || || || || || || || || || ||
sub-type-----------++ || || || || || || || || || ||
sub-byte-count--------++ || || || || || || || || ||
hExe offset--------------++ || || || || || || || ||
match bytes-----------------++-++ || || || || || ||
sub-type terminator---------------++ || || || || ||
|| || || || ||
byte count---------------------------++ || || || ||
sub-type--------------------------------++ || || ||
file size----------------------------------++-++ ||
||
terminating byte count---------------------------++
matches if the 2 bytes at offset 3eh in the exe header match 0a,03
and the file size is 0cd0h.
Criteria for selecting a signature:
-----------------------------------
In terms of time required to validate a signature, the hExe types are
the fastest, the file size types are next-fastest, and the file data
types are the slowest. A signature specifying the expected Windows
version (hExe offset 3e) and the file size is probably sufficient
for most cases, since almost any code change changes the file size.
A signature which also requires a file match on the bytes being replaced
(if they are not fixed up and they are in a segment, they must be
somewhere in the file!) is very good, but might be overkill.
Since file match signatures hit the disk, they might noticeably increase
the load time of the module.
DO NOT match on the NE signature in the module header. This is
just a waste of bytes, since it always matches.
DO match on the file size unless you have a very good reason not to.
If going through the ifs, this is a very cheap test.
DO match on something, since we don't want to rely on just the
name of the module. We have signatures. Use them. If you have
no better idea (eg. there is only one version), match on the
windows version, the size(s) of the segment(s) being patched, and
the file size.
Patch data:
-----------
The patch values specify sequences of bytes to add or replace.
Add's must be placed after the end of the unpatched segment.
The segment is GlobalReAlloc'd as necessary to contain the new code.
Replace's must be within the limits of the unpatched segment,
and they must match the sequence of old bytes in the patch value.
Obviously, replaced bytes cannot contain fixups.
The contents of the value string are not used, except to be sent
to the debugger as a debugging aid when the value is loaded from
the registry. The contents of the value data must be in REG_BINARY
format.
Example:
--------
Note: The registry key show below will not work as shown, since it
has been broken into multiple lines for legibility. Join the lines
together and it works.
==================================================================
REGEDIT4
[HKEY_LOCAL_MACHINE\System\CurrentControlSet\control\SessionManager
\AppPatches\GENERIC
\ff 06,01,02,3e,0a,03,00 03,06,d0,0c 08,03,03,67,05,c2,0a,00,00 00
\1]
"Add"=hex:02,08,f0,03,03,c2,0a,00
"Replace"=hex:01,0b,67,00,03,c2,0a,00,e9,86,03
==================================================================
The Add value has data with a size of 8 bytes, saying that at offset
3f0h that the 3 bytes c2,0a,00 should be added.
"Add"=hex:02,08,f0,03,03,c2,0a,00
|| || || || || || || ||
type=Add--++ || || || || || || ||
total bytes--++ || || || || || ||
offset----------++-++ || || || ||
byte count------------++ || || ||
bytes to add-------------++-++-++
The Replace value has data with a size of 0bh bytes, saying that at
offset 67h that the 3 bytes c2,0a,00 should be replaced with e9,86,03.
"Replace"=hex:01,0b,67,00,03,c2,0a,00,e9,86,03
|| || || || || || || || || || ||
type=Replace--++ || || || || || || || || || ||
total bytes------++ || || || || || || || || ||
offset--------------++-++ || || || || || || ||
byte count----------------++ || || || || || ||
old bytes--------------------++-++-++ || || ||
new bytes-----------------------------++-++-++
The combined effect of these two changes is to replace a "retn a" at 67
with "jmp 3f0" and, at 3f0 to add "retn a". The app runs exactly as
before in this case, except for a small detour. When it reaches ip=67,
instead of doing "retn a", it does a "jmp 3f0" and then the "retn a".