mirror of https://github.com/lianthony/NT4.0
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
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".
|