Updated source code to upstream 140201 version

This commit is contained in:
Adrian Gibanel Lopez
2014-10-18 22:25:05 +02:00
parent 05445b7200
commit 0ec5c9cdf7
15 changed files with 5892 additions and 662 deletions
+41 -1
View File
@@ -1,7 +1,7 @@
The Offline NT Password Editor
(c) 1997-2011 Petter Nordahl-Hagen
(c) 1997-2014 Petter Nordahl-Hagen
See README for general info, copyright & credits.
See INSTALL for compile/installation instructions.
@@ -9,6 +9,46 @@ See INSTALL for compile/installation instructions.
Revision history
(tools only, see website for info on bootdisk revisions)
* v 1.00 140201 (beta releases to some people dated 1308xx)
samlib:
new library, contains functions for user and group
manipulation and low level SID functions. Main features:
- add user to local group
- remove user from local group
- reset user password
- list users and groups, with memberships etc
- SID handling routines (binary to/from string etc) required by
group/user edit functions
- updates both group and user correctly, thus finally user "promotion" works!
regedit library:
- Enter buil-in buffer debugger only if in trace mode, else return error or abort()
- Fixed critical bug in del_value which could
thrash the hive when removing value in bottom of key.
- And a pointer not reinitialized when buffer reallocated in some cases, fixed.
Thanks to Jacky To for reporting those two.
- Some minor adjustments for compiler. A few more utility functions.
sampasswd:
new command line utility
- Can be used to reset users passwords from scripts (non interactive)
- Also a few list options, also can supply parsable output
samusrgrp:
new command line utility
- edit group memberships (non interactive)
- some list functions, parsable
chntpw:
- Now dedicated to interactive edits
- Now supports full local group membership edits, finally working promotion!
- A lot of users complained about user select being difficult to exit
(not understanding to use ! to quit), so after user edit, return
is to main menu or program exit (if not -i option)
- User edit (reset, promote etc) now loops instead until q for quit
* v 0.99.6 110511
regedit library (chntpw and reged uses it):
+20 -10
View File
@@ -1,13 +1,13 @@
The Offline NT Password & Registry Editor
(c) 1997-2011 Petter Nordahl-Hagen
(c) 1997-2014 Petter Nordahl-Hagen
This file is meant for developers.
General usage is via the provided boot CD images, but
static linux builds of the tools are also available
in the source zip file.
static 32 bit linux builds of the tools are also available
in the "static" zip file available on my website.
See REAMDE for general instructions and license info,
HISTORY.txt for version info.
@@ -16,7 +16,12 @@ If you want to build yourself, it should compile
with most gcc compilers. However, I am not particulary good
with types, so there may be some warnings.
You may have to install the openssl-devel package from you
Unlike earlier, crypto stuff is not compiled in by default any more,
but that only disables function to set new specific password,
which does not seem to work well on newer Windows anyway.
However, you can enable it in the Makefile, if so
you may have to install the openssl-devel package from you
linux distro to compile/link, since I need the static variant.
(change the makefile to link dynamic if you wish)
I link statically with libcrypto.a from OpenSSL.
@@ -26,15 +31,23 @@ Thanks to Denis Ducamp for modifying chntpw to use OpenSSL.
Thanks to Mike Doty, via Alon Bar-Lev for 64 bit build compatible patch.
(from http://bugs.gentoo.org/show_bug.cgi?id=185411)
which I may or may not have broken again i 2013...
Look into Makefile, you may need to change things to fit your env.
(also if you need 64 bit)
also if you need 64 bit.
Warning: I mostly build on 32-bit and may not have tested on 64 bit
in every version! (32-bit binaries run fine on 64-bit systems anyway,
and it does not matter if Windows it edits registry from is 32- or 64-bit)
If make goes well, you should have:
chntpw - Password tool, dynamic with libc, static with OpenSSL
chntpw - interactive password tool
chntpw.static - Completely static passord tool
reged - Registry edit tool, dynamic linked with libc. No crypto.
reged.static - Registry too, statically linked.
reged.static - Registry tool, statically linked.
sampasswd - user list and password reset commandline util
sampasswd.static - same but statically linked
samusrgrp - group list and group membership tool
samusrgrp.static - static version of same
cpnt - Simple copy utility, does not truncate file on write
Now deprecated, was at least needed earlier for NTFS writes.
@@ -42,7 +55,4 @@ Also, the floppies and CDs are build under a different
environment for small libc size (uClibc), see website
for details. (build scripts not currently available..)
So, note that the binary then contains cryptographic code
which may be illegal for you to export.
Good luck.
+513
View File
@@ -0,0 +1,513 @@
The Offline Windows Password Editor
(c) 1997-2014 Petter Nordahl-Hagen
Command line utilities
All these programs need one (or more) registry files to work on.
These are the binary registry files, called "hives" that usually are
located on a Windows machine under \WINDOWS\System32\config
where the most important ones are:
SAM - HKEY_LOCAL_MACHINE\SAM, Security Accounts Manager
contains local user / group info, user passwords are here
SYSTEM - HKEY_LOCAL_MACHINE\SYSTEM: System settings / configuration
(services, drivers, hardware info, some boot config)
SOFTWARE - HKEY_LOCAL_MACHINE\SOFTARE: Config and info of installed
software and a lot of higher level windows config
Note that these programs (and the registry library they use) does not
join all these files in the same tree like windows does.
For example, a path like
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control
is not valid, it is instead just
ControSet001\Control
(after selecting the hive if more than one is loaded)
But except if you use the registry edit or export/import functions you
do not need think about this as for other things the tools handle
this.
chntpw: Program for interactively resetting passwords and group
memberships.
My boot CD runs this with options -i -L SAM
chntpw: change password of a user in a Windows SAM file,
or invoke registry editor. Should handle both 32 and 64 bit windows and
all version from NT3.x to Win8
chntpw [OPTIONS] <samfile> [systemfile] [securityfile] [otherreghive] [...]
-h This message
-u <user> Username or RID (0x3e9 for example) to interactively edit
-l list all users in SAM file and exit
-i Interactive Menu system
-f Interactively edit first admin user
-e Registry editor. Now with full write support!
-d Enter buffer debugger instead (hex editor),
-v Be a little more verbose (for debuging)
-L For scripts, write names of changed files to /tmp/changed
-N No allocation mode. Only same length overwrites possible (very safe mode)
-E No expand mode, do not expand hive file (safe mode)
-u <user> Username or RID (0x3e9 for example) to interactively edit
Invoke the interactive edit menu on specified user.
Specifying a user name will most likely fail if user has international
character, so better to use user ID (RID), for example
chnptw -u 0x3e9 SAM
to edit user with hexadecimal RID 3e9
-l list all users in SAM file and exit
Just that, list users in human readable form, with some info about if
user is admin and if password is set.
-i Interactive Menu system
Invokes the menu system. Menu items will vary a bit depending on what
registry hives are loaded.
-f Interactively edit first admin user
Select first admin user for edit. This is user with lowest RID that
also is member of administators group, or built-in user 0x1f4 if not
others possible.
-e Registry editor. Now with full write support!
Enter the registry editor. It is a small command system. ? for help
there. See other documentation for more on regedits.
-d Enter buffer debugger instead (hex editor),
Command line type hex editor, mostly for debugging purposes. ? for help.
-v Be a little more verbose (for debuging)
Lots of debug output during most operations (especially hive loading)
-L For scripts, write names of changed files to /tmp/changed
If any of the other functions changes the registry, the changed files
are listed here. Can be used by wrapper scripts to know what to save.
My boot CD uses it.
-N No allocation mode. Only same length overwrites possible (very safe mode)
Safe mode. Will only allow changes in registry that overwrites old
values with same length data. Password reset only changes 2 bytes, and
does not change value lenght, so password reset will still work in
this safe mode. If something tries to violate this safe mode, a lot of
error messages (some of the rather obscure) may occur.
-E No expand mode, do not expand hive file (safe mode)
Safe mode. Does not allow expanding the size of the file, but will
allow adding keys/values as long as there is free space in the file
already. (most files contains some free space)
If expansion is needed but not allowed by this option,
a lot of obscure error messages may occur, and file should not be saved.
-------------------------------------------------------------------------
reged: Registry editor and export and import of .reg files.
reged version 0.1 110511, (c) Petter N Hagen
Modes:
-x <registryhivefile> <prefixstring> <key> <output.reg>
Xport. Where <prefixstring> for example is HKEY_LOCAL_MACHINE\SOFTWARE
<key> is key to dump (recursively), \ or \\ means all keys in hive
Only one .reg and one hive file supported at the same time
-I <registryhivefile> <prefixstring> <input.reg>
Import from .reg file. Where <prefixstring> for example is HKEY_LOCAL_MACHINE\SOFTWARE
Only one .reg and one hive file supported at the same time
-e <registryhive> ...
Interactive edit one or more of registry files
Options:
-L : Log changed filenames to /tmp/changed, also auto-saves
-C : Auto-save (commit) changed hives without asking
-N : No allocate mode, only allow edit of existing values with same size
-E : No expand mode, do not expand hive file (safe mode)
-t : Debug trace of allocated blocks
-v : Some more verbose messages
Modes:
-x <registryhivefile> <prefixstring> <key> <output.reg>
Xport. Where <prefixstring> for example is HKEY_LOCAL_MACHINE\SOFTWARE
<key> is key to dump (recursively), \ or \\ means all keys in hive
Only one .reg and one hive file supported at the same time
Exports everything below the specified <key> of the hive to .reg file.
Since the program does not care about what part of the registry
Windows thinks it is, you must specify the prefix if the .reg file
needs to be used in Windows.
Prefix is like
HKEY_LOCAL_MACHINE\SOFTWARE for software hive file
HKEY_LOCAL_MACHINE\SYSTEM for system hive file
and so on, look in regedit.exe in windows for details.
To dump the whole hive, use \ or \\ or . as the key.
Examples:
reged -x system HKEY_LOCAL_MACHINE\\SYSTEM ControlSet001 output.reg
should export everything below ControlSet001 key from registry hive
file named system into .reg file named output.reg, using
HKEY_LOCAL_MACHINE\SYSTEM in front of every key name in the .reg file.
(in most shells \\ is needed to ignore the meaning \ has to the shell)
reged -x system HKEY_LOCAL_MACHINE\\SYSTEM ControlSet001\\Enum output.reg
same, but start from ControlSet001\Enum
reged -x system HKEY_LOCAL_MACHINE\\SYSTEM \\ output.reg
export everything in the system file
-I <registryhivefile> <prefixstring> <input.reg>
Import from .reg file. Where <prefixstring> for example is HKEY_LOCAL_MACHINE\SOFTWARE
Only one .reg and one hive file supported at the same time
Reverse of -x, this reads from a .reg file and puts it into the hive
file, just like regedit.exe foobar.reg will do in windows.
The <prefixstring> is removed from the start of each key name, if you
specify this wrong, the result may not be what you expected.
KNOWN PROBLEM: This routine is slow, very slow indeed on binary values (has
hex numbers in .reg file). May take over 5 minutes to import a file
the size of a normal XP software-hive .reg export.
Problems / unusual things in the .reg file may cause crash or
unexpected data or some times even an error messsage! :)
KNOWN PROBLEMS: May bork on some wide character (UTF16) .reg files.
(crash or does not do exactly what you would expect)
-e <registryhive> ...
Interactive edit one or more of registry files
Enter the registry editor. It is a small command system. ? for help
there. See other documentation for more on regedits.
If both -I and -e given, editor will be entered after import, but
before save, so you can check things if you need.
Options:
-L : Log changed filenames to /tmp/changed, also auto-saves
-C : Auto-save (commit) changed hives without asking
Usually this command prompts for save, since import may have messed up.
-N : No allocate mode, only allow edit of existing values with same size
-E : No expand mode, do not expand hive file (safe mode)
same as on chntpw
-t : Debug trace of allocated blocks
lot of hive load debug info. very much. allocator debug.
-v : Some more verbose messages
a bit debug messages
** In interactive registry editor:
<keyname> name of a key (not a path), ex: MyKey
<valuename> name of a value (not a path), ex: MyCoolValueName
<keypath> may be a path, ex: foobar\baz\gazonk
as usual with paths, \ starts from the root key, without it is relative to
current key
<valuepath> value, may have path. ex: \foo\bar\MyValue
is value named MyValue in key bar in key foo
cd .. goes back a directory
List subkeys and values in current key
dir
ls
Change current key:
cd <keypath>
cd .. # goes back a key
Show a value, tries to show according to type, else hexdump
type <valuepath>
cat <valuepath>
Hexdump value data (no matter what type it is)
hex <valuepath>
Edit a value:
ed <valupath>
and it will show old value and ask for new
Make a new key (cannot be a path)
nk <keyname>
Delete a key, key must be empty, cannot be a path
dk <keyname>
Delete a value, cannot be a path
dv <valuename>
Delete all values in current key (no questions asked!)
delallv
Delete recursively key and all keys under it (no questions asked)
May take some time if large tree
rdel <keyname>
Make a new value of selected type. It will be empty, use 'ed' afterwards.
nv <type> <valuename>
Where type is a hex number, from this list:
0 : REG_NONE
1 : REG_SZ
2 : REG_EXPAND_SZ
3 : REG_BINARY
4 : REG_DWORD
5 : REG_DWORD_BIG_ENDIAN
6 : REG_LINK
7 : REG_MULTI_SZ
8 : REG_RESOUCE_LIST
9 : REG_FULL_RES_DESC
a : REG_RES_REQ
b : REG_QWORD
it will not prevent you from using other numbers
Show key class data, in hexdump (class data has no special type)
ck <keypath>
Export to .reg file, same as -x option above
ek <filename.reg> <prefix> <keyname>
------------------------------
samusrgrp
is a command line tool to add users to groups or remove users from
groups. Users and groups must be local (cannot be domain / AD).
It can also list the groups with their members in several forms, the
output can be used in scripts of course.
Listing groups will also list domain users that are members of the
group (if any), but it will not be able to look up the name, so it
will be listed as a SID only.
samusrgrp version 0.2 130501, (c) Petter N Hagen
./samusrgrp [-a|-r] -u <user> -g <groupid> <samhive>
Add or remove a (local) user to/from a group
Mode:
-a = add user to group
-r = remove user from group
-l = list groups
-L = list groups and also their members
-s = Print machine SID
For add or remove, you must also specify a bit more info:
Parameters:
<user> can be given as a username or a RID in hex with 0x in front
<group> is the group number, in hex with 0x in front
Example:
-a -u theboss -g 0x220 -> add user named 'theboss' group hex 220 (administrators)
-a -u 0x3ea -g 0x221 -> add user with RID (hex) 3ea group hex 221 (users)
-r -u 0x3ff -g 0x220 -> remove user RID 0x3ff from grp 0x220
Note that usernames with international characters usually fails to be found
so please use RID number instead
If success, there will be no output, and exit code is 0
Also, success if user already in (or not in if -r) the group
Options:
-H : Human readable output, else parsable
-N : No allocate mode, only allow edit of existing values with same size
-E : No expand mode, do not expand hive file (safe mode)
-t : Debug trace of allocated blocks
-v : Some more verbose messages/debug
Multi call binary, if program is named:
samusrtogrp -- Assume -a mode: Add a user into a group
samusrfromgrp -- Assume -r mode: Remove user from a group
Example of group listing:
samusrgrp -L SAM
will list groups with member users, for example:
222:Guests:0:1f4:Administrator:S-1-5-21-596911364-3511430013-269812654-500
222:Guests:1:1f5:Guest:S-1-5-21-596911364-3511430013-269812654-501
223:Power Users:0:1f4:Administrator:S-1-5-21-596911364-3511430013-269812654-500
223:Power Users:1:1f5:Guest:S-1-5-21-596911364-3511430013-269812654-501
223:Power Users:2:3e9:foo:S-1-5-21-596911364-3511430013-269812654-1001
Fields are:
Group ID (hex)
Group name
Member number (starts at 0)
Members RID (hex)
Members name (if available, else SID)
Members SID
So in this example, the Guests group have 2 members: Administrator and
Guest.
At the time of writing this, it WILL NOT LIST EMPTY GROUPS (no
members). I plan to change this, empty groups on one line with -1 in
member number field, and rest of user fields empty.
samusrgrp -l SAM
will list groups (no members)
220:Administrators:2
221:Users:2
222:Guests:1
223:Power Users:0
Fields are:
Group ID (hex)
Group name
Number of members
this listing will show all groups, also empty groups.
-------------------------------------------------
sampasswd
Password reset from command line (scriptable)
Or list users in SAM file in a few different formats.
./sampasswd [-r|-l] [-H] -u <user> <samhive>
Reset password or list users in SAM database
Mode:
-r = reset users password
-l = list users in sam
One mode must be selected
Parameters:
<user> can be given as a username or a RID in hex with 0x in front
Example:
-r -u theboss -> resets password of user named 'theboss' if found
-r -u 0x3ea -> resets password for user with RID 0x3ea (hex)
-r -a -> Reset password of all users in administrators group (0x220)
-r -f -> Reset password of admin user with lowest RID
not counting built-in admin (0x1f4) unless it is the only admin
Note that usernames with international characters usually fails to be found
so please use RID number instead
If success, there will be no output, and exit code is 0
Options:
-H : For list: Human readable listing (default is parsable table)
-H : For reset: Will output confirmation message if success
-N : No allocate mode, only allow edit of existing values with same size
-E : No expand mode, do not expand hive file (safe mode)
-t : Debug trace of allocated blocks
-v : Some more verbose messages/debug
List examples:
sampasswd -l xpreg/SAM
01f4:Administrator:1:210:14
01f5:Guest:0:8215:4
03e8:HelpAssistant:0:8211:14
03e9:foo:1:210:14
Fields are:
User RID (hex)
User name
Is user admin? (1 = yes, 0 = no) (member of group ID 0x220)
Account flags, ACB (hex). See sam.h file.
Password hash length. 14 = normal password. 0 or 4 = probably blank.
The bulk reset (-r -a) will also output parsable result:
sampasswd -H -r -a xpreg/SAM
Reset user :01f4:Administrator
Reset user :03e9:pnh
which of course is :RID:username
Explanation on this:
-r -f -> Reset password of admin user with lowest RID
not counting built-in admin (0x1f4) unless it is the only admin
All windows from NT3.1 up has a system created administrator account
with RID (user ID) 0x1f4 (500 decimal)
Before Windows XP the installer asked for a password for this account,
and then it was used to login first after installation.
(Built-in guest account was also created by the installer, but it has
always been disabled by default)
On Windows XP and newer systems, the installer also creates this
account, but locks it down, it generally cannot be logged in. It is
also not shown on the welcome screen (unless all other users are
deleted or disabled)
The installer instead asks for a user to create during install. That
user is a normal non-hardwired user (RID > 0x3e8, 1000 decimal),
and it is added to the built-in administrators group (group # 0x220).
It is users in the built-in group 0x220 that generally has full
administrator rights to the machine.
(XP installer can create several users, but only first gets group
0x220)
More users can of course be added from the control panel, and they can
be put into the 0x220 group if neccessary. From the "simplified"
control panel dialog this is what happens if user is selected to be able to
have full (or admin) access to the machine. If user is set to "normal"
or something like that, it is not in the 0x220 group.
From the "Users and Groups" part of the administrative tools (not
available on some home versions of windows) the group assignments and
other user info can be changed in more detail of course.
From there, users in a domain (if machine is in domain) can also be
added to the local 0x220 group, the domain user full access to that
local machine even if the user is nothing special in the domain.
Anyway..
On XP and newer, it is therefore not the hardwired 0x1f4 account that is
used for admim. On home machines it is most often the first regular one (since
most people do not change any user stuff after the installer) or it
could be any other user in the list.
So this reset function picks the first it finds over 0x3e8 (1000)
that is also in the 0x220 group. It will most likely work for 98% of
home user machines :) Unless there are no users in the 0x220 group,
then it picks the 0x1f4 hard-wired user (since it may be Windows
2000??????).
This may of course be wrong if someone managed to remove all accounts
above 1000 from the 0x220 group. But then they have "samusrgrp" to add
someone back :)
Explanation on -a -r:
The -r -a option will reset all users in the 0x220 group. Also user
0x1f4, which maybe is bad.. will consider changing this...
+22 -7
View File
@@ -22,15 +22,17 @@ OSSLLIB=$(OSSLPATH)/lib
# This is to link with whatever we have, SSL crypto lib we put in static
LIBS=-L$(OSSLLIB) $(OSSLLIB)/libcrypto.a
#LIBS=-L$(OSSLLIB) $(OSSLLIB)/libcrypto.a
LIBS=-L$(OSSLLIB)
all: chntpw chntpw.static cpnt reged reged.static
chntpw: chntpw.o ntreg.o edlib.o
$(CC) $(CFLAGS) -o chntpw chntpw.o ntreg.o edlib.o $(LIBS)
all: chntpw chntpw.static cpnt reged reged.static samusrgrp samusrgrp.static sampasswd sampasswd.static
chntpw.static: chntpw.o ntreg.o edlib.o
$(CC) -static $(CFLAGS) -o chntpw.static chntpw.o ntreg.o edlib.o $(LIBS)
chntpw: chntpw.o ntreg.o edlib.o libsam.o
$(CC) $(CFLAGS) -o chntpw chntpw.o ntreg.o edlib.o libsam.o $(LIBS)
chntpw.static: chntpw.o ntreg.o edlib.o libsam.o
$(CC) -static $(CFLAGS) -o chntpw.static chntpw.o ntreg.o edlib.o libsam.o $(LIBS)
cpnt: cpnt.o
$(CC) $(CFLAGS) -o cpnt cpnt.o $(LIBS)
@@ -41,6 +43,19 @@ reged: reged.o ntreg.o edlib.o
reged.static: reged.o ntreg.o edlib.o
$(CC) -static $(CFLAGS) -o reged.static reged.o ntreg.o edlib.o
samusrgrp.static: samusrgrp.o ntreg.o libsam.o
$(CC) -static $(CFLAGS) -o samusrgrp.static samusrgrp.o ntreg.o libsam.o
samusrgrp: samusrgrp.o ntreg.o libsam.o
$(CC) $(CFLAGS) -o samusrgrp samusrgrp.o ntreg.o libsam.o
sampasswd: sampasswd.o ntreg.o libsam.o
$(CC) $(CFLAGS) -o sampasswd sampasswd.o ntreg.o libsam.o
sampasswd.static: sampasswd.o ntreg.o libsam.o
$(CC) -static $(CFLAGS) -o sampasswd.static sampasswd.o ntreg.o libsam.o
#ts: ts.o ntreg.o
# $(CC) $(CFLAGS) -nostdlib -o ts ts.o ntreg.o $(LIBS)
@@ -51,5 +66,5 @@ reged.static: reged.o ntreg.o edlib.o
$(CC) -c $(CFLAGS) $<
clean:
rm -f *.o chntpw chntpw.static cpnt reged reged.static *~
rm -f *.o chntpw chntpw.static cpnt reged reged.static samusrgrp samusrgrp.static sampasswd sampasswd.static *~
+50 -92
View File
@@ -1,20 +1,26 @@
The Offline NT Password Editor
The Offline Windows Password Editor
(c) 1997-2011 Petter Nordahl-Hagen
(c) 1997-2014 Petter Nordahl-Hagen
This is free software, licensed under the following:
"ntreg" (the registry library) is licensed under the GNU Lesser Public
License. See LGPL.txt.
"ntreg" (the registry library) and
"libsam" (SAM manipulation library, user, groups etc)
is licensed under the GNU Lesser Public License. See LGPL.txt.
"chntpw" (the password reset / registry editor frontend) is licensed
under the GNU General Public License, see GPL.txt.
"chntpw" (the password reset / registry editor frontend)
"reged" (registry editor, export and import tool)
"sampasswd" (password reset command line program)
"samusrgrp" (user and group command line program)
is licensed under the GNU General Public License, see GPL.txt.
"reged" (registry editor /export tool) is licensed
under the GNU General Public License, see GPL.txt.
See INSTALL.txt for compile/installation instructions.
For manual to the different commands, see MANUAL.txt
Also, all have some help built in, just use the -h option.
See INSTALL.txt for compile instructions.
Where to get more info:
-----------------------
@@ -22,16 +28,17 @@ Where to get more info:
http://pogostick.net/~pnh/ntpasswd/
At that site there's a floppy and a bootable CD that use chntpw to
access the NT/2k/XP/Vista-system it is booted on to edit password etc.
access the NT/2k/XP/Vista/Win7/Win8 system it is booted on to edit password etc.
The instructions below are for the standalone program itself, not the floppy.
What does chntpw do?
--------------------
This little program will enable you to view some information and
change user passwords in a Windows (NT/XP/Vista/win7) etc SAM userdatabase file.
change user passwords, change user/group memberships
in a Windows (NT/XP/Vista/win7/win8) etc SAM userdatabase file.
You do not need to know the old passwords.
However, you need to get at the file some way or another yourself.
However, you need to get at the registry files some way or another yourself.
In addition it contains a simple registry editor with full write support,
and hex-editor which enables you to
fiddle around with bits&bytes in the file as you wish yourself.
@@ -43,6 +50,15 @@ Also have registry import or export
the registry hive (binary) files. Also has an editor, but still
rudimentary text based command line type thing.
And by popular request
Even have programs that can be used in scripts!
-----------------------------------------------
"sampasswd" can be used in scripts to get lists
of users or reset passwords automatically
"samusrgrp" can be used in scripts to list or change
memberships in groups automatically.
Why?
----
@@ -52,101 +68,43 @@ I just _must_ have some stuff out of half a year later..)
On most unix-based boxes you just boot the thingy off some kind
of rescue bootmedia (cd/floppy etc), and simply edit the
password file.
On Windows NT however, as far as I know, there is no way except reinstalling
On Windows however, as far as I know, there is no way except reinstalling
the userdatabase, losing all users except admin.
(ok, some companies let you pay lotsa $$$$$ for some rescue service..)
(ok, from Windows Vista or something you can make a password reset
file, but you have to remember to do that BEFORE you forget your password...)
How?
----
Currently, this thing only runs under linux, but it may just happen
to compile on other platforms, too.
(there are dos-versions available, look for links on my webpage)
So, to set a new adminpassword on your NT installation you either:
So, to set a new adminpassword on your Windows installation you either:
1) Take the harddrive and mount it on a linux-box
2) Use a linux-bootdisk or CD
one is available at: http://pogostick.net/~pnh/ntpasswd/
ie. you do it offline, with the NT system down.
or
2) Boot a "live" linux CD with full GUI (many available: Ubuntu,
Knoppix and more. Search for them)
In both those cases, use the "chntpw.static" program found in the
"static" zip file on my website.
or
3) Use my linux boot CD (or USB) at: http://pogostick.net/~pnh/ntpasswd/
Usage:
------
This is usage of the "chntpw" program binary only.
For info on the bootdisk, see the web site.
Some of the output format has changed a little since the docs were
first written.
For manual to the different commands, see MANUAL.txt
Also, all have some help built in, just use the -h option.
chntpw version 0.99.2 040105, (c) Petter N Hagen
chntpw: change password of a user in a NT SAM file, or invoke registry editor.
chntpw [OPTIONS] <samfile> [systemfile] [securityfile] [otherreghive] [...]
-h This message
-u <user> Username to change, Administrator is default
-l list all users in SAM file
-i Interactive. List users (as -l) then ask for username to change
-e Registry editor. Now with full write support!
-d Enter buffer debugger instead (hex editor),
-t Trace. Show hexdump of structs/segments. (deprecated debug function)
-v Be a little more verbose (for debuging)
-L Write names of changed files to /tmp/changed
-N No allocation mode. Only (old style) same length overwrites possible
Some old tech babble on how the password is stored
--------------------------------------------------
(still mostly valid, but should be moved somewhere else than this file)
Normal usage is:
> chntpw sam system security
- open registry hives 'sam' and 'system' and change administrator account.
Verions dated later from Feb 1999 and later also supports
and will find the admin account, even if the name has been changed,
or the name has been localized (different languageversion of NT
use different admin-names)
The -u option:
Specifies user to change:
> chntpw -u jabbathehutt mysam
- Prompt for password for 'jabbathehutt', if found (otherwise do nothing)
Or you may give RID number in hex:
> chntpw -u 0x1f4 mysam
- Will edit administrator.
Names does not support multibyte (unicode) characters like
some russian and asian locales. Give RID in hex to edit users
with such names. Must start with 0x. Ex: 0x2fa
The -l option:
Will list all users in the sam-file.
The -i option:
Go into the interactive menu system.
The -d option:
This will load the file, and then immediately enter the
buffer debugger.
This is a simple hex-editor with only a few commands,
enter ? at the . prompt to se a short command overview.
'q' exits without saving, 's' exit and saves.
The -e option:
Will enter the registry editor.
You can navigate the registry like a filesystem at the command-line prompt:
See regedit.txt file for more info.
The -t option:
This is a debug function (extended -l) to show how it traces the chain
of structs in the file. This also includes a raw interpretation
of the different registry structures + a hex dump.
The -L option:
Drops the filenames of the changed hives in /tmp/changed
Used by the bootdisk scripts.
The -N option:
Will fall back to old edit mode, disable the block allocations
and only support overwrite-same-size. Used to ensure safety
in testing period.
How does it work:
-----------------
A struct, called the V value of a key in the NT registry
was suddenly somewhat documented through the pwdump utility
+2312
View File
File diff suppressed because it is too large Load Diff
+326 -493
View File
File diff suppressed because it is too large Load Diff
+17 -11
View File
@@ -4,6 +4,8 @@
* Point of this is so that interactive registry editor
* can be accessed from several other programs
*
* 2013-aug: Some minor bugfixes and adjustments
* Thanks to David Collett for catching and fixing bug in REG_MULTI_SZ editing.
* 2010-jun: New function from Aleksander Wojdyga: dpi, decode product ID
* Mostly used on \Microsoft\Windows NT\CurrentVersion\DigitalProductId
* Now as command in registry editor, but may be moved to chnpw menu later.
@@ -23,7 +25,7 @@
*
*****
*
* Copyright (c) 1997-2011 Petter Nordahl-Hagen.
* Copyright (c) 1997-2014 Petter Nordahl-Hagen.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -48,7 +50,7 @@
#include "ntreg.h"
const char edlib_version[] = "edlib version 0.1 110511, (c) Petter N Hagen";
const char edlib_version[] = "edlib version 0.1 140201, (c) Petter N Hagen";
#define ALLOC_DEBUG 0 /* Reg allocation debug hooks */
#define ADDBIN_DEBUG 0 /* Reg expansion debug hooks */
@@ -221,8 +223,8 @@ void cat_vk(struct hive *hdesc, int nkofs, char *path, int dohex)
data = (void *)&(kv->data);
printf("Value <%s> of type %s, data length %d [0x%x]\n", path,
(type < REG_MAX ? val_types[type] : "(unknown)"), len, len);
printf("Value <%s> of type %s (%x), data length %d [0x%x]\n", path,
(type < REG_MAX ? val_types[type] : "(unknown)"), type, len, len);
if (dohex) type = REG_BINARY;
switch (type) {
@@ -277,8 +279,8 @@ void edit_val(struct hive *h, int nkofs, char *path)
}
len = kv->len;
printf("EDIT: <%s> of type %s with length %d [0x%x]\n", path,
(type < REG_MAX ? val_types[type] : "(unknown)"),
printf("EDIT: <%s> of type %s (%x) with length %d [0x%x]\n", path,
(type < REG_MAX ? val_types[type] : "(unknown)"), type,
len, len);
switch(type) {
@@ -364,15 +366,19 @@ void edit_val(struct hive *h, int nkofs, char *path)
if (strcmp("--Q", inbuf)) { /* We didn't bail out */
if (newstring) newstring = realloc(newstring, in+1);
else newstring = malloc(in+1);
if (type == REG_MULTI_SZ) {
in++;
*(newstring+in) = '\0'; /* Must add null termination */
in++;
}
ALLOC(newkv,1,(in<<1)+sizeof(int));
newkv->len = in<<1;
printf("newkv->len: %d\n",newkv->len);
VERBF(h,"newkv->len: %d\n",newkv->len);
cheap_ascii2uni(newstring, (char *)&(newkv->data), in);
d = 1;
FREE(kv);
@@ -456,7 +462,7 @@ void regedit_interactive(struct hive *hive[], int no_hives)
{
struct hive *hdesc;
int cdofs, newofs;
struct nk_key *cdkey;
// struct nk_key *cdkey;
char inbuf[100], *bp, *file, *prefix;
char path[1000];
int l, vkofs, nh, i;
@@ -474,7 +480,7 @@ void regedit_interactive(struct hive *hive[], int no_hives)
printf("Simple registry editor. ? for help.\n");
while (1) {
cdkey = (struct nk_key *)(hdesc->buffer + cdofs);
// cdkey = (struct nk_key *)(hdesc->buffer + cdofs);
*path = 0;
get_abs_path(hdesc,cdofs+4, path, 50);
+1791
View File
File diff suppressed because it is too large Load Diff
+166 -42
View File
@@ -1,6 +1,18 @@
/*
* ntreg.c - Windows (NT and up) Registry Hive access library
* should be able to handle most basic functions:
* iterate, add&delete keys and values, read stuff, change stuff etc
* no rename of keys or values yet..
* also contains some minor utility functions (string handling etc) for now
*
* 2014-jan: openhive() now more compatible with build on non-unix?
* 2013-aug: Enter buil-in buffer debugger only if in trace mode, else return error or abort()
* 2013-may-aug: Fixed critical bug in del_value which could
* thrash the hive when removing value in bottom of key.
* And a pointer not reinitialized when buffer reallocated in some cases, fixed.
* Thanks to Jacky To for reporting those two.
* Some minor adjustments for compiler. A few more utility functions.
* 2012-oct: Added set_val_type. some minor changes.
* 2011-may: Seems like large values >16k or something like that is split
* into several blocks (db), have tried to implement that.
* Vista seems to accept it. Not tested on others yet.
@@ -43,7 +55,7 @@
*****
*
* NTREG - Window registry file reader / writer library
* Copyright (c) 1997-2010 Petter Nordahl-Hagen.
* Copyright (c) 1997-2014 Petter Nordahl-Hagen.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -68,6 +80,7 @@
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include <stdarg.h>
#include "ntreg.h"
@@ -77,7 +90,7 @@
#define ZEROFILL 1 /* Fill blocks with zeroes when allocating and deallocating */
#define ZEROFILLONLOAD 0 /* Fill blocks marked as unused/deallocated with zeroes on load. FOR DEBUG */
const char ntreg_version[] = "ntreg lib routines, v0.95 110511, (c) Petter N Hagen";
const char ntreg_version[] = "ntreg lib routines, v0.95 140201, (c) Petter N Hagen";
const char *val_types[REG_MAX+1] = {
"REG_NONE", "REG_SZ", "REG_EXPAND_SZ", "REG_BINARY", "REG_DWORD", /* 0 - 4 */
@@ -162,14 +175,38 @@ char *str_dup( const char *str )
{
char *str_new;
if (!str)
return 0 ;
if (!str) return(0);
CREATE( str_new, char, strlen(str) + 1 );
strcpy( str_new, str );
return str_new;
}
char *str_cat(char *str, char *add)
{
if (!add) return(str);
str = (char *) realloc(str, strlen(str) + strlen(add) + 3);
strcat (str, add);
return (str);
}
char *str_catf(char *str, const char *format, ... )
{
va_list ap;
char add[640] ;
va_start(ap, format) ;
vsprintf(add, format, ap) ;
va_end(ap) ;
str = (char *) realloc(str, strlen(str) + strlen(add) + 3);
strcat (str, add );
return(str);
}
/* Copy non-terminated string to buffer we allocate and null terminate it
* Uses length only, does not check for nulls
*/
@@ -440,6 +477,44 @@ int debugit(char *buf, int sz)
}
/* Utility function to copy and append two values into a new one
* Will allocate new buffer, but not touch the input ones
*/
struct keyval *reg_valcat(struct keyval *a, struct keyval *b)
{
int newsize = 0;
int asize = 0;
int bsize = 0;
struct keyval *result = NULL;
if (!a && !b) return(NULL);
if (a) asize = a->len;
if (b) bsize = b->len;
newsize = asize + bsize;
// printf("asize = %d, bsize = %d, newsize = %d\n",asize,bsize,newsize);
ALLOC(result, sizeof(struct keyval) + newsize, 1);
if (asize) memcpy(&result->data, &a->data, asize);
if (bsize) memcpy(&result->data + asize / sizeof(int), &b->data, bsize);
result->len = newsize;
//printf("reg_valcat done\n");
return(result);
}
/* ========================================================================= */
/* The following routines are mostly for debugging, I used it
@@ -690,7 +765,7 @@ int parse_block(struct hive *hdesc, int vofs,int verbose)
#endif
if (seglen == 0) {
printf("parse_block: FATAL! Zero data block size! (not registry or corrupt file?)\n");
debugit(hdesc->buffer,hdesc->size);
if (verbose) debugit(hdesc->buffer,hdesc->size);
return(0);
}
@@ -819,8 +894,8 @@ int find_free_blk(struct hive *hdesc, int pofs, int size)
printf("find_free_blk: at exact end of hbin, do not care..\n");
return(0);
}
abort();
debugit(hdesc->buffer,hdesc->size);
if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
else abort();
return(0);
}
@@ -1108,7 +1183,7 @@ int free_block(struct hive *hdesc, int blk)
printf("free_block: trying to free already free block!\n");
#ifdef DOCORE
printf("blk = %x\n",blk);
debugit(hdesc->buffer,hdesc->size);
if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
abort();
#endif
return(0);
@@ -1148,7 +1223,7 @@ int free_block(struct hive *hdesc, int blk)
printf("free_block: ran off end of page!?!? Error in chains?\n");
#ifdef DOCORE
printf("vofs = %x, pofs = %x, blk = %x\n",vofs,pofs,blk);
debugit(hdesc->buffer,hdesc->size);
if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
abort();
#endif
return(0);
@@ -1701,9 +1776,9 @@ void nk_ls(struct hive *hdesc, char *path, int vofs, int type)
VERBF(hdesc,"ls of node at offset 0x%0x\n",nkofs);
if (key->id != 0x6b6e) {
printf("Error: Not a 'nk' node!\n");
printf("Error: Not a 'nk' node at offset %x!\n",nkofs);
debugit(hdesc->buffer,hdesc->size);
if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
}
@@ -1721,13 +1796,13 @@ void nk_ls(struct hive *hdesc, char *path, int vofs, int type)
}
count = 0;
if (key->no_values) {
printf(" size type value name [value if type DWORD]\n");
printf(" size type value name [value if type DWORD]\n");
while ((ex_next_v(hdesc, nkofs, &count, &vex) > 0)) {
if (hdesc->state & HMODE_VERBOSE) printf("[%6x] %6d %-16s <%s>", vex.vkoffs - 4, vex.size,
if (hdesc->state & HMODE_VERBOSE) printf("[%6x] %6d %x %-16s <%s>", vex.vkoffs - 4, vex.size, vex.type,
(vex.type < REG_MAX ? val_types[vex.type] : "(unknown)"), vex.name);
else
printf("%6d %-16s <%s>", vex.size,
(vex.type < REG_MAX ? val_types[vex.type] : "(unknown)"), vex.name);
printf("%6d %x %-16s <%s>", vex.size, vex.type,
(vex.type < REG_MAX ? val_types[vex.type] : "(unknown)"), vex.name);
if (vex.type == REG_DWORD) printf(" %*d [0x%x]",25-(int)strlen(vex.name),vex.val , vex.val);
printf("\n");
@@ -1752,6 +1827,27 @@ int get_val_type(struct hive *hdesc, int vofs, char *path, int exact)
return(vkkey->val_type);
}
/* Set/change the type of a value. Strangely we need this in some situation in SAM
* and delete + add value is a bit overkill */
int set_val_type(struct hive *hdesc, int vofs, char *path, int exact, int type)
{
struct vk_key *vkkey;
int vkofs;
vkofs = trav_path(hdesc, vofs,path,exact | TPF_VK);
if (!vkofs) {
return -1;
}
vkofs +=4;
vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
vkkey->val_type = type;
return(vkkey->val_type);
}
/* Get len of a value, given current key + path */
int get_val_len(struct hive *hdesc, int vofs, char *path, int exact)
@@ -1930,7 +2026,7 @@ int fill_block(struct hive *hdesc, int ofs, void *data, int size)
/* if (blksize < size || ( (ofs & 0xfffff000) != ((ofs+size) & 0xfffff000) )) { */
if (blksize < size) {
printf("fill_block: ERROR: block to small for data: ofs = %x, size = %x, blksize = %x\n",ofs,size,blksize);
debugit(hdesc->buffer,hdesc->size);
if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
abort();
}
@@ -2282,7 +2378,7 @@ int del_value(struct hive *hdesc, int nkofs, char *name, int exact)
/* Now copy over, omitting deleted entry */
newlistkey = (int32_t *)(hdesc->buffer + newlistofs + 4);
for (n = 0, o = 0; o < nk->no_values+1; o++, n++) {
for (n = 0, o = 0; n < nk->no_values; o++, n++) {
if (o == slot) o++;
newlistkey[n] = tmplist[o];
}
@@ -2609,7 +2705,7 @@ int del_key(struct hive *hdesc, int nkofs, char *name)
if (key->id != 0x6b6e) {
printf("add_key: current ptr not nk\n");
printf("del_key: current ptr not nk\n");
return(1);
}
@@ -2747,6 +2843,13 @@ int del_key(struct hive *hdesc, int nkofs, char *name)
/* Allocate space for our new lf list and copy it into reg */
if ( no_keys && (newlf || newli) ) {
newlfofs = alloc_block(hdesc, nkofs, 8 + (newlf ? 8 : 4) * no_keys);
/* alloc_block may invalidate pointers if hive expanded. Recalculate this one.
* Thanks to Jacky To for reporting it here, and suggesting a fix
* (better would of course be for me to redesign stuff :)
*/
if (delnkofs) delnk = (struct nk_key *)(delnkofs + hdesc->buffer + 0x1004);
#ifdef DKDEBUG
printf("del_key: alloc_block for index returns: %x\n",newlfofs);
#endif
@@ -2775,7 +2878,7 @@ int del_key(struct hive *hdesc, int nkofs, char *name)
if (newlfofs < 0xfff) {
printf("del_key: ERROR: newlfofs = %x\n",newlfofs);
#if DOCORE
debugit(hdesc->buffer,hdesc->size);
if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
abort();
#endif
}
@@ -2878,10 +2981,10 @@ void rdel_keys(struct hive *hdesc, char *path, int vofs)
*/
if (key->id != 0x6b6e) {
printf("Error: Not a 'nk' node!\n");
printf("rdel_keys: ERROR: Not a 'nk' node!\n");
debugit(hdesc->buffer,hdesc->size);
if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
return;
}
#if 0
@@ -3770,7 +3873,7 @@ void import_reg(struct hive *hdesc, char *filename, char *prefix)
char *value = NULL;
int wide = 0;
int c,wl;
void *valbuf;
// void *valbuf;
char *valname = NULL;
char *plainname = NULL;
char *valstr, *key, *walstr = NULL;
@@ -3951,7 +4054,7 @@ void import_reg(struct hive *hdesc, char *filename, char *prefix)
}
value = dequote(value);
valstr = str_dup(value);
valbuf = NULL;
// valbuf = NULL;
//printf("import_reg: val name = %s\n",valname);
//printf("import_reg: val str = %s\n",valstr);
@@ -4018,7 +4121,8 @@ int32_t calc_regfsum(struct hive *hdesc)
/* Write the hive back to disk (only if dirty & not readonly */
/* Write the hive back to disk (only if dirty & not readonly) */
int writeHive(struct hive *hdesc)
{
int len;
@@ -4065,6 +4169,7 @@ struct hive *openHive(char *filename, int mode)
uint32_t pofs;
int32_t checksum;
char *c;
int rt;
struct hbin_page *p;
struct regf_header *hdr;
struct nk_key *nk;
@@ -4072,6 +4177,9 @@ struct hive *openHive(char *filename, int mode)
int verbose = (mode & HMODE_VERBOSE);
int trace = (mode & HMODE_TRACE) ? 1 : 0;
int info = (mode & HMODE_INFO);
if (!filename || !*filename) return(NULL);
CREATE(hdesc,struct hive,1);
@@ -4085,6 +4193,12 @@ struct hive *openHive(char *filename, int mode)
} else {
fmode = O_RDWR;
}
/* Some non-unix platforms may need this. Thanks to Dan Schmidt */
#ifdef O_BINARY
fmode |= O_BINARY;
#endif
hdesc->filedesc = open(hdesc->filename,fmode);
if (hdesc->filedesc < 0) {
fprintf(stderr,"openHive(%s) failed: %s, trying read-only\n",hdesc->filename,strerror(errno));
@@ -4106,14 +4220,23 @@ struct hive *openHive(char *filename, int mode)
hdesc->size = sbuf.st_size;
hdesc->state = mode | HMODE_OPEN;
/* fprintf(stderr,"hiveOpen(%s) successful\n",hdesc->filename); */
/* Read the whole file */
ALLOC(hdesc->buffer,1,hdesc->size);
r = read(hdesc->filedesc,hdesc->buffer,hdesc->size);
if (r < hdesc->size) {
rt = 0;
do { /* On some platforms read may not block, and read in chunks. handle that */
r = read(hdesc->filedesc, hdesc->buffer + rt, hdesc->size - rt);
rt += r;
} while ( !errno && (rt < hdesc->size) );
if (errno) {
perror("openHive(): read error: ");
closeHive(hdesc);
return(NULL);
}
if (rt < hdesc->size) {
fprintf(stderr,"Could not read file, got %d bytes while expecting %d\n",
r, hdesc->size);
closeHive(hdesc);
@@ -4141,13 +4264,13 @@ struct hive *openHive(char *filename, int mode)
fprintf(stderr,"openHive(%s): WARNING: REGF header checksum mismatch! calc: 0x%08x != file: 0x%08x\n",filename,checksum,hdr->checksum);
}
printf("Hive <%s> name (from header): <",filename);
for (c = hdr->name; *c && (c < hdr->name + 64); c += 2) putchar(*c);
hdesc->rootofs = hdr->ofs_rootkey + 0x1000;
printf(">\nROOT KEY at offset: 0x%06x * ",hdesc->rootofs);
if (info) {
printf("Hive <%s> name (from header): <",filename);
for (c = hdr->name; *c && (c < hdr->name + 64); c += 2) putchar(*c);
printf(">\nROOT KEY at offset: 0x%06x * ",hdesc->rootofs);
}
/* Cache the roots subkey index type (li,lf,lh) so we can use the correct
* one when creating the first subkey in a key */
@@ -4157,7 +4280,7 @@ struct hive *openHive(char *filename, int mode)
rikey = (struct ri_key *)(hdesc->buffer + nk->ofs_lf + 0x1004);
hdesc->nkindextype = rikey->id;
if (hdesc->nkindextype == 0x6972) { /* Gee, big root, must check indirectly */
printf("openHive: DEBUG: BIG ROOT!\n");
fprintf(stderr,"openHive: DEBUG: BIG ROOT!\n");
rikey = (struct ri_key *)(hdesc->buffer + rikey->hash[0].ofs_li + 0x1004);
hdesc->nkindextype = rikey->id;
}
@@ -4167,7 +4290,7 @@ struct hive *openHive(char *filename, int mode)
hdesc->nkindextype = 0x666c;
}
printf("Subkey indexing type is: %04x <%c%c>\n",
if (info) printf("Subkey indexing type is: %04x <%c%c>\n",
hdesc->nkindextype,
hdesc->nkindextype & 0xff,
hdesc->nkindextype >> 8);
@@ -4183,7 +4306,7 @@ struct hive *openHive(char *filename, int mode)
#endif
p = (struct hbin_page *)(hdesc->buffer + pofs);
if (p->id != 0x6E696268) {
printf("Page at 0x%x is not 'hbin', assuming file contains garbage at end\n",pofs);
if (info) printf("Page at 0x%x is not 'hbin', assuming file contains garbage at end\n",pofs);
break;
}
hdesc->pages++;
@@ -4218,10 +4341,11 @@ struct hive *openHive(char *filename, int mode)
printf("hdr->unknown4 (version?) : 0x%x\n",hdr->unknown4);
}
printf("File size %d [%x] bytes, containing %d pages (+ 1 headerpage)\n",hdesc->size,hdesc->size, hdesc->pages);
printf("Used for data: %d/%d blocks/bytes, unused: %d/%d blocks/bytes.\n\n",
hdesc->useblk,hdesc->usetot,hdesc->unuseblk,hdesc->unusetot);
if (info || verbose) {
printf("File size %d [%x] bytes, containing %d pages (+ 1 headerpage)\n",hdesc->size,hdesc->size, hdesc->pages);
printf("Used for data: %d/%d blocks/bytes, unused: %d/%d blocks/bytes.\n\n",
hdesc->useblk,hdesc->usetot,hdesc->unuseblk,hdesc->unusetot);
}
/* So, let's guess what kind of hive this is, based on keys in its root */
+8 -1
View File
@@ -6,7 +6,7 @@
*****
*
* NTREG - Window registry file reader / writer library
* Copyright (c) 1997-2011 Petter Nordahl-Hagen.
* Copyright (c) 1997-2013 Petter Nordahl-Hagen.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -318,6 +318,7 @@ struct keyvala {
#define HMODE_DIDEXPAND 0x20 /* File has been expanded */
#define HMODE_VERBOSE 0x1000
#define HMODE_TRACE 0x2000
#define HMODE_INFO 0x4000 /* Show some info on open and close */
/* Suggested type of hive loaded, guessed by library, but not used by it */
#define HTYPE_UNKNOWN 0
@@ -386,6 +387,8 @@ struct hive {
/******* Function prototypes **********/
char *str_dup( const char *str );
char *str_cat(char *str, char *add);
char *str_catf(char *str, const char *format, ... );
int fmyinput(char *prmpt, char *ibuf, int maxlen);
void hexprnt(char *s, unsigned char *bytes, int len);
void hexdump(char *hbuf, int start, int stop, int ascii);
@@ -397,12 +400,16 @@ void skipspace(char **c);
int gethex(char **c);
int gethexorstr(char **c, char *wb);
int debugit(char *buf, int sz);
struct keyval *reg_valcat(struct keyval *a, struct keyval *b);
int parse_block(struct hive *hdesc, int vofs,int verbose);
int ex_next_n(struct hive *hdesc, int nkofs, int *count, int *countri, struct ex_data *sptr);
int ex_next_v(struct hive *hdesc, int nkofs, int *count, struct vex_data *sptr);
int get_abs_path(struct hive *hdesc, int nkofs, char *path, int maxlen);
int trav_path(struct hive *hdesc, int vofs, char *path, int type);
int get_val_type(struct hive *hdesc, int vofs, char *path, int exact);
int set_val_type(struct hive *hdesc, int vofs, char *path, int exact, int type);
int get_val_len(struct hive *hdesc, int vofs, char *path, int exact);
void *get_val_data(struct hive *hdesc, int vofs, char *path, int val_type, int exact);
struct keyval *get_val2buf(struct hive *hdesc, struct keyval *kv,
+2 -2
View File
@@ -13,7 +13,7 @@
*
*****
*
* Copyright (c) 1997-2010 Petter Nordahl-Hagen.
* Copyright (c) 1997-2014 Petter Nordahl-Hagen.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -38,7 +38,7 @@
#include "ntreg.h"
const char reged_version[] = "reged version 0.1 110511, (c) Petter N Hagen";
const char reged_version[] = "reged version 0.1 140201, (c) Petter N Hagen";
/* Global verbosity flag */
+77 -3
View File
@@ -4,7 +4,7 @@
*****
*
* NTREG - Window registry file reader / writer library
* Copyright (c) 1997-2011 Petter Nordahl-Hagen.
* Copyright (c) 1997-2012 Petter Nordahl-Hagen.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -25,6 +25,23 @@
#include <sys/types.h>
#include <inttypes.h>
/* Account V, contains machine SID and a lot of unknonw stuff */
#define SID_BIN_LEN 24 /* Lenght of binary machine SID, incl header */
#define SID_STR_MAX 50 /* Max string lenght of S-1-5-xxx-xxx-xxx-xxx type SID */
#define ACCOUNTDB_V_PATH "\\SAM\\Domains\\Account\\V"
struct accountdb_V {
char unknown1[0x38]; /* No idea what it does */
uint32_t sid_ofs; /* 38 - Offset to machine SID */
uint32_t sid_len; /* 3c - Lenght of sid (should be 0x14?) */
// char unknown2[0x4]; /* 40 */
/* Data start at 0x40 ?? */
char data[4];
};
/* This contains some policy settings for the account database */
#define ACCOUNTDB_F_PATH "\\SAM\\Domains\\Account\\F"
@@ -89,6 +106,8 @@ struct user_F {
/* Seems not to be used on failed console logins at least */
#define ACB_AUTOLOCK 0x0400 /* Account auto locked */
/* Account Bits Fields strings (defined in libsam.c)
char *acb_fields[16] = {
"Disabled" ,
"Homedir req." ,
@@ -108,6 +127,9 @@ char *acb_fields[16] = {
"(unknown 0x80)" ,
};
*/
/* Users V data struct */
/* First 0xCC bytes is pointer & len table, rest is data which
* the table points to
@@ -208,8 +230,9 @@ struct group_C {
int fullname_len; /* 0x20 */
int unknown3_1; /* 0x24 - always zero? */
int unknown3_2; /* 0x28 - always zero? */
int unknown3_3; /* 0x2c - always zero? */
int members_ofs; /* 0x28 - offset to member list, which is SIDs */
int members_len; /* 0x2c - member list size */
int grp_members; /* 0x30 - number of group members */
@@ -219,4 +242,55 @@ struct group_C {
};
/* Variable length binary structure that most SIDs are stored in
*/
struct sid_binary {
uint8_t revision; /* 0x0 - Don't know. Always 1? Revision level? number of SIDs following? */
uint8_t sections; /* 0x1 - Number of parts, 4 bytes each (unsigne integer) */
uint8_t unknown2; /* 0x2 - Unknown, seems to be null padding */
uint8_t unknown3; /* 0x3 - Unknown, seems to be null padding */
uint8_t unknown4; /* 0x4 - Unknown */
uint8_t unknown5; /* 0x5 - Unknown */
uint8_t unknown6; /* 0x6 - Unknown */
uint8_t authority; /* 0x7 - Unknown, often 5 (could this be 8bit authority number?) */
uint32_t array[8]; /* As many as sections value says it is */
};
/* Array of SIDs */
struct sid_array {
int len;
struct sid_binary *sidptr;
};
/* libsam.c functions */
int sam_get_lockoutinfo(struct hive *hdesc, int show);
short sam_handle_accountbits(struct hive *hdesc, int rid, int mode);
int sam_get_machine_sid(struct hive *hdesc, char *sidbuf);
char *sam_sid_to_string(struct sid_binary *sidbuf);
struct sid_array *sam_make_sid_array(struct sid_binary *sidbuf, int size);
void sam_free_sid_array(struct sid_array *array);
int sam_sid_cmp(struct sid_binary *s1, struct sid_binary *s2);
int sam_get_grp_members_sid(struct hive *hdesc, int grp, struct sid_array **sarray);
int sam_put_grp_members_sid(struct hive *hdesc, int grp, struct sid_array *sarray);
struct keyval *sam_get_user_grpids(struct hive *hdesc, int rid);
int sam_put_user_grpids(struct hive *hdesc, int rid, struct keyval *val);
int sam_add_user_to_grp(struct hive *hdesc, int rid, int grp);
int sam_remove_user_from_grp(struct hive *hdesc, int rid, int grp);
char *sam_get_username(struct hive *hdesc, int rid);
char *sam_get_groupname(struct hive *hdesc, int grpid);
int sam_list_users(struct hive *hdesc, int readable);
int sam_list_user_groups(struct hive *hdesc, int rid, int check);
int sam_reset_pw(struct hive *hdesc, int rid);
void sam_list_groups(struct hive *hdesc, int listmembers, int human);
int sam_reset_all_pw(struct hive *hdesc, int list);
#endif
+255
View File
@@ -0,0 +1,255 @@
/*
* sampasswd.c - SAM database, add or remove user in a group
*
* Command line utility, non-interactive to reset user password and/or
* account bits for a user in the SAM database
*
* Changes:
* 2013 - aug: cleaned up a bit for release, some debug still there
* 2013 - apr-may: reset one, all and first features
* 2012 - oct: First version, some code from earlier chntpw.c. Not released.
*
*****
*
* Copyright (c) 1997-2014 Petter Nordahl-Hagen.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* See file GPL.txt for the full license.
*
*****
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "ntreg.h"
#include "sam.h"
const char sampasswd_version[] = "sampasswd version 0.2 140201, (c) Petter N Hagen";
/* Global verbosity flag */
int gverbose = 0;
/* Array of loaded hives */
#define MAX_HIVES 10
struct hive *hive[MAX_HIVES+1];
int no_hives = 0;
int H_SAM = -1;
int do_reset(char *user, int inrid, int verb)
{
int rid = 0;
int ret;
char *resolvedname = NULL;
char s[200];
if ((H_SAM < 0) || (!user && !inrid) ) return(1);
if (inrid) {
rid = inrid;
} else {
if (*user == '0' && *(user+1) == 'x') sscanf(user,"%i",&rid);
}
if (!rid) { /* Look up username */
/* Extract the unnamed value out of the username-key, value is RID */
snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\Names\\%s\\@",user);
rid = get_dword(hive[H_SAM],0,s, TPF_VK_EXACT|TPF_VK_SHORT);
if (rid == -1) {
printf("ERROR: User <%s> not found\n",user);
return(1);
}
}
/* At this point we have a RID, so get the real username it maps to just to show it */
resolvedname = sam_get_username(hive[H_SAM], rid);
if (!resolvedname) return(1); /* RID lookup failed, no such user */
if (gverbose) printf("do_reset: Username: %s, RID = %d (0x%0x)\n",resolvedname,rid,rid);
ret = sam_reset_pw(hive[H_SAM], rid);
if (!ret && verb) printf("Password reset for user %s, RID = %d [0x%0x]\n",resolvedname,rid,rid);
FREE(resolvedname);
return(ret);
}
void usage(void)
{
printf(" [-r|-l] [-H] -u <user> <samhive>\n"
"Reset password or list users in SAM database\n"
"Mode:\n"
" -r = reset users password\n"
" -l = list users in sam\n"
"Parameters:\n"
" <user> can be given as a username or a RID in hex with 0x in front\n"
" Example:\n"
" -r -u theboss -> resets password of user named 'theboss' if found\n"
" -r -u 0x3ea -> resets password for user with RID 0x3ea (hex)\n"
" -r -a -> Reset password of all users in administrators group (0x220)\n"
" -r -f -> Reset password of admin user with lowest RID\n"
" not counting built-in admin (0x1f4) unless it is the only admin\n"
" Usernames with international characters usually fails to be found,\n"
" please use RID number instead\n"
" If success, there will be no output, and exit code is 0\n"
"Options:\n"
" -H : For list: Human readable listing (default is parsable table)\n"
" -H : For reset: Will output confirmation message if success\n"
" -N : No allocate mode, only allow edit of existing values with same size\n"
" -E : No expand mode, do not expand hive file (safe mode)\n"
" -t : Debug trace of allocated blocks\n"
" -v : Some more verbose messages/debug\n"
);
}
int main(int argc, char **argv)
{
extern int optind;
extern char* optarg;
int what = 0;
int reset = 0;
int list = 0;
int mode = 0;
int human = 0;
int adm = 0;
int all = 0;
int first = 0;
int ret, wret, il;
char *hivename;
char c;
char *usr = NULL;
char *options = "rlHu:vNEthaf";
while((c=getopt(argc,argv,options)) > 0) {
switch(c) {
case 'r': reset = 1; break;
case 'l': list = 2; break;
case 'u': usr = optarg; break;
case 'a': all = 1; break;
case 'f': first = 1; break;
case 'H': human = 1; break;
case 'v': mode |= HMODE_VERBOSE; gverbose = 1; break;
case 'N': mode |= HMODE_NOALLOC; break;
case 'E': mode |= HMODE_NOEXPAND; break;
case 't': mode |= HMODE_TRACE; break;
case 'h': printf("%s\n%s ",sampasswd_version,argv[0]); usage(); exit(0); break;
default: printf("%s\n%s ",sampasswd_version,argv[0]); usage(); exit(1); break;
}
}
if (!reset && !list && !what) {
fprintf(stderr,"%s: ERROR: Mode -r or -l must be specified. -h for help\n",argv[0]);
exit(1);
}
#if 0 /* Should both be allowed at same time?? */
if (list && reset) {
fprintf(stderr,"%s: ERROR: Mode -r and -l impossible at the same time. -h for help\n",argv[0]);
exit(1);
}
#endif
if (reset && !all && !first && (!usr || !*usr)) {
fprintf(stderr,"%s: ERROR: Need a user for reset, -u must be specified.\n",argv[0]);
exit(1);
}
/* Load hives. Only first SAM hive will be used however */
hivename = argv[optind+no_hives];
if (!hivename || !*hivename) {
fprintf(stderr,"%s: ERROR: You must specify a SAM registry hive filename.\n",argv[0]);
exit(1);
}
do {
if (!(hive[no_hives] = openHive(hivename,
HMODE_RW|mode))) {
fprintf(stderr,"%s: ERROR: Unable to open/read registry hive, cannot continue\n",argv[0]);
exit(1);
}
switch(hive[no_hives]->type) {
case HTYPE_SAM: H_SAM = no_hives; break;
// case HTYPE_SOFTWARE: H_SOF = no_hives; break;
// case HTYPE_SYSTEM: H_SYS = no_hives; break;
// case HTYPE_SECURITY: H_SEC = no_hives; break;
}
no_hives++;
hivename = argv[optind+no_hives];
} while (hivename && *hivename && no_hives < MAX_HIVES);
if (H_SAM == -1) {
fprintf(stderr,"%s: WARNING: Hive file does not look like SAM, but continuing anyway in case detection was wrong\n"
"%s: WARNING: If it really is not a SAM file you will get strange errors or bad results\n",argv[0],argv[0]);
H_SAM = 0;
}
/* Do logic */
if (list) {
adm = sam_list_users(hive[H_SAM], human);
if (gverbose) printf(" sam_list_users found admin to be 0x%x\n",adm);
}
if (reset) {
if (all) {
ret = sam_reset_all_pw(hive[H_SAM], human);
if (ret) {
fprintf(stderr,"%s: ERROR: Failed reset password of ALL users\n",argv[0]);
}
} else if (first) {
adm = sam_list_users(hive[H_SAM], 2);
if (!adm) {
fprintf(stderr,"%s: ERROR: Unable to reset, no admin users found\n",argv[0]);
} else {
// printf("Resetting password of user with RID %x\n",adm);
ret = do_reset(usr, adm, human);
}
} else {
ret = do_reset(usr, 0, human);
if (ret) {
fprintf(stderr,"%s: ERROR: Failed reset password for %s\n",argv[0],usr);
}
}
}
/* write registry hive (if needed) */
wret = 0;
for (il = 0; il < no_hives; il++) {
// wret |= writeHive(hive[il]);
if (hive[il]->state & HMODE_DIDEXPAND)
fprintf(stderr," WARNING: Registry file %s was expanded! Experimental! Use at own risk!\n",hive[il]->filename);
while (no_hives > 0)
closeHive(hive[--no_hives]);
}
return(ret | wret);
}
+292
View File
@@ -0,0 +1,292 @@
/*
* samusrgrp.c - SAM database, add or remove user in a group
*
* Command line utility, non-inreractive to add or remove a user to/from
* a local group in the SAM database, list groups with memberships etc
*
* When run as:
* samusrtogrp - add user to group
* samusrfromgrp - remove user from a group
* or as any other name, option of what to do must be specified
*
* Changes:
* 2013 - aug: cleaned up for release, still some debug & strangeness left
* 2013 - apr-may: add, remove, list working (more or less)
* 2012 - oct: First version, never released
*
*****
*
* Copyright (c) 1997-2014 Petter Nordahl-Hagen.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* See file GPL.txt for the full license.
*
*****
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "ntreg.h"
#include "sam.h"
const char samusrgrp_version[] = "samusrgrp version 0.2 140201, (c) Petter N Hagen";
/* Global verbosity flag */
int gverbose = 0;
/* Array of loaded hives */
#define MAX_HIVES 10
struct hive *hive[MAX_HIVES+1];
int no_hives = 0;
int H_SAM = -1;
/* Print machine SID. Put into here since no better place for now */
void cmd_machinesid(void)
{
struct sid_binary sid;
char *sidstr;
if (sam_get_machine_sid(hive[H_SAM], (char *)&sid)) {
sidstr = sam_sid_to_string(&sid);
puts(sidstr);
FREE(sidstr);
}
}
/* Get and parse parameters for group changes */
int cmd_usrgrp(char *user, char *grp, int what, int human)
{
int numgrp;
int rid = 0;
char *resolveduser = NULL;
char *resolvedgroup = NULL;
char s[200];
numgrp = strtol(grp, NULL, 0);
if ((H_SAM < 0) || (!user)) return(1);
if (*user == '0' && *(user+1) == 'x') sscanf(user,"%i",&rid);
if (!rid) { /* Look up username */
/* Extract the unnamed value out of the username-key, value is RID */
snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\Names\\%s\\@",user);
rid = get_dword(hive[H_SAM],0,s, TPF_VK_EXACT|TPF_VK_SHORT);
if (rid == -1) {
printf("User <%s> not found\n",user);
return(1);
}
}
/* At this point we have a RID, so get the real username it maps to just to show it */
resolveduser = sam_get_username(hive[H_SAM], rid);
if (!resolveduser) return(1); /* Fails if RID does not exists */
resolvedgroup = sam_get_groupname(hive[H_SAM], numgrp);
if (!resolvedgroup) return(1); /* Fails if GID does not exists */
if (human) printf("%s user <%s> with RID = %d (0x%0x) %s group <%s> with GID = %d (0x%x)\n",
(what == 1 ? "Add" : "Remove"),
resolveduser,
rid, rid,
(what == 1 ? "to" : "from"),
resolvedgroup,
numgrp, numgrp );
FREE(resolveduser);
FREE(resolvedgroup);
switch (what) {
case 1: return(!sam_add_user_to_grp(hive[H_SAM] ,rid, numgrp)); break;
case 2: return(!sam_remove_user_from_grp(hive[H_SAM] ,rid, numgrp)); break;
}
return(0);
}
void usage(void)
{
printf(" [-a|-r] -u <user> -g <groupid> <samhive>\n"
"Add or remove a (local) user to/from a group\n"
"Mode:"
" -a = add user to group\n"
" -r = remove user from group\n"
" -l = list groups\n"
" -L = list groups and also their members\n"
" -s = Print machine SID\n"
"Parameters:\n"
" <user> can be given as a username or a RID in hex with 0x in front\n"
" <group> is the group number, in hex with 0x in front\n"
" Example:\n"
" -a -u theboss -g 0x220 -> add user named 'theboss' group hex 220 (administrators)\n"
" -a -u 0x3ea -g 0x221 -> add user with RID (hex) 3ea group hex 221 (users)\n"
" -r -u 0x3ff -g 0x220 -> remove user RID 0x3ff from grp 0x220\n"
" Usernames with international characters usually fails to be found,\n"
" please use RID number instead\n"
" If success, there will be no output, and exit code is 0\n"
" Also, success if user already in (or not in if -r) the group\n"
"Options:\n"
" -H : Human readable output, else parsable\n"
" -N : No allocate mode, only allow edit of existing values with same size\n"
" -E : No expand mode, do not expand hive file (safe mode)\n"
" -t : Debug trace of allocated blocks\n"
" -v : Some more verbose messages/debug\n"
"Multi call binary, if program is named:\n"
" samusrtogrp -- Assume -a mode: Add a user into a group\n"
" samusrfromgrp -- Assume -r mode: Remove user from a group\n"
);
}
int main(int argc, char **argv)
{
extern int optind;
extern char* optarg;
int what = 0;
int add = 0;
int rem = 0;
int mode = 0;
int m = 0;
int list = 0;
int human = 0;
int ret, wret, il;
char *hivename;
char c;
char *usr = NULL;
char *grp = NULL;
char *options = "aru:g:vNEthlLHs";
if (!strcmp(argv[0],"samusrtogrp")) what = 1;
if (!strcmp(argv[0],"samusrfromgrp")) what = 2;
if (!strcmp(argv[0],"samgrplist")) what = 3;
while((c=getopt(argc,argv,options)) > 0) {
switch(c) {
case 'a': add = 1; m++; break; /* Add user */
case 'r': rem = 2; m++; break; /* Remove user */
case 'l': list = 1; m++; break; /* List groups */
case 'L': list = 2; m++; break; /* List groups + members */
case 's': list = 3; m++; break; /* Print machine sid */
case 'u': usr = optarg; break;
case 'g': grp = optarg; break;
case 'H': human = 1; break;
case 'v': mode |= HMODE_VERBOSE; gverbose = 1; break;
case 'N': mode |= HMODE_NOALLOC; break;
case 'E': mode |= HMODE_NOEXPAND; break;
case 't': mode |= HMODE_TRACE; break;
case 'h': printf("%s\n%s ",samusrgrp_version,argv[0]); usage(); exit(0); break;
default: printf("%s\n%s ",samusrgrp_version,argv[0]); usage(); exit(1); break;
}
}
if (m == 0) {
fprintf(stderr,"%s: ERROR: One of mode -a -r -l -L must be specified\n",argv[0]);
printf("%s\n%s ",samusrgrp_version,argv[0]);
usage();
exit(1);
}
if (m > 1) {
fprintf(stderr,"%s: ERROR: Please select only one of modes -a -r -l -L\n",argv[0]);
exit(1);
}
if (!list && (!usr || !grp || !*usr || !*grp) ) {
fprintf(stderr,"%s: ERROR: Both -u and -g must be specified.\n",argv[0]);
exit(1);
}
/* Implicit mode parameter overrides mode based on binary name */
if (add) what = 1;
if (rem) what = 2;
if (list) what = 3;
// printf("add = %d, rem = %d, list = %d, what = %d\n",add,rem,list,what);
/* Load hives. Only first SAM hive will be used however */
hivename = argv[optind+no_hives];
if (!hivename || !*hivename) {
fprintf(stderr,"%s: ERROR: You must specify a SAM registry hive filename.\n",argv[0]);
exit(1);
}
do {
if (!(hive[no_hives] = openHive(hivename,
HMODE_RW|mode))) {
fprintf(stderr,"%s: ERROR: Unable to open/read registry hive, cannot continue\n",argv[0]);
exit(1);
}
switch(hive[no_hives]->type) {
case HTYPE_SAM: H_SAM = no_hives; break;
// case HTYPE_SOFTWARE: H_SOF = no_hives; break;
// case HTYPE_SYSTEM: H_SYS = no_hives; break;
// case HTYPE_SECURITY: H_SEC = no_hives; break;
}
no_hives++;
hivename = argv[optind+no_hives];
} while (hivename && *hivename && no_hives < MAX_HIVES);
if (H_SAM == -1) {
fprintf(stderr,"%s: WARNING: Registry hive does not look like SAM!\n"
"%s: WARNING: Continuing anyway, may lead to strange messages/failures!\n",argv[0],argv[0]);
H_SAM = 0;
}
/* Do logic */
if (list == 3) {
cmd_machinesid();
ret = 0;
} else {
if (list) {
sam_list_groups(hive[H_SAM], list - 1, human);
ret = 0;
} else {
ret = cmd_usrgrp(usr, grp, what, human);
if (!ret && human) printf("Success!\n");
}
}
/* write registry hive (if needed) */
wret = 0;
for (il = 0; il < no_hives; il++) {
wret |= writeHive(hive[il]);
if (hive[il]->state & HMODE_DIDEXPAND)
fprintf(stderr," WARNING: Registry file %s was expanded! Experimental! Use at own risk!\n",hive[il]->filename);
while (no_hives > 0)
closeHive(hive[--no_hives]);
}
return(ret | wret);
}